changeset 5831:bd047b71ab37

Repaired internal references
author Sascha L. Teichmann <teichmann@intevation.de>
date Thu, 25 Apr 2013 12:06:39 +0200
parents 160f53ee0870
children b79f83bdd841
files flys-artifacts/src/main/java/de/intevation/flys/artifacts/AbstractStaticStateArtifact.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/AnnotationArtifact.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/AreaArtifact.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/BedHeightsArtifact.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/ChartArtifact.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/CollectionMonitor.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/CrossSectionArtifact.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/ExternalWMSArtifact.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/FixationArtifact.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/FlowVelocityMeasurementArtifact.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/GaugeDischargeArtifact.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/GaugeDischargeCurveArtifact.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/HYKArtifact.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/MINFOArtifact.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/ManualPointsArtifact.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/MapArtifact.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/QSectorArtifact.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/RiverAxisArtifact.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/SQRelationArtifact.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/StaticFLYSArtifact.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/StaticMorphWidthArtifact.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/StaticWKmsArtifact.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/StaticWQKmsArtifact.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/WMSBackgroundArtifact.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/WMSBuildingsArtifact.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/WMSDBArtifact.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/WMSFixpointsArtifact.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/WMSFloodmapsArtifact.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/WMSFloodmarksArtifact.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/WMSFloodplainArtifact.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/WMSGaugeLocationArtifact.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/WMSHWSLinesArtifact.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/WMSHWSPointsArtifact.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/WMSHydrBoundaryArtifact.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/WMSHydrBoundaryPolyArtifact.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/WMSJettiesArtifact.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/WMSKmArtifact.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/WMSLineArtifact.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/WMSQPSArtifact.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/WQKmsInterpolArtifact.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/WaterLineArtifact.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/WaterlevelArtifact.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/access/Access.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/access/BedDifferencesAccess.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/access/BedHeightAccess.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/access/BedQualityAccess.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/access/Calculation4Access.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/access/ExtremeAccess.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/access/FixAccess.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/access/FixAnalysisAccess.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/access/FixRealizingAccess.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/access/FlowVelocityAccess.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/access/GaugeAccess.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/access/HistoricalDischargeAccess.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/access/MapAccess.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/access/RangeAccess.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/access/RiverAccess.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/access/SQRelationAccess.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/access/SedimentLoadAccess.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/access/StaticSQRelationAccess.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/cache/CacheFactory.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/charts/CrossSectionApp.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/charts/TimeseriesStepChart.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/context/FLYSContext.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/context/FLYSContextFactory.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/context/SessionCallContextListener.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/DBConfig.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/DatacageBackendListener.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/Recommendations.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/App.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/Builder.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/BuilderPool.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/CompiledStatement.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/FunctionResolver.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/ResultData.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/StackFrames.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/TypeConverter.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/geom/Lines.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/map/PrintMap.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/map/RiverMapfileGeneratorStarter.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/AddScaleFunction.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/BackJumpCorrector.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/Distance.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/FilterFunction.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/Function.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/GrubbsOutlier.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/Identity.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/Linear.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/LinearFilterFunction.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/LinearFunction.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/MovingAverage.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/NaNFunction.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/StdDevOutlier.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/Sub.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/UnivariateRealFunctionFunction.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/Utils.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/WKmsOperation.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/fitting/App.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/fitting/Exp.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/fitting/Function.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/fitting/FunctionFactory.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/fitting/InvExp.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/fitting/InvLinear.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/fitting/InvLog.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/fitting/InvLogLinear.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/fitting/InvPow.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/fitting/InvQuad.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/fitting/InvSQPow.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/fitting/Linear.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/fitting/Log.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/fitting/LogLinear.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/fitting/Pow.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/fitting/Quad.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/fitting/SQPow.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/AnnotationFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/AnnotationsFactory.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/AreaFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/BlackboardDataFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/Calculation.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/Calculation1.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/Calculation2.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/Calculation3.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/Calculation4.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/Calculation5.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/Calculation6.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/CalculationMessage.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/CalculationResult.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/ConstantWQKms.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/CrossSectionFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/CrossSectionFactory.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/CrossSectionWaterLineFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/DataFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/DateRange.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/DifferenceCurveFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/DurationCurveFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/EmptyFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/FacetTypes.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/FastCrossSectionChunk.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/FastCrossSectionLineFactory.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/FixingsColumn.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/FixingsColumnFactory.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/FixingsFilterBuilder.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/FixingsOverview.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/FixingsOverviewFactory.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/FlowVelocityCalculation.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/FlowVelocityData.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/FlowVelocityFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/FlowVelocityFilterFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/GaugeDischargeCurveFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/GaugeDischargeFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/GaugeFinder.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/GaugeFinderFactory.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/GaugeRange.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/GaugesFactory.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/HYKFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/HYKFactory.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/HistoricalDischargeCurveFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/HistoricalDischargeData.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/HistoricalDischargeDifferenceFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/HistoricalDischargeFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/HistoricalDischargeWQFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/HistoricalWQKms.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/HistoricalWQTimerange.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/InfoldingColumns.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/LayerInfo.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/LocationProvider.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/MainValuesQFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/MainValuesWFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/ManagedDomFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/ManagedFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/ManagedFacetAdapter.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/ManualPointsFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/MapserverStyle.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/MiddleBedHeightCalculation.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/MiddleBedHeightData.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/MiddleBedHeightFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/Module.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/NamedDouble.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/NamedObject.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/NamedObjectImpl.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/Parameters.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/QKms.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/QKmsImpl.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/QRangeTree.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/QSectorFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/QW.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/QWDDateRange.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/Range.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/RangeWithValues.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/ReferenceCurveFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/RelativePointFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/ReportFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/RiverFactory.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/SQOverview.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/SQOverviewFactory.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/Segment.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/StaticBedHeightCacheKey.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/StaticMorphoWidthCacheKey.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/StaticSedimentLoadCacheKey.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/StaticWKmsCacheKey.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/StaticWQKmsCacheKey.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/Timerange.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/W.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WKms.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WKmsFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WKmsFactory.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WKmsImpl.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WKmsJRDataSource.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WQ.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WQCKms.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WQDay.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WQFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WQKms.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WQKmsFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WQKmsFactory.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WQKmsResult.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WQTJRDataSource.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WQTimerange.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WW.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WWAxisTypes.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WWQQ.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WWQQJRDataSource.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WaterlevelFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WstFactory.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WstLine.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WstValueTableCacheKey.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WstValueTableFactory.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/ZoomScale.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/extreme/Curve.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/extreme/ExtremeCalculation.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/extreme/ExtremeCurveFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/extreme/ExtremeResult.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/AnalysisPeriod.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/Fitting.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixAnalysisCalculation.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixAnalysisEventsFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixAnalysisPeriodsFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixAnalysisResult.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixAvSectorFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixCalculation.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixDerivateFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixDeviationFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixEventFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixFacetUtils.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixFunction.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixLongitudinalAnalysisFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixLongitudinalAvSectorFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixLongitudinalDeviationFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixLongitudinalReferenceFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixOutlierFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixRealizingCalculation.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixRealizingResult.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixReferenceEventsFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixResult.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixWQCurveFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixWaterlevelFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixingsFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/QWD.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/QWI.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/map/HWS.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/map/HWSContainer.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/map/HWSFactory.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/map/StaticHWSCacheKey.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/map/WMSDBLayerFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/map/WMSLayerFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/map/WSPLGENCalculation.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/map/WSPLGENJob.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/map/WSPLGENLayerFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/map/WSPLGENReportFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedDensityFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedDiameterFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedDiameterResult.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedDiffCalculation.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedDiffEpochFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedDiffEpochFilterFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedDiffEpochResult.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedDiffYearFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedDiffYearFilterFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedDiffYearResult.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedDifferencesResult.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedHeight.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedHeightEpoch.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedHeightFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedHeightFactory.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedHeightSingle.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedOverview.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedOverviewFactory.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedParametersResult.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedPorosityFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedQualityCalculation.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedQualityDiameterResult.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedQualityResult.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedloadDiameterFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedloadDiameterResult.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedloadOverview.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedloadOverviewFactory.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/FlowVelocityMeasurementFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/FlowVelocityMeasurementFactory.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/MorphologicWidth.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/MorphologicWidthFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/MorphologicWidthFactory.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/QualityMeasurement.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/QualityMeasurementFactory.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/QualityMeasurements.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/SedimentDensity.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/SedimentDensityFactory.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/SedimentDensityValue.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/SedimentLoad.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/SedimentLoadCalculation.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/SedimentLoadFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/SedimentLoadFactory.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/SedimentLoadFraction.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/SedimentLoadResult.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/sq/Fitting.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/sq/Measurement.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/sq/MeasurementFactory.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/sq/Measurements.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/sq/Outlier.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/sq/SQ.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/sq/SQCurveFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/sq/SQFractionResult.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/sq/SQFunction.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/sq/SQMeasurementFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/sq/SQOutlierCurveFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/sq/SQOutlierFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/sq/SQOutlierMeasurementFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/sq/SQOverviewFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/sq/SQRelationCalculation.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/sq/SQResult.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/sq/Sieve.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/sq/SieveArray.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/sq/StaticSQCacheKey.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/sq/StaticSQContainer.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/sq/StaticSQFactory.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/sq/StaticSQRelation.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/resources/Resources.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/AbstractChartService.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/BedKMChartService.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/BedloadKMChartService.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/CacheInvalidationService.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/CrossSectionKMService.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/DischargeInfoService.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/DischargeTablesOverview.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/DistanceInfoService.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/FLYSService.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/FileUploadService.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/FixingsKMChartService.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/FixingsOverviewService.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/GaugeInfoService.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/GaugeOverviewInfoService.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/MainValuesService.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/MapInfoService.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/MeasurementStationInfoService.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/ModuleService.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/QWSeriesCollection.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/RiverInfoService.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/RiverService.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/SQKMChartService.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/SedimentLoadInfoService.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/ThemeListingService.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/AnnotationRiverState.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/AreaCreationState.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/CalculationSelect.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/CalculationSelectMinfo.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/ComputationRangeState.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/ComputedDischargeCurveState.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/DGMSelect.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/DefaultState.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/DischargeLongitudinalSection.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/DischargeState.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/DistanceOnlySelect.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/DistanceSelect.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/DurationCurveState.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/EnterLocationState.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/EnterMultipleLocationsState.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/FloodplainChoice.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/FlowVelocityState.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/GaugeDischargeState.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/GaugeTimerangeState.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/HWSBarriersState.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/HWSDatacageState.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/HistoricalDischargeComputeState.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/HistoricalDischargeState.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/InputDoubleState.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/IntRangeState.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/LocationDistanceSelect.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/LocationSelect.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/ManualPointsSingleState.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/MiddleBedHeight.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/MinMaxState.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/MultiIntArrayState.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/MultiStringArrayState.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/OutliersInput.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/OutputState.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/PeriodSelect.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/PeriodsSelect.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/ProfileDistanceSelect.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/QSectorSingleState.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/RangeState.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/ReferenceCurveState.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/ReferenceGaugeState.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/RiverSelect.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/SQRelation.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/ScenarioSelect.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/SoundingsSelect.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/StateFactory.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/StaticHYKState.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/StaticState.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/StaticWQKmsState.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/UserRGDState.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/WDifferencesState.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/WMSBackgroundState.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/WQAdapted.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/WQSelect.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/WaterlevelGroundDifferences.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/WaterlevelInfoState.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/WaterlevelPairSelectState.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/WaterlevelSelectState.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/extreme/ExtremeCompute.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/extreme/ExtremeQInput.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/extreme/PercentInput.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/fixation/AnalysisPeriods.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/fixation/EventSelect.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/fixation/FixAnalysisCompute.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/fixation/FixRealizingCompute.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/fixation/FixationPeriod.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/fixation/FixationSelect.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/fixation/FunctionSelect.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/fixation/GaugeRange.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/fixation/LocationSelect.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/fixation/PreprocessingSelect.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/fixation/QSelect.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/fixation/ReferencePeriod.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/minfo/BedQualityPeriodsSelect.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/minfo/BedQualityState.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/minfo/CharDiameter.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/minfo/DifferenceSelect.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/minfo/DifferencesState.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/minfo/SedimentLoadCalculate.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/minfo/SedimentLoadDistanceSelect.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/minfo/SedimentLoadEpochSelect.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/minfo/SedimentLoadOffEpochSelect.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/minfo/SedimentLoadUnit.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/minfo/SedimentLoadYearSelect.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/minfo/YearEpochSelect.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/sq/OutlierMethod.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/sq/SQPeriodSelect.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/sq/SQStaticState.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/sq/StaticSQRelationFacet.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/transitions/DefaultTransition.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/transitions/TransitionFactory.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/transitions/ValueCompareTransition.java flys-artifacts/src/main/java/de/intevation/flys/collections/AttributeParser.java flys-artifacts/src/main/java/de/intevation/flys/collections/AttributeWriter.java flys-artifacts/src/main/java/de/intevation/flys/collections/CollectionAttribute.java flys-artifacts/src/main/java/de/intevation/flys/collections/CollectionDescriptionHelper.java flys-artifacts/src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java flys-artifacts/src/main/java/de/intevation/flys/collections/OutputParser.java flys-artifacts/src/main/java/de/intevation/flys/exports/ATExporter.java flys-artifacts/src/main/java/de/intevation/flys/exports/ATWriter.java flys-artifacts/src/main/java/de/intevation/flys/exports/AbstractExporter.java flys-artifacts/src/main/java/de/intevation/flys/exports/AxisSection.java flys-artifacts/src/main/java/de/intevation/flys/exports/BooleanAttribute.java flys-artifacts/src/main/java/de/intevation/flys/exports/ChartArea.java flys-artifacts/src/main/java/de/intevation/flys/exports/ChartExportHelper.java flys-artifacts/src/main/java/de/intevation/flys/exports/ChartGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/ChartHelper.java flys-artifacts/src/main/java/de/intevation/flys/exports/ChartInfoGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/ChartSection.java flys-artifacts/src/main/java/de/intevation/flys/exports/ChartSettings.java flys-artifacts/src/main/java/de/intevation/flys/exports/ChoiceStringAttribute.java flys-artifacts/src/main/java/de/intevation/flys/exports/ComputedDischargeCurveExporter.java flys-artifacts/src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/ComputedDischargeCurveInfoGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/CrossSectionGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/CrossSectionInfoGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/DischargeCurveInfoGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionExporter.java flys-artifacts/src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionInfoGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/DoubleAttribute.java flys-artifacts/src/main/java/de/intevation/flys/exports/DurationCurveExporter.java flys-artifacts/src/main/java/de/intevation/flys/exports/DurationCurveGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/DurationCurveInfoGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/EmptySettings.java flys-artifacts/src/main/java/de/intevation/flys/exports/ExportSection.java flys-artifacts/src/main/java/de/intevation/flys/exports/FlowVelocityExporter.java flys-artifacts/src/main/java/de/intevation/flys/exports/FlowVelocityGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/FlowVelocityInfoGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/HistoricalDischargeCurveExporter.java flys-artifacts/src/main/java/de/intevation/flys/exports/HistoricalDischargeCurveGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/HistoricalDischargeCurveInfoGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/HistoricalDischargeWQCurveGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/HistoricalDischargeWQCurveInfoGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/IdentifiableNumberAxis.java flys-artifacts/src/main/java/de/intevation/flys/exports/InfoGeneratorHelper.java flys-artifacts/src/main/java/de/intevation/flys/exports/IntegerAttribute.java flys-artifacts/src/main/java/de/intevation/flys/exports/LegendProcessor.java flys-artifacts/src/main/java/de/intevation/flys/exports/LegendSection.java flys-artifacts/src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/LongitudinalSectionInfoGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/MapGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/MiddleBedHeightExporter.java flys-artifacts/src/main/java/de/intevation/flys/exports/MiddleBedHeightGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/MiddleBedHeightInfoGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/NormalizedReferenceCurveGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/NormalizedReferenceCurveInfoGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/OutGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/OutputHelper.java flys-artifacts/src/main/java/de/intevation/flys/exports/ReferenceCurveExporter.java flys-artifacts/src/main/java/de/intevation/flys/exports/ReferenceCurveGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/ReferenceCurveInfoGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/ReportGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/StepCSVWriter.java flys-artifacts/src/main/java/de/intevation/flys/exports/StringAttribute.java flys-artifacts/src/main/java/de/intevation/flys/exports/StyledSeriesBuilder.java flys-artifacts/src/main/java/de/intevation/flys/exports/TimeseriesChartGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/TypeSection.java flys-artifacts/src/main/java/de/intevation/flys/exports/VisibleAttribute.java flys-artifacts/src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/WDifferencesCurveInfoGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/WDifferencesExporter.java flys-artifacts/src/main/java/de/intevation/flys/exports/WaterlevelExporter.java flys-artifacts/src/main/java/de/intevation/flys/exports/WstWriter.java flys-artifacts/src/main/java/de/intevation/flys/exports/XYChartGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/extreme/ExtremeWQCurveGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/extreme/ExtremeWQCurveInfoGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/fixings/DeltaWtExporter.java flys-artifacts/src/main/java/de/intevation/flys/exports/fixings/FixATExport.java flys-artifacts/src/main/java/de/intevation/flys/exports/fixings/FixATWriter.java flys-artifacts/src/main/java/de/intevation/flys/exports/fixings/FixChartGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/fixings/FixDeltaWtGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/fixings/FixDeltaWtInfoGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/fixings/FixDerivedCurveGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/fixings/FixDerivedCurveInfoGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/fixings/FixLongitudinalSectionGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/fixings/FixLongitudinalSectionInfoGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/fixings/FixWQCurveGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/fixings/FixWQCurveInfoGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/fixings/ParametersExporter.java flys-artifacts/src/main/java/de/intevation/flys/exports/minfo/BedDiffEpochInfoGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/minfo/BedDiffHeightYearGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/minfo/BedDiffHeightYearInfoGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/minfo/BedDiffYearInfoGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/minfo/BedDifferenceEpochGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/minfo/BedDifferenceYearGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/minfo/BedQualityExporter.java flys-artifacts/src/main/java/de/intevation/flys/exports/minfo/BedQualityGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/minfo/BedQualityInfoGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/minfo/SedimentLoadLSGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/minfo/SedimentLoadLSInfoGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/process/BedheightProcessor.java flys-artifacts/src/main/java/de/intevation/flys/exports/process/KMIndexProcessor.java flys-artifacts/src/main/java/de/intevation/flys/exports/process/Processor.java flys-artifacts/src/main/java/de/intevation/flys/exports/process/WOutProcessor.java flys-artifacts/src/main/java/de/intevation/flys/exports/sq/SQOverviewGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/sq/SQRelationExporter.java flys-artifacts/src/main/java/de/intevation/flys/exports/sq/SQRelationGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/sq/SQRelationGeneratorA.java flys-artifacts/src/main/java/de/intevation/flys/exports/sq/SQRelationGeneratorB.java flys-artifacts/src/main/java/de/intevation/flys/exports/sq/SQRelationGeneratorC.java flys-artifacts/src/main/java/de/intevation/flys/exports/sq/SQRelationGeneratorD.java flys-artifacts/src/main/java/de/intevation/flys/exports/sq/SQRelationGeneratorE.java flys-artifacts/src/main/java/de/intevation/flys/exports/sq/SQRelationGeneratorF.java flys-artifacts/src/main/java/de/intevation/flys/exports/sq/SQRelationInfoGenerator.java flys-artifacts/src/main/java/de/intevation/flys/java2d/NOPGraphics2D.java flys-artifacts/src/main/java/de/intevation/flys/java2d/ShapeUtils.java flys-artifacts/src/main/java/de/intevation/flys/jfree/Bounds.java flys-artifacts/src/main/java/de/intevation/flys/jfree/CollisionFreeLineLabelEntity.java flys-artifacts/src/main/java/de/intevation/flys/jfree/CollisionFreeXYTextAnnotation.java flys-artifacts/src/main/java/de/intevation/flys/jfree/CollisionFreeXYTextAnnotationEntity.java flys-artifacts/src/main/java/de/intevation/flys/jfree/DoubleBounds.java flys-artifacts/src/main/java/de/intevation/flys/jfree/EnhancedLineAndShapeRenderer.java flys-artifacts/src/main/java/de/intevation/flys/jfree/FLYSAnnotation.java flys-artifacts/src/main/java/de/intevation/flys/jfree/HasLabel.java flys-artifacts/src/main/java/de/intevation/flys/jfree/JFreeUtil.java flys-artifacts/src/main/java/de/intevation/flys/jfree/ShapeRenderer.java flys-artifacts/src/main/java/de/intevation/flys/jfree/StableXYDifferenceRenderer.java flys-artifacts/src/main/java/de/intevation/flys/jfree/StickyAxisAnnotation.java flys-artifacts/src/main/java/de/intevation/flys/jfree/Style.java flys-artifacts/src/main/java/de/intevation/flys/jfree/StyledAreaSeriesCollection.java flys-artifacts/src/main/java/de/intevation/flys/jfree/StyledDomainMarker.java flys-artifacts/src/main/java/de/intevation/flys/jfree/StyledSeries.java flys-artifacts/src/main/java/de/intevation/flys/jfree/StyledTimeSeries.java flys-artifacts/src/main/java/de/intevation/flys/jfree/StyledValueMarker.java flys-artifacts/src/main/java/de/intevation/flys/jfree/StyledXYSeries.java flys-artifacts/src/main/java/de/intevation/flys/jfree/TimeBounds.java flys-artifacts/src/main/java/de/intevation/flys/jfree/XYStyle.java flys-artifacts/src/main/java/de/intevation/flys/themes/DefaultTheme.java flys-artifacts/src/main/java/de/intevation/flys/themes/DefaultThemeField.java flys-artifacts/src/main/java/de/intevation/flys/themes/LineStyle.java flys-artifacts/src/main/java/de/intevation/flys/themes/PointStyle.java flys-artifacts/src/main/java/de/intevation/flys/themes/TextStyle.java flys-artifacts/src/main/java/de/intevation/flys/themes/Theme.java flys-artifacts/src/main/java/de/intevation/flys/themes/ThemeAccess.java flys-artifacts/src/main/java/de/intevation/flys/themes/ThemeFactory.java flys-artifacts/src/main/java/de/intevation/flys/themes/ThemeField.java flys-artifacts/src/main/java/de/intevation/flys/themes/ThemeGroup.java flys-artifacts/src/main/java/de/intevation/flys/themes/ThemeMapping.java flys-artifacts/src/main/java/de/intevation/flys/utils/ArtifactMapfileGenerator.java flys-artifacts/src/main/java/de/intevation/flys/utils/DataUtil.java flys-artifacts/src/main/java/de/intevation/flys/utils/DateAverager.java flys-artifacts/src/main/java/de/intevation/flys/utils/DoubleUtil.java flys-artifacts/src/main/java/de/intevation/flys/utils/FLYSUtils.java flys-artifacts/src/main/java/de/intevation/flys/utils/Formatter.java flys-artifacts/src/main/java/de/intevation/flys/utils/GeometryUtils.java flys-artifacts/src/main/java/de/intevation/flys/utils/IdGenerator.java flys-artifacts/src/main/java/de/intevation/flys/utils/KMIndex.java flys-artifacts/src/main/java/de/intevation/flys/utils/MapUtils.java flys-artifacts/src/main/java/de/intevation/flys/utils/MapfileGenerator.java flys-artifacts/src/main/java/de/intevation/flys/utils/Pair.java flys-artifacts/src/main/java/de/intevation/flys/utils/RiverMapfileGenerator.java flys-artifacts/src/main/java/de/intevation/flys/utils/ThemeUtil.java flys-artifacts/src/main/java/de/intevation/flys/wsplgen/FacetCreator.java flys-artifacts/src/main/java/de/intevation/flys/wsplgen/JobObserver.java flys-artifacts/src/main/java/de/intevation/flys/wsplgen/ProblemObserver.java flys-artifacts/src/main/java/de/intevation/flys/wsplgen/Scheduler.java flys-artifacts/src/main/java/de/intevation/flys/wsplgen/SchedulerSetup.java flys-artifacts/src/main/java/de/intevation/flys/wsplgen/WSPLGENCallable.java flys-artifacts/src/main/java/de/intevation/flys/wsplgen/WSPLGENFuture.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/AbstractStaticStateArtifact.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/AnnotationArtifact.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/AreaArtifact.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/BedHeightsArtifact.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/ChartArtifact.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/CollectionMonitor.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/CrossSectionArtifact.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/ExternalWMSArtifact.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/FLYSArtifact.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/FixationArtifact.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/FlowVelocityMeasurementArtifact.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/GaugeDischargeArtifact.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/GaugeDischargeCurveArtifact.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/HYKArtifact.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/MINFOArtifact.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/MainValuesArtifact.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/ManualPointsArtifact.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/MapArtifact.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/QSectorArtifact.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/RiverAxisArtifact.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/SQRelationArtifact.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/StaticFLYSArtifact.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/StaticMorphWidthArtifact.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/StaticWKmsArtifact.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/StaticWQKmsArtifact.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/WINFOArtifact.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/WMSBackgroundArtifact.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/WMSBuildingsArtifact.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/WMSDBArtifact.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/WMSFixpointsArtifact.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/WMSFloodmapsArtifact.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/WMSFloodmarksArtifact.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/WMSFloodplainArtifact.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/WMSGaugeLocationArtifact.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/WMSHWSLinesArtifact.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/WMSHWSPointsArtifact.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/WMSHydrBoundaryArtifact.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/WMSHydrBoundaryPolyArtifact.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/WMSJettiesArtifact.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/WMSKmArtifact.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/WMSLineArtifact.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/WMSQPSArtifact.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/WQKmsInterpolArtifact.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/WaterLineArtifact.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/WaterlevelArtifact.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/access/Access.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/access/BedDifferencesAccess.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/access/BedHeightAccess.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/access/BedQualityAccess.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/access/Calculation4Access.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/access/ExtremeAccess.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/access/FixAccess.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/access/FixAnalysisAccess.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/access/FixRealizingAccess.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/access/FlowVelocityAccess.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/access/GaugeAccess.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/access/HistoricalDischargeAccess.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/access/MapAccess.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/access/RangeAccess.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/access/RiverAccess.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/access/SQRelationAccess.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/access/SedimentLoadAccess.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/access/StaticSQRelationAccess.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/cache/CacheFactory.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/charts/CrossSectionApp.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/charts/TimeseriesStepChart.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/context/FLYSContext.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/context/FLYSContextFactory.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/context/SessionCallContextListener.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/datacage/DBConfig.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/datacage/Datacage.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/datacage/DatacageBackendListener.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/datacage/Recommendations.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/datacage/templating/App.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/datacage/templating/Builder.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/datacage/templating/BuilderPool.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/datacage/templating/CompiledStatement.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/datacage/templating/FunctionResolver.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/datacage/templating/ResultData.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/datacage/templating/StackFrames.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/datacage/templating/TypeConverter.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/geom/Lines.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/map/PrintMap.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/map/RiverMapfileGeneratorStarter.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/AddScaleFunction.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/BackJumpCorrector.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/Distance.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/FilterFunction.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/Function.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/GrubbsOutlier.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/Identity.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/Linear.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/LinearFilterFunction.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/LinearFunction.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/MovingAverage.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/NaNFunction.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/StdDevOutlier.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/Sub.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/UnivariateRealFunctionFunction.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/Utils.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/WKmsOperation.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/fitting/App.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/fitting/Exp.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/fitting/Function.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/fitting/FunctionFactory.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/fitting/InvExp.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/fitting/InvLinear.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/fitting/InvLog.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/fitting/InvLogLinear.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/fitting/InvPow.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/fitting/InvQuad.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/fitting/InvSQPow.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/fitting/Linear.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/fitting/Log.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/fitting/LogLinear.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/fitting/Pow.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/fitting/Quad.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/fitting/SQPow.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/AnnotationFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/AnnotationsFactory.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/AreaFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/BlackboardDataFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/Calculation.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/Calculation1.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/Calculation2.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/Calculation3.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/Calculation4.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/Calculation5.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/Calculation6.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/CalculationMessage.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/CalculationResult.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/ConstantWQKms.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/CrossSectionFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/CrossSectionFactory.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/CrossSectionWaterLineFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/DataFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/DateRange.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/DifferenceCurveFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/DischargeTables.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/DurationCurveFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/EmptyFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/FacetTypes.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/FastCrossSectionChunk.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/FastCrossSectionLineFactory.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/FixingsColumn.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/FixingsColumnFactory.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/FixingsFilterBuilder.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/FixingsOverview.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/FixingsOverviewFactory.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/FlowVelocityCalculation.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/FlowVelocityData.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/FlowVelocityFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/FlowVelocityFilterFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/GaugeDischargeCurveFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/GaugeDischargeFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/GaugeFinder.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/GaugeFinderFactory.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/GaugeRange.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/GaugesFactory.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/HYKFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/HYKFactory.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/HistoricalDischargeCurveFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/HistoricalDischargeData.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/HistoricalDischargeDifferenceFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/HistoricalDischargeFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/HistoricalDischargeWQFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/HistoricalWQKms.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/HistoricalWQTimerange.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/InfoldingColumns.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/LayerInfo.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/LocationProvider.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/MainValuesQFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/MainValuesWFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/ManagedDomFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/ManagedFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/ManagedFacetAdapter.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/ManualPointsFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/MapserverStyle.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/MiddleBedHeightCalculation.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/MiddleBedHeightData.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/MiddleBedHeightFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/Module.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/NamedDouble.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/NamedObject.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/NamedObjectImpl.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/Parameters.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/QKms.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/QKmsImpl.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/QRangeTree.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/QSectorFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/QW.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/QWDDateRange.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/Range.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/RangeWithValues.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/ReferenceCurveFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/RelativePointFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/ReportFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/RiverFactory.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/SQOverview.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/SQOverviewFactory.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/Segment.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/StaticBedHeightCacheKey.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/StaticMorphoWidthCacheKey.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/StaticSedimentLoadCacheKey.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/StaticWKmsCacheKey.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/StaticWQKmsCacheKey.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/Timerange.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/W.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WKms.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WKmsFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WKmsFactory.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WKmsImpl.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WKmsJRDataSource.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WQ.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WQCKms.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WQDay.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WQFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WQKms.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WQKmsFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WQKmsFactory.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WQKmsResult.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WQTJRDataSource.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WQTimerange.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WW.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WWAxisTypes.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WWQQ.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WWQQJRDataSource.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WaterlevelFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WstFactory.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WstLine.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WstValueTable.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WstValueTableCacheKey.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WstValueTableFactory.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/ZoomScale.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/extreme/Curve.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/extreme/ExtremeCalculation.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/extreme/ExtremeCurveFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/extreme/ExtremeResult.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/AnalysisPeriod.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/Fitting.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixAnalysisCalculation.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixAnalysisEventsFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixAnalysisPeriodsFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixAnalysisResult.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixAvSectorFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixCalculation.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixDerivateFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixDeviationFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixEventFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixFacetUtils.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixFunction.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixLongitudinalAnalysisFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixLongitudinalAvSectorFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixLongitudinalDeviationFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixLongitudinalReferenceFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixOutlierFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixRealizingCalculation.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixRealizingResult.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixReferenceEventsFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixResult.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixWQCurveFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixWaterlevelFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixingsFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/QWD.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/QWI.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/map/HWS.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/map/HWSContainer.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/map/HWSFactory.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/map/StaticHWSCacheKey.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/map/WMSDBLayerFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/map/WMSLayerFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/map/WSPLGENCalculation.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/map/WSPLGENJob.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/map/WSPLGENLayerFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/map/WSPLGENReportFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedDensityFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedDiameterFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedDiameterResult.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedDiffCalculation.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedDiffEpochFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedDiffEpochFilterFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedDiffEpochResult.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedDiffYearFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedDiffYearFilterFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedDiffYearResult.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedDifferencesResult.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedHeight.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedHeightEpoch.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedHeightFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedHeightFactory.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedHeightSingle.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedOverview.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedOverviewFactory.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedParametersResult.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedPorosityFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedQualityCalculation.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedQualityDiameterResult.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedQualityResult.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedloadDiameterFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedloadDiameterResult.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedloadOverview.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedloadOverviewFactory.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/FlowVelocityMeasurementFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/FlowVelocityMeasurementFactory.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/MorphologicWidth.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/MorphologicWidthFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/MorphologicWidthFactory.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/QualityMeasurement.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/QualityMeasurementFactory.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/QualityMeasurements.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentDensity.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentDensityFactory.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentDensityValue.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentLoad.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentLoadCalculation.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentLoadFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentLoadFactory.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentLoadFraction.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentLoadResult.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/Fitting.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/Measurement.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/MeasurementFactory.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/Measurements.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/Outlier.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/SQ.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/SQCurveFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/SQFractionResult.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/SQFunction.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/SQMeasurementFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/SQOutlierCurveFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/SQOutlierFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/SQOutlierMeasurementFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/SQOverviewFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/SQRelationCalculation.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/SQResult.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/Sieve.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/SieveArray.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/StaticSQCacheKey.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/StaticSQContainer.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/StaticSQFactory.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/StaticSQRelation.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/resources/Resources.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/AbstractChartService.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/BedKMChartService.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/BedloadKMChartService.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/CacheInvalidationService.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/CrossSectionKMService.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/DischargeInfoService.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/DischargeTablesOverview.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/DistanceInfoService.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/FLYSService.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/FileUploadService.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/FixingsKMChartService.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/FixingsOverviewService.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/GaugeInfoService.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/GaugeOverviewInfoService.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/MainValuesService.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/MapInfoService.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/MeasurementStationInfoService.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/MetaDataService.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/ModuleService.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/QWSeriesCollection.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/RiverInfoService.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/RiverService.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/SQKMChartService.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/SedimentLoadInfoService.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/ThemeListingService.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/AnnotationRiverState.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/AreaCreationState.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/CalculationSelect.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/CalculationSelectMinfo.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/ComputationRangeState.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/ComputedDischargeCurveState.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/DGMSelect.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/DefaultState.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/DischargeLongitudinalSection.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/DischargeState.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/DistanceOnlySelect.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/DistanceSelect.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/DurationCurveState.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/EnterLocationState.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/EnterMultipleLocationsState.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/FloodMapState.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/FloodplainChoice.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/FlowVelocityState.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/GaugeDischargeState.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/GaugeTimerangeState.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/HWSBarriersState.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/HWSDatacageState.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/HistoricalDischargeComputeState.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/HistoricalDischargeState.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/InputDoubleState.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/IntRangeState.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/LocationDistanceSelect.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/LocationSelect.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/ManualPointsSingleState.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/MiddleBedHeight.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/MinMaxState.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/MultiIntArrayState.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/MultiStringArrayState.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/OutliersInput.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/OutputState.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/PeriodSelect.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/PeriodsSelect.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/ProfileDistanceSelect.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/QSectorSingleState.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/RangeState.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/ReferenceCurveState.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/ReferenceGaugeState.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/RiverSelect.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/SQRelation.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/ScenarioSelect.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/SoundingsSelect.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/StateFactory.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/StaticHYKState.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/StaticState.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/StaticWQKmsState.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/UserRGDState.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/WDifferencesState.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/WMSBackgroundState.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/WQAdapted.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/WQSelect.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/WaterlevelGroundDifferences.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/WaterlevelInfoState.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/WaterlevelPairSelectState.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/WaterlevelSelectState.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/WaterlevelState.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/extreme/ExtremeCompute.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/extreme/ExtremeQInput.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/extreme/PercentInput.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/fixation/AnalysisPeriods.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/fixation/EventSelect.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/fixation/FixAnalysisCompute.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/fixation/FixRealizingCompute.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/fixation/FixationPeriod.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/fixation/FixationSelect.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/fixation/FunctionSelect.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/fixation/GaugeRange.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/fixation/LocationSelect.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/fixation/PreprocessingSelect.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/fixation/QSelect.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/fixation/ReferencePeriod.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/minfo/BedQualityPeriodsSelect.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/minfo/BedQualityState.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/minfo/CharDiameter.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/minfo/DifferenceSelect.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/minfo/DifferencesState.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/minfo/SedimentLoadCalculate.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/minfo/SedimentLoadDistanceSelect.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/minfo/SedimentLoadEpochSelect.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/minfo/SedimentLoadOffEpochSelect.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/minfo/SedimentLoadUnit.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/minfo/SedimentLoadYearSelect.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/minfo/YearEpochSelect.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/sq/OutlierMethod.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/sq/SQPeriodSelect.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/sq/SQStaticState.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/sq/StaticSQRelationFacet.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/transitions/DefaultTransition.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/transitions/TransitionFactory.java flys-artifacts/src/main/java/org/dive4elements/river/artifacts/transitions/ValueCompareTransition.java flys-artifacts/src/main/java/org/dive4elements/river/collections/AttributeParser.java flys-artifacts/src/main/java/org/dive4elements/river/collections/AttributeWriter.java flys-artifacts/src/main/java/org/dive4elements/river/collections/CollectionAttribute.java flys-artifacts/src/main/java/org/dive4elements/river/collections/CollectionDescriptionHelper.java flys-artifacts/src/main/java/org/dive4elements/river/collections/FLYSArtifactCollection.java flys-artifacts/src/main/java/org/dive4elements/river/collections/OutputParser.java flys-artifacts/src/main/java/org/dive4elements/river/exports/ATExporter.java flys-artifacts/src/main/java/org/dive4elements/river/exports/ATWriter.java flys-artifacts/src/main/java/org/dive4elements/river/exports/AbstractExporter.java flys-artifacts/src/main/java/org/dive4elements/river/exports/AxisSection.java flys-artifacts/src/main/java/org/dive4elements/river/exports/BooleanAttribute.java flys-artifacts/src/main/java/org/dive4elements/river/exports/ChartArea.java flys-artifacts/src/main/java/org/dive4elements/river/exports/ChartExportHelper.java flys-artifacts/src/main/java/org/dive4elements/river/exports/ChartGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/ChartHelper.java flys-artifacts/src/main/java/org/dive4elements/river/exports/ChartInfoGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/ChartSection.java flys-artifacts/src/main/java/org/dive4elements/river/exports/ChartSettings.java flys-artifacts/src/main/java/org/dive4elements/river/exports/ChoiceStringAttribute.java flys-artifacts/src/main/java/org/dive4elements/river/exports/ComputedDischargeCurveExporter.java flys-artifacts/src/main/java/org/dive4elements/river/exports/ComputedDischargeCurveGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/ComputedDischargeCurveInfoGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/CrossSectionGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/CrossSectionInfoGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/DischargeCurveGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/DischargeCurveInfoGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/DischargeLongitudinalSectionExporter.java flys-artifacts/src/main/java/org/dive4elements/river/exports/DischargeLongitudinalSectionGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/DischargeLongitudinalSectionInfoGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/DoubleAttribute.java flys-artifacts/src/main/java/org/dive4elements/river/exports/DurationCurveExporter.java flys-artifacts/src/main/java/org/dive4elements/river/exports/DurationCurveGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/DurationCurveInfoGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/EmptySettings.java flys-artifacts/src/main/java/org/dive4elements/river/exports/ExportSection.java flys-artifacts/src/main/java/org/dive4elements/river/exports/FlowVelocityExporter.java flys-artifacts/src/main/java/org/dive4elements/river/exports/FlowVelocityGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/FlowVelocityInfoGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/HistoricalDischargeCurveExporter.java flys-artifacts/src/main/java/org/dive4elements/river/exports/HistoricalDischargeCurveGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/HistoricalDischargeCurveInfoGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/HistoricalDischargeWQCurveGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/HistoricalDischargeWQCurveInfoGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/IdentifiableNumberAxis.java flys-artifacts/src/main/java/org/dive4elements/river/exports/InfoGeneratorHelper.java flys-artifacts/src/main/java/org/dive4elements/river/exports/IntegerAttribute.java flys-artifacts/src/main/java/org/dive4elements/river/exports/LegendProcessor.java flys-artifacts/src/main/java/org/dive4elements/river/exports/LegendSection.java flys-artifacts/src/main/java/org/dive4elements/river/exports/LongitudinalSectionGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/LongitudinalSectionInfoGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/MapGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/MiddleBedHeightExporter.java flys-artifacts/src/main/java/org/dive4elements/river/exports/MiddleBedHeightGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/MiddleBedHeightInfoGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/NormalizedReferenceCurveGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/NormalizedReferenceCurveInfoGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/OutGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/OutputHelper.java flys-artifacts/src/main/java/org/dive4elements/river/exports/ReferenceCurveExporter.java flys-artifacts/src/main/java/org/dive4elements/river/exports/ReferenceCurveGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/ReferenceCurveInfoGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/ReportGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/StepCSVWriter.java flys-artifacts/src/main/java/org/dive4elements/river/exports/StringAttribute.java flys-artifacts/src/main/java/org/dive4elements/river/exports/StyledSeriesBuilder.java flys-artifacts/src/main/java/org/dive4elements/river/exports/TimeseriesChartGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/TypeSection.java flys-artifacts/src/main/java/org/dive4elements/river/exports/VisibleAttribute.java flys-artifacts/src/main/java/org/dive4elements/river/exports/WDifferencesCurveGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/WDifferencesCurveInfoGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/WDifferencesExporter.java flys-artifacts/src/main/java/org/dive4elements/river/exports/WaterlevelExporter.java flys-artifacts/src/main/java/org/dive4elements/river/exports/WstWriter.java flys-artifacts/src/main/java/org/dive4elements/river/exports/XYChartGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/extreme/ExtremeWQCurveGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/extreme/ExtremeWQCurveInfoGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/fixings/DeltaWtExporter.java flys-artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixATExport.java flys-artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixATWriter.java flys-artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixChartGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixDeltaWtGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixDeltaWtInfoGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixDerivedCurveGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixDerivedCurveInfoGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixLongitudinalSectionGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixLongitudinalSectionInfoGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixWQCurveGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixWQCurveInfoGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/fixings/ParametersExporter.java flys-artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedDiffEpochInfoGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedDiffHeightYearGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedDiffHeightYearInfoGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedDiffYearInfoGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedDifferenceEpochGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedDifferenceYearGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedQualityExporter.java flys-artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedQualityGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedQualityInfoGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/minfo/SedimentLoadLSGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/minfo/SedimentLoadLSInfoGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/process/BedheightProcessor.java flys-artifacts/src/main/java/org/dive4elements/river/exports/process/KMIndexProcessor.java flys-artifacts/src/main/java/org/dive4elements/river/exports/process/Processor.java flys-artifacts/src/main/java/org/dive4elements/river/exports/process/WOutProcessor.java flys-artifacts/src/main/java/org/dive4elements/river/exports/sq/SQOverviewGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/sq/SQRelationExporter.java flys-artifacts/src/main/java/org/dive4elements/river/exports/sq/SQRelationGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/exports/sq/SQRelationGeneratorA.java flys-artifacts/src/main/java/org/dive4elements/river/exports/sq/SQRelationGeneratorB.java flys-artifacts/src/main/java/org/dive4elements/river/exports/sq/SQRelationGeneratorC.java flys-artifacts/src/main/java/org/dive4elements/river/exports/sq/SQRelationGeneratorD.java flys-artifacts/src/main/java/org/dive4elements/river/exports/sq/SQRelationGeneratorE.java flys-artifacts/src/main/java/org/dive4elements/river/exports/sq/SQRelationGeneratorF.java flys-artifacts/src/main/java/org/dive4elements/river/exports/sq/SQRelationInfoGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/java2d/NOPGraphics2D.java flys-artifacts/src/main/java/org/dive4elements/river/java2d/ShapeUtils.java flys-artifacts/src/main/java/org/dive4elements/river/jfree/Bounds.java flys-artifacts/src/main/java/org/dive4elements/river/jfree/CollisionFreeLineLabelEntity.java flys-artifacts/src/main/java/org/dive4elements/river/jfree/CollisionFreeXYTextAnnotation.java flys-artifacts/src/main/java/org/dive4elements/river/jfree/CollisionFreeXYTextAnnotationEntity.java flys-artifacts/src/main/java/org/dive4elements/river/jfree/DoubleBounds.java flys-artifacts/src/main/java/org/dive4elements/river/jfree/EnhancedLineAndShapeRenderer.java flys-artifacts/src/main/java/org/dive4elements/river/jfree/FLYSAnnotation.java flys-artifacts/src/main/java/org/dive4elements/river/jfree/HasLabel.java flys-artifacts/src/main/java/org/dive4elements/river/jfree/JFreeUtil.java flys-artifacts/src/main/java/org/dive4elements/river/jfree/ShapeRenderer.java flys-artifacts/src/main/java/org/dive4elements/river/jfree/StableXYDifferenceRenderer.java flys-artifacts/src/main/java/org/dive4elements/river/jfree/StickyAxisAnnotation.java flys-artifacts/src/main/java/org/dive4elements/river/jfree/Style.java flys-artifacts/src/main/java/org/dive4elements/river/jfree/StyledAreaSeriesCollection.java flys-artifacts/src/main/java/org/dive4elements/river/jfree/StyledDomainMarker.java flys-artifacts/src/main/java/org/dive4elements/river/jfree/StyledSeries.java flys-artifacts/src/main/java/org/dive4elements/river/jfree/StyledTimeSeries.java flys-artifacts/src/main/java/org/dive4elements/river/jfree/StyledValueMarker.java flys-artifacts/src/main/java/org/dive4elements/river/jfree/StyledXYSeries.java flys-artifacts/src/main/java/org/dive4elements/river/jfree/TimeBounds.java flys-artifacts/src/main/java/org/dive4elements/river/jfree/XYStyle.java flys-artifacts/src/main/java/org/dive4elements/river/themes/DefaultTheme.java flys-artifacts/src/main/java/org/dive4elements/river/themes/DefaultThemeField.java flys-artifacts/src/main/java/org/dive4elements/river/themes/LineStyle.java flys-artifacts/src/main/java/org/dive4elements/river/themes/PointStyle.java flys-artifacts/src/main/java/org/dive4elements/river/themes/TextStyle.java flys-artifacts/src/main/java/org/dive4elements/river/themes/Theme.java flys-artifacts/src/main/java/org/dive4elements/river/themes/ThemeAccess.java flys-artifacts/src/main/java/org/dive4elements/river/themes/ThemeFactory.java flys-artifacts/src/main/java/org/dive4elements/river/themes/ThemeField.java flys-artifacts/src/main/java/org/dive4elements/river/themes/ThemeGroup.java flys-artifacts/src/main/java/org/dive4elements/river/themes/ThemeMapping.java flys-artifacts/src/main/java/org/dive4elements/river/utils/ArtifactMapfileGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/utils/DataUtil.java flys-artifacts/src/main/java/org/dive4elements/river/utils/DateAverager.java flys-artifacts/src/main/java/org/dive4elements/river/utils/DoubleUtil.java flys-artifacts/src/main/java/org/dive4elements/river/utils/FLYSUtils.java flys-artifacts/src/main/java/org/dive4elements/river/utils/Formatter.java flys-artifacts/src/main/java/org/dive4elements/river/utils/GeometryUtils.java flys-artifacts/src/main/java/org/dive4elements/river/utils/IdGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/utils/KMIndex.java flys-artifacts/src/main/java/org/dive4elements/river/utils/MapUtils.java flys-artifacts/src/main/java/org/dive4elements/river/utils/MapfileGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/utils/Pair.java flys-artifacts/src/main/java/org/dive4elements/river/utils/RiverMapfileGenerator.java flys-artifacts/src/main/java/org/dive4elements/river/utils/ThemeUtil.java flys-artifacts/src/main/java/org/dive4elements/river/wsplgen/FacetCreator.java flys-artifacts/src/main/java/org/dive4elements/river/wsplgen/JobObserver.java flys-artifacts/src/main/java/org/dive4elements/river/wsplgen/ProblemObserver.java flys-artifacts/src/main/java/org/dive4elements/river/wsplgen/Scheduler.java flys-artifacts/src/main/java/org/dive4elements/river/wsplgen/SchedulerSetup.java flys-artifacts/src/main/java/org/dive4elements/river/wsplgen/WSPLGENCallable.java flys-artifacts/src/main/java/org/dive4elements/river/wsplgen/WSPLGENFuture.java
diffstat 1260 files changed, 97376 insertions(+), 97376 deletions(-) [+]
line wrap: on
line diff
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/AbstractStaticStateArtifact.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,140 +0,0 @@
-package de.intevation.flys.artifacts;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.ArtifactNamespaceContext;
-
-import de.intevation.artifacts.common.utils.XMLUtils;
-import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
-
-import de.intevation.artifactdatabase.ProtocolUtils;
-import de.intevation.artifactdatabase.data.StateData;
-import de.intevation.artifactdatabase.state.State;
-
-import de.intevation.flys.artifacts.states.StaticState;
-
-/**
- * A abstract baseclass for Artifacts which are using only one static state.
- *
- * This class is intended to be used without the config/stateengine to generate
- * the static state.
- *
- * @author <a href="mailto:bjoern.ricks@intevation.de">Björn Ricks</a>
- */
-public abstract class AbstractStaticStateArtifact extends StaticFLYSArtifact {
-
-    private transient StaticState staticstate;
-
-    private static final Logger logger =
-        Logger.getLogger(AbstractStaticStateArtifact.class);
-
-    /**
-     * 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(getStaticState());
-        return states;
-    }
-
-
-    /**
-     * Get the "current" state.
-     * @param cc ignored.
-     * @return always the set static state.
-     */
-    @Override
-    public State getCurrentState(Object cc) {
-        return getStaticState();
-    }
-
-    /**
-     * A child class must override this method to set its static state
-     */
-    protected abstract void initStaticState();
-
-    protected void setStaticState(StaticState state) {
-        this.staticstate = state;
-    }
-
-    protected StaticState getStaticState() {
-        if (staticstate == null) {
-            initStaticState();
-        }
-        return staticstate;
-    }
-
-    /**
-     * Get the state.
-     * @param context ignored.
-     * @param stateID ignored.
-     * @return the state.
-     */
-    @Override
-    protected State getState(Object context, String stateID) {
-        return getStaticState();
-    }
-
-    @Override
-    public Document describe(Document data, CallContext cc) {
-        logger.debug("Describe artifact: " + identifier());
-
-        Document desc = XMLUtils.newDocument();
-
-        ElementCreator creator = new ElementCreator(
-            desc,
-            ArtifactNamespaceContext.NAMESPACE_URI,
-            ArtifactNamespaceContext.NAMESPACE_PREFIX);
-
-        Element root = ProtocolUtils.createRootNode(creator);
-        desc.appendChild(root);
-
-        Element name = ProtocolUtils.createArtNode(
-            creator, "name",
-            new String[] { "value" },
-            new String[] { getName() });
-
-        root.appendChild(name);
-        root.appendChild(createOutputModes(cc, desc, creator));
-
-        ProtocolUtils.appendDescribeHeader(creator, root, identifier(), hash());
-
-        // Add the data to an anonymous state.
-        Collection<StateData> datas = getAllData();
-        if (datas.size() > 0) {
-            Element ui = creator.create("ui");
-            Element staticE = creator.create("static");
-
-            StaticState current = getStaticState();
-            Element state = current.describeStatic(this, desc, root, cc, null);
-            staticE.appendChild(state);
-
-            for (StateData dataItem : datas) {
-                Element itemelent = creator.create("data");
-                creator.addAttr(itemelent, "name", dataItem.getName(), true);
-                creator.addAttr(itemelent, "type", dataItem.getType(), true);
-                state.appendChild(itemelent);
-                Element valuement = creator.create("item");
-                creator.addAttr(valuement, "label", dataItem.getDescription(), true);
-                creator.addAttr(valuement, "value", dataItem.getValue().toString(), true);
-                itemelent.appendChild(valuement);
-            }
-
-            ui.appendChild(staticE);
-            root.appendChild(ui);
-        }
-
-        return desc;
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/AnnotationArtifact.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,261 +0,0 @@
-package de.intevation.flys.artifacts;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.ArtifactFactory;
-import de.intevation.artifacts.ArtifactNamespaceContext;
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.artifactdatabase.ProtocolUtils;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.FacetActivity;
-import de.intevation.artifactdatabase.state.Output;
-import de.intevation.artifactdatabase.state.State;
-import de.intevation.artifactdatabase.state.StateEngine;
-
-import de.intevation.flys.artifacts.model.FacetTypes;
-
-import de.intevation.artifacts.common.utils.XMLUtils;
-
-import de.intevation.flys.artifacts.states.DefaultState;
-import de.intevation.flys.artifacts.context.FLYSContext;
-
-import de.intevation.flys.utils.FLYSUtils;
-
-/**
- * Artifact to access names of Points Of Interest along a segment of a river.
- */
-public class AnnotationArtifact
-extends      StaticFLYSArtifact
-implements   FacetTypes {
-
-    /** The logger for this class. */
-    private static Logger logger = Logger.getLogger(AnnotationArtifact.class);
-
-    /** The name of the artifact. */
-    public static final String ARTIFACT_NAME = "annotation";
-
-    // Let Annotations enter life inactively if in Fix Analysis LS setting.
-    static {
-        FacetActivity.Registry.getInstance().register(
-            "annotation",
-            new FacetActivity() {
-                @Override
-                public Boolean isInitialActive(
-                    Artifact artifact,
-                    Facet    facet,
-                    String   output
-                ) {
-                    if (output.contains(FacetTypes.ChartType.FLSC.toString())) {
-                        // Longitudinal section chart
-                        String name = facet.getName();
-
-                        if (name.contains(FacetTypes.LONGITUDINAL_ANNOTATION)) {
-                            return Boolean.FALSE;
-                        }
-                    }
-
-                    return Boolean.TRUE;
-                }
-            });
-    }
-
-
-    @Override
-    public void setup(
-        String          identifier,
-        ArtifactFactory factory,
-        Object          context,
-        CallMeta        callMeta,
-        Document        data)
-    {
-        logger.debug("AnnotationArtifact.setup");
-        String filter = StaticFLYSArtifact.getDatacageIDValue(data);
-        String[] splits = filter.split(":");
-        if (splits.length > 1) {
-            addStringData("nameFilter", splits[1]);
-        }
-        super.setup(identifier, factory, context, callMeta, data);
-    }
-
-
-    /** Get river, setup Facets. */
-    @Override
-    protected void initialize(Artifact artifact, Object context,
-            CallMeta meta) {
-        logger.debug("AnnotationArtifact.initialize, id: "
-            + artifact.identifier());
-
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-        // TODO issue880: make annotations available _per type_
-        importData(flys, "river");
-
-        List<Facet> fs = new ArrayList<Facet>();
-
-        // TODO Add CallMeta (duplicate TODO in RiverAxisArtifact.java).
-        DefaultState state = (DefaultState) getCurrentState(context);
-        state.computeInit(this, hash(), context, meta, fs);
-
-        if (!fs.isEmpty()) {
-            logger.debug("Facets to add in AnnotationsArtifact.initialize .");
-            addFacets(getCurrentStateId(), fs);
-        }
-        else {
-            logger.debug("No facets to add in AnnotationsArtifact.initialize .");
-        }
-    }
-
-
-    /** Shortcut to nameFilter-data (TODO: move to respective Access). */
-    public String getFilterName() {
-        return getDataAsString("nameFilter");
-    }
-
-
-    public double[] getDistance() {
-        /** TODO In initialize(), access maximal range of river (via
-         * AnnotationFactory) instead of overriding getDistance,
-         * important for diagram generation. */
-        return new double[] {0f, 1000f};
-    }
-
-
-    /**
-     * Create the description of this AnnotationArtifact-instance.
-     *
-     * @param data Some data.
-     * @param context The CallContext.
-     *
-     * @return the description of this artifact.
-     */
-    @Override
-    public Document describe(Document data, CallContext context) {
-        logger.debug("Describe: the current state is: " + getCurrentStateId());
-
-        if (logger.isDebugEnabled()) {
-            dumpArtifact();
-        }
-
-        Document description            = XMLUtils.newDocument();
-        XMLUtils.ElementCreator creator = new XMLUtils.ElementCreator(
-            description,
-            ArtifactNamespaceContext.NAMESPACE_URI,
-            ArtifactNamespaceContext.NAMESPACE_PREFIX);
-
-        Element root = ProtocolUtils.createRootNode(creator);
-        description.appendChild(root);
-
-        State current = getCurrentState(context);
-
-        ProtocolUtils.appendDescribeHeader(creator, root, identifier(), hash());
-        ProtocolUtils.appendState(creator, root, current);
-
-        Element name = ProtocolUtils.createArtNode(
-            creator, "name",
-            new String[] { "value" },
-            new String[] { getName() });
-
-        Element outs = ProtocolUtils.createArtNode(
-            creator, "outputmodes", null, null);
-        appendOutputModes(description, outs, context);
-
-        root.appendChild(name);
-        root.appendChild(outs);
-
-        return description;
-    }
-
-
-    /**
-     * Returns the name of the concrete artifact.
-     *
-     * @return the name of the concrete artifact.
-     */
-    public String getName() {
-        return ARTIFACT_NAME;
-    }
-
-
-    /**
-     * Append outputmode elements to given document.
-     *
-     * @param doc Document to add outputmodes to.
-     * @param outs Element to add outputmode elements to.
-     * @param context The given CallContext (mostly for internationalization).
-     */
-    //@Override
-    protected void appendOutputModes(
-        Document    doc,
-        Element     outs,
-        CallContext context)
-    {
-        List<String> stateIds = getPreviousStateIds();
-
-        FLYSContext flysContext = FLYSUtils.getFlysContext(context);
-        StateEngine engine      = (StateEngine) flysContext.get(
-            FLYSContext.STATE_ENGINE_KEY);
-
-        for (String stateId: stateIds) {
-            logger.debug("Append output modes for state: " + stateId);
-            DefaultState state = (DefaultState) engine.getState(stateId);
-
-            List<Output> list = state.getOutputs();
-            if (list == null || list.isEmpty()) {
-                logger.debug("-> No output modes for this state.");
-                continue;
-            }
-
-            List<Facet> fs = getFacets(stateId);
-            if (fs == null || fs.isEmpty()) {
-                logger.debug("No facets found.");
-                continue;
-            }
-
-            logger.debug("Found " + fs.size() + " facets in previous states.");
-
-            List<Output> generated = generateOutputs(list, fs);
-
-            ProtocolUtils.appendOutputModes(doc, outs, generated);
-        }
-
-        try {
-            DefaultState cur = (DefaultState) getCurrentState(context);
-            if (cur.validate(this)) {
-                List<Output> list = cur.getOutputs();
-                if (list != null && list.size() > 0) {
-                    logger.debug(
-                        "Append output modes for state: " + cur.getID());
-
-                    List<Facet> fs = getFacets(cur.getID());
-                    if (fs != null && fs.size() > 0) {
-                        List<Output> generated = generateOutputs(list, fs);
-
-                        logger.debug("Found " + fs.size() + " current facets.");
-                        if (!generated.isEmpty()) {
-                            ProtocolUtils.appendOutputModes(
-                                doc, outs, generated);
-                        }
-                        else{
-                            logger.debug("Cannot append output to generated document.");
-                        }
-                    }
-                    else {
-                        logger.debug("No facets found for the current state.");
-                    }
-                }
-            }
-        }
-        catch (IllegalArgumentException iae) {
-            // state is not valid, so we do not append its outputs.
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/AreaArtifact.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,180 +0,0 @@
-package de.intevation.flys.artifacts;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Document;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.ArtifactFactory;
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.flys.artifacts.model.AreaFacet;
-
-
-import de.intevation.flys.artifacts.states.AreaCreationState;
-import de.intevation.flys.artifacts.states.StaticState;
-
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.State;
-
-
-/**
- * Artifact describing the area between two WKms.
- */
-public class AreaArtifact extends StaticFLYSArtifact {
-
-    /** Name of Artifact. */
-    public static final String AREA_ARTIFACT_NAME = "area_artifact";
-
-    /** Dataitem: Facet name. Facets with this name will be created (important
-     * to not have the area calculated in e.g. a CrossSection to be shown in
-     * LongitudinalSection.  */
-    protected static final String FACET_NAME = "area.facet";
-
-    /** Name of state. */
-    public static final String STATIC_STATE_NAME = "state.area_artifact";
-
-    /** data item name to access upper curve. */
-    protected static final String AREA_CURVE_OVER = "area.curve_over";
-
-    /** data item name to access lower curve. */
-    protected static final String AREA_CURVE_UNDER = "area.curve_under";
-
-    /** data item name to access whether or not paint over and under. */
-    protected static final String AREA_BETWEEN = "area.between";
-
-    /** Name of state. */
-    protected static final String AREA_NAME = "area.name";
-
-    /** Own logger. */
-    private static final Logger logger =
-        Logger.getLogger(AreaArtifact.class);
-
-
-    /** Return given name. */
-    @Override
-    public String getName() {
-        return AREA_ARTIFACT_NAME;
-    }
-
-
-    /** Store ids, create an AreaFacet. */
-    @Override
-    public void setup(
-        String          identifier,
-        ArtifactFactory factory,
-        Object          context,
-        CallMeta        callMeta,
-        Document        data)
-    {
-        logger.info("AreaArtifact.setup");
-
-        super.setup(identifier, factory, context, callMeta, data);
-
-        // TODO yet unused.
-        String ids = getDatacageIDValue(data);
-
-        // TODO this facet will be remodeled during next feed.
-        List<Facet> fs = new ArrayList<Facet>();
-        fs.add(new AreaFacet(0, "", "TODO: I am an AreaFacet"));
-
-        AreaCreationState state = (AreaCreationState) getCurrentState(context);
-
-        if (!fs.isEmpty()) {
-            addFacets(getCurrentStateId(), fs);
-        }
-    }
-
-    // TODO Data is not cached in this way.
-
-    /** Do not copy data from daddyfact. */
-    @Override
-    protected void initialize(
-        Artifact artifact,
-        Object   context,
-        CallMeta callMeta)
-    {
-        // do nothing
-    }
-
-
-    /**
-     * Get name of facets to create.
-     */
-    public String getFacetName() {
-        return getDataAsString(FACET_NAME);
-    }
-
-
-    /**
-     * Get dataprovider key for the 'lower' curve (we got that information fed
-     * from the client and store it as data).
-     */
-    public String getLowerDPKey() {
-        return getDataAsString(AREA_CURVE_UNDER);
-    }
-
-
-    /**
-     * True if the whole area between the two curves shall be filled.
-     */
-    public boolean getPaintBetween() {
-        String val = getDataAsString(AREA_BETWEEN);
-
-        return val != null && val.equals("true");
-    }
-
-
-    /**
-     * Get dataprovider key for the 'upper' curve (we got that information fed
-     * from the client and store it as data).
-     */
-    public String getUpperDPKey() {
-        return getDataAsString(AREA_CURVE_OVER);
-    }
-
-
-    /** Return data item that is used to configure name of area. */
-    public String getAreaName() {
-        return getDataAsString(AREA_NAME);
-    }
-
-
-    /**
-     * Create and return a new AreaCreationState with charting output.
-     */
-    @Override
-    public State getCurrentState(Object cc) {
-        final List<Facet> fs = getFacets(getCurrentStateId());
-
-        AreaCreationState state = new AreaCreationState();
-
-        StaticState.addDefaultChartOutput(state, "cross_section", fs);
-
-        return state;
-    }
-
-
-    /**
-     * 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(getCurrentState(context));
-
-        return states;
-    }
-
-
-    /** Trivia. */
-    protected State getState(Object context, String stateID) {
-        return getCurrentState(null);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/BedHeightsArtifact.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,137 +0,0 @@
-package de.intevation.flys.artifacts;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-import org.w3c.dom.Document;
-
-import de.intevation.artifactdatabase.state.DefaultOutput;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.FacetActivity;
-import de.intevation.artifacts.ArtifactFactory;
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.common.utils.XMLUtils;
-import de.intevation.flys.artifacts.model.minfo.BedHeightFacet;
-import de.intevation.flys.artifacts.model.minfo.BedHeightFactory;
-import de.intevation.flys.artifacts.states.StaticState;
-
-public class BedHeightsArtifact
-extends      AbstractStaticStateArtifact
-{
-    /** The logger for this class. */
-    private static Logger logger =
-        Logger.getLogger(BedHeightsArtifact.class);
-
-    private static final String NAME = "bedheights";
-    private static final String STATIC_FACET_NAME = "bedheight";
-
-    static {
-        // TODO: Move to configuration.
-        FacetActivity.Registry.getInstance()
-            .register(NAME, FacetActivity.INACTIVE);
-    }
-
-    public static final String STATIC_STATE_NAME =
-        "state.additional_bedheights.static";
-
-    /** Data Item name to know whether we are Heighmarks and reveive
-     * some data slightly different. */
-    public static final String DATA_HEIGHT_TYPE =
-        "height_marks";
-
-    /**
-     * Trivial Constructor.
-     */
-    public BedHeightsArtifact() {
-        logger.debug("BedHeightsArtifact.BedHeightsArtifact");
-    }
-
-    @Override
-    public String getName() {
-        return NAME;
-    }
-
-    /**
-     * Gets called from factory, to set things up.
-     */
-    @Override
-    public void setup(
-        String          identifier,
-        ArtifactFactory factory,
-        Object          context,
-        CallMeta        callMeta,
-        Document        data)
-    {
-        logger.debug("BedHeightsArtifact.setup");
-
-        if (logger.isDebugEnabled()) {
-            logger.debug(XMLUtils.toString(data));
-        }
-
-        String code = getDatacageIDValue(data);
-
-        if (code != null) {
-            String [] parts = code.split("-");
-
-            if (parts.length >= 4) {
-                String name = parts[0];
-                String type = parts[1];
-                String btype = type;
-                int hId = Integer.parseInt(parts[2]);
-                if (name.equals("bedheight")) {
-                    addStringData("type", type);
-                    addStringData("height_id", parts[2]);
-                    addStringData("time", parts[3]);
-                }
-                if (type.equals("singlevalues")) {
-                    btype = "single";
-                }
-
-                String bedHName = BedHeightFactory.getHeightName(btype, hId);
-
-                Facet facet = new BedHeightFacet(
-                        STATIC_FACET_NAME, bedHName, type);
-
-                ArrayList<Facet> facets = new ArrayList<Facet>(1);
-                facets.add(facet);
-
-                addFacets(STATIC_STATE_NAME, facets);
-            }
-        }
-        super.setup(identifier, factory, context, callMeta, data);
-    }
-
-    @Override
-    protected void initStaticState() {
-
-        logger.debug("initStaticState " + getName() + " " + identifier());
-
-        StaticState state = new StaticState(STATIC_STATE_NAME);
-        DefaultOutput output = new DefaultOutput(
-                "general",
-                "general",
-                "image/png",
-                "chart");
-
-        List<Facet> facets = getFacets(STATIC_STATE_NAME);
-        output.addFacets(facets);
-        state.addOutput(output);
-
-        setStaticState(state);
-    }
-
-    @Override
-    protected void initialize(Artifact artifact, Object context, CallMeta meta) {
-        // do not clone facets, etc. from master artifact
-
-        logger.debug("initialize");
-        importData((FLYSArtifact)artifact, "ld_from");
-        importData((FLYSArtifact)artifact, "ld_to");
-
-        logger.debug("ld_from " + getDataAsString("ld_from"));
-        logger.debug("ld_to " + getDataAsString("ld_to"));
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/ChartArtifact.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,207 +0,0 @@
-package de.intevation.flys.artifacts;
-
-import org.apache.log4j.Logger;
-
-import java.util.List;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-import de.intevation.artifacts.Artifact;
-
-import de.intevation.artifacts.ArtifactFactory;
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.artifacts.common.utils.XMLUtils;
-import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
-
-import de.intevation.artifactdatabase.ProtocolUtils;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.State;
-import de.intevation.artifactdatabase.state.StateEngine;
-import de.intevation.artifactdatabase.state.Output;
-
-import de.intevation.flys.utils.FLYSUtils;
-
-import de.intevation.flys.artifacts.states.DefaultState;
-import de.intevation.flys.artifacts.context.FLYSContext;
-import de.intevation.flys.artifacts.resources.Resources;
-
-
-/** Artifact, open to generate any (?) out. */
-public class ChartArtifact extends FLYSArtifact {
-
-    private static final Logger logger =
-        Logger.getLogger(ChartArtifact.class);
-
-    @Override
-    public void setup(
-        String          identifier,
-        ArtifactFactory factory,
-        Object          context,
-        CallMeta        callmeta,
-        Document        data)
-    {
-        logger.debug("ChartArtifact.setup");
-        this.identifier = identifier;
-        name = "new_chart";
-
-        List<State> states = getStates(context);
-
-        setCurrentState(states.get(0));
-    }
-
-    @Override
-    protected void appendBackgroundActivity(
-        ElementCreator cr,
-        Element        root,
-        CallContext    context
-    ) {
-        Element inBackground = cr.create("background-processing");
-        root.appendChild(inBackground);
-
-        cr.addAttr(
-            inBackground,
-            "value",
-            String.valueOf(context.isInBackground()),
-            true);
-    }
-
-
-    /**
-     * Append output mode nodes to a document.
-     */
-    @Override
-    protected void appendOutputModes(
-        Document    doc,
-        Element     outs,
-        CallContext context,
-        String      uuid)
-    {
-        List<String> stateIds = getPreviousStateIds();
-
-        FLYSContext flysContext = FLYSUtils.getFlysContext(context);
-        StateEngine engine      = (StateEngine) flysContext.get(
-            FLYSContext.STATE_ENGINE_KEY);
-
-        for (String stateId: stateIds) {
-            logger.debug("Append output modes for state: " + stateId);
-            DefaultState state = (DefaultState) engine.getState(stateId);
-
-            List<Output> list = state.getOutputs();
-            if (list == null || list.isEmpty()) {
-                logger.debug("-> No output modes for this state.");
-                continue;
-            }
-
-            List<Facet> fs = getFacets(stateId);
-
-            if (fs == null || fs.isEmpty()) {
-                logger.debug("No facets for previous state found.");
-                continue;
-            }
-
-            logger.debug("Found " + fs.size() + " facets in previous states.");
-
-            List<Output> generated = generateOutputs(list, fs);
-
-            ProtocolUtils.appendOutputModes(doc, outs, generated);
-        }
-
-        try {
-            DefaultState cur = (DefaultState) getCurrentState(context);
-            List<Output> list = cur.getOutputs();
-            if (list != null && list.size() > 0) {
-                logger.debug(
-                    "Append output modes for current state: " + cur.getID());
-
-                List<Facet> fs = getFacets(cur.getID());
-
-                if (fs != null && fs.size() > 0) {
-                    List<Output> generated = generateOutputs(list, fs);
-
-                    logger.debug("Found " + fs.size() + " current facets.");
-                    if (!generated.isEmpty()) {
-                        ProtocolUtils.appendOutputModes(
-                            doc, outs, generated);
-                    }
-                }
-                else {
-                    logger.debug("No facets found for the current state.");
-                }
-            }
-        }
-        catch (IllegalArgumentException iae) {
-            // state is not valid, so we do not append its outputs.
-        }
-    }
-
-    public static class ChartState extends DefaultState {
-
-        public static final String FIELD_MODE = "chart_type";
-
-        public static final String DURATION_CURVE =
-            "chart.new.durationcurve";
-
-        public static final String COMPUTED_DISCHARGE_CURVE =
-            "chart.new.computeddischargecurve";
-
-        public static final String DISCHARGE_LONGITUDINAL_CURVE =
-            "chart.new.longitudinal_section";
-
-        public static final String W_DIFFERENCES =
-            "chart.new.w_differences";
-
-        public static final String WATERLEVEL =
-            "chart.new.crosssection";
-
-        public static final String[] CHARTS = {
-            COMPUTED_DISCHARGE_CURVE,
-            DURATION_CURVE,
-            DISCHARGE_LONGITUDINAL_CURVE,
-            W_DIFFERENCES,
-            WATERLEVEL };
-
-
-
-        @Override
-        public Object computeAdvance(
-            FLYSArtifact artifact,
-            String       hash,
-            CallContext  context,
-            List<Facet>  facets,
-            Object       old)
-        {
-            logger.debug("ChartState.computeAdvance");
-
-
-            return null;
-        }
-
-
-        @Override
-        protected Element[] createItems(
-            XMLUtils.ElementCreator cr,
-            Artifact    artifact,
-            String      name,
-            CallContext context)
-        {
-            CallMeta meta   = context.getMeta();
-            Element[] charts = new Element[CHARTS.length];
-
-            int i = 0;
-
-            for (String chart: CHARTS) {
-                charts[i++] = createItem(
-                    cr, new String[] {
-                        Resources.getMsg(meta, chart, chart),
-                        chart
-                    });
-            }
-
-            return charts;
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/CollectionMonitor.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,104 +0,0 @@
-package de.intevation.flys.artifacts;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.xml.xpath.XPathConstants;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.ArtifactNamespaceContext;
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.Hook;
-
-import de.intevation.artifacts.common.utils.XMLUtils;
-import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
-
-import de.intevation.artifactdatabase.state.Output;
-
-import de.intevation.flys.artifacts.datacage.Recommendations;
-
-/** Monitors collection changes. */
-public class CollectionMonitor implements Hook {
-
-    public static final String XPATH_RESULT = "/art:result";
-
-
-    @Override
-    public void setup(Node cfg) {
-    }
-
-
-    @Override
-    public void execute(Artifact artifact, CallContext context, Document doc) {
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-
-        Element result = (Element) XMLUtils.xpath(
-            doc,
-            XPATH_RESULT,
-            XPathConstants.NODE,
-            ArtifactNamespaceContext.INSTANCE);
-
-        ElementCreator creator = new ElementCreator(
-            doc,
-            ArtifactNamespaceContext.NAMESPACE_URI,
-            ArtifactNamespaceContext.NAMESPACE_PREFIX);
-
-        Element recommended = creator.create("recommended-artifacts");
-        result.appendChild(recommended);
-
-        String[] outs              = extractOutputNames(flys, context);
-        Map<String, Object> params = getNoneUserSpecificParameters(flys, context);
-
-        Recommendations rec = Recommendations.getInstance();
-
-        // TODO For newer official-lines recommendations we actually
-        // need user-id (null here).
-        rec.recommend(flys, null, outs, params, recommended);
-    }
-
-
-    /**
-     * Get outputnames from current state (only the ones for which
-     * facets exist).
-     */
-    public static String[] extractOutputNames(
-        FLYSArtifact flys,
-        CallContext  context)
-    {
-        if (flys instanceof ChartArtifact) {
-            return new String[0];
-        }
-
-        List<Output> outs = flys.getCurrentOutputs(context);
-
-        int num = outs == null ? 0 : outs.size();
-
-        String[] names = new String[num];
-
-        for (int i = 0; i < num; i++) {
-            names[i] = outs.get(i).getName();
-        }
-
-        return names;
-    }
-
-
-    /**
-     * Creates Map from Strings "recommended" to "true".
-     */
-    protected Map<String, Object> getNoneUserSpecificParameters(
-        FLYSArtifact flys,
-        CallContext  context)
-    {
-        Map<String, Object> params = new HashMap<String, Object>(1);
-        params.put("recommended", "true");
-
-        return params;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/CrossSectionArtifact.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,346 +0,0 @@
-package de.intevation.flys.artifacts;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.NavigableMap;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Document;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.ArtifactFactory;
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.flys.artifacts.access.RangeAccess;
-import de.intevation.flys.artifacts.model.CrossSectionFacet;
-import de.intevation.flys.artifacts.model.FastCrossSectionLineFactory;
-
-import de.intevation.flys.model.FastCrossSectionLine;
-
-import de.intevation.flys.model.CrossSection;
-import de.intevation.flys.model.CrossSectionLine;
-import de.intevation.flys.artifacts.model.CrossSectionFactory;
-
-import de.intevation.flys.artifacts.states.StaticState;
-
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.FacetActivity;
-import de.intevation.artifactdatabase.state.State;
-
-import de.intevation.flys.artifacts.services.CrossSectionKMService;
-
-
-/**
- * Artifact describing a cross-section.
- */
-public class CrossSectionArtifact extends StaticFLYSArtifact {
-
-    /** Name of Artifact. */
-    public static final String CS_ARTIFACT_NAME = "cross_section";
-
-    /** Name of state. */
-    public static final String STATIC_STATE_NAME = "state.cross_section";
-
-    /** Name of data item keeping the position. */
-    public static final String DATA_KM = "cross_section.km";
-
-    /** Name of data item keeping the database id of this c.s.. */
-    public static final String DATA_DBID = "cross_section.dbid";
-
-    /** Name of data item flagging whether we think that we are master. */
-    public static final String DATA_IS_MASTER = "cross_section.master?";
-
-    /** Name of data item flagging whether we are the newest. */
-    public static final String DATA_IS_NEWEST = "cross_section.newest?";
-
-    /** Name of data item storing the previous possible km. */
-    public static final String DATA_PREV_KM = "cross_section.km.previous";
-
-    /** Name of data item storing the next possible km. */
-    public static final String DATA_NEXT_KM = "cross_section.km.next";
-
-    /** Own logger. */
-    private static final Logger logger =
-        Logger.getLogger(CrossSectionArtifact.class);
-
-    static {
-        // TODO: Move to configuration.
-        FacetActivity.Registry.getInstance().register(
-            CS_ARTIFACT_NAME,
-            new FacetActivity() {
-                @Override
-                public Boolean isInitialActive(
-                    Artifact artifact,
-                    Facet    facet,
-                    String   outputName
-                ) {
-                    if (artifact instanceof FLYSArtifact) {
-                        FLYSArtifact flys = (FLYSArtifact)artifact;
-                        String data = flys.getDataAsString(DATA_IS_NEWEST);
-                        return data != null && data.equals("1");
-                    }
-                    return null;
-                }
-            });
-    }
-
-    /** Return given name. */
-    @Override
-    public String getName() {
-        return CS_ARTIFACT_NAME;
-    }
-
-
-    /** Store ids, create a CrossSectionFacet. */
-    @Override
-    public void setup(
-        String          identifier,
-        ArtifactFactory factory,
-        Object          context,
-        CallMeta        callMeta,
-        Document        data)
-    {
-        logger.info("CrossSectionArtifact.setup");
-
-        super.setup(identifier, factory, context, callMeta, data);
-
-        String ids = getDatacageIDValue(data);
-
-        if (ids != null && ids.length() > 0) {
-            addStringData(DATA_DBID, ids);
-            logger.debug("CrossSectionArtifacts db-id: " + ids);
-        }
-        else {
-            throw new IllegalArgumentException("No attribute 'ids' found!");
-        }
-
-        List<Facet> fs = new ArrayList<Facet>();
-        CrossSection cs = CrossSectionFactory.getCrossSection(
-            Integer.parseInt(ids));
-
-        List<CrossSectionLine> csls = cs.getLines();
-        if (!csls.isEmpty()) {
-            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));
-            if (dataKm < csl.getKm().doubleValue()) {
-                addStringData(DATA_KM, csl.getKm().toString());
-            }
-        }
-        fs.add(new CrossSectionFacet(0, cs.getDescription()));
-
-        // Find out if we are newest and become master if so.
-        boolean isNewest = CrossSectionFactory.isNewest(cs);
-        String newString = (isNewest) ? "1" : "0";
-        addStringData(DATA_IS_NEWEST, newString);
-        addStringData(DATA_IS_MASTER, newString);
-
-        StaticState state = (StaticState) getCurrentState(context);
-
-        if (!fs.isEmpty()) {
-            addFacets(getCurrentStateId(), fs);
-        }
-    }
-
-
-    /** Copy km where master-artifact "starts". */
-    @Override
-    protected void initialize(
-        Artifact artifact,
-        Object   context,
-        CallMeta callMeta)
-    {
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-
-        RangeAccess rangeAccess = new RangeAccess(flys, null);
-        double[] range = rangeAccess.getKmRange();
-        double min = 0.0f;
-        if (range != null && range.length > 0) {
-            min = range[0];
-        }
-        this.addStringData(DATA_KM, Double.toString(min));
-    }
-
-
-    /** Returns next possible km for a cross-section. */
-    public Double getNextKm() {
-        return getDataAsDouble(DATA_NEXT_KM);
-    }
-
-
-    /** Returns previous possible km for a cross-section. */
-    public Double getPrevKm() {
-        return getDataAsDouble(DATA_PREV_KM);
-    }
-
-
-    /**
-     * Create and return a new StaticState with charting output.
-     */
-    @Override
-    public State getCurrentState(Object cc) {
-        final List<Facet> fs = getFacets(getCurrentStateId());
-
-        StaticState state = new StaticState(STATIC_STATE_NAME) {
-            @Override
-            public Object staticCompute(List<Facet> facets) {
-                if (facets != null) {
-                    facets.addAll(fs);
-                }
-                return null;
-            }
-        };
-
-        state.addDefaultChartOutput("cross_section", fs);
-
-        return state;
-    }
-
-
-    /**
-     * 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(getCurrentState(context));
-
-        return states;
-    }
-
-    // TODO all data access needs proper caching.
-
-    /**
-     * Get a DataItem casted to int (0 if fails).
-     */
-    public int getDataAsIntNull(String dataName) {
-        String val = getDataAsString(dataName);
-        try {
-            return Integer.parseInt(val);
-        }
-        catch (NumberFormatException e) {
-            logger.warn("Could not get data " + dataName + " as int", e);
-            return 0;
-        }
-    }
-
-
-    /** Returns database-id of cross-section (from data). */
-    protected int getDBID() {
-        return getDataAsIntNull(DATA_DBID);
-    }
-
-
-    /**
-     * Return position (km) from data, 0 if not found.
-     */
-    protected double getKm() {
-        String val = getDataAsString(DATA_KM);
-        try {
-            return Double.valueOf(val);
-        }
-        catch (NumberFormatException e) {
-            logger.warn("Could not get data " + DATA_KM + " as double", e);
-            return 0;
-        }
-    }
-
-
-    /** Returns true if artifact is set to be a "master" (other facets will
-     * refer to this). */
-    public boolean isMaster() {
-        return !getDataAsString(DATA_IS_MASTER).equals("0");
-    }
-
-
-    /**
-     * Get points of Profile of cross section at given kilometer.
-     *
-     * @return an array holding coordinates of points of profile (
-     *         in the form {{x1, x2} {y1, y2}} ).
-     */
-    public double [][] getCrossSectionData() {
-        logger.info("getCrossSectionData() for cross_section.km "
-            + getDataAsString(DATA_KM));
-        FastCrossSectionLine line = searchCrossSectionLine();
-
-        return line != null
-               ? line.fetchCrossSectionProfile()
-               : null;
-    }
-
-
-    /**
-     * Get CrossSectionLine spatially closest to what is specified in the data
-     * "cross_section.km", null if considered too far.
-     *
-     * It also adds DataItems to store the next and previous (numerically)
-     * values at which cross-section data was recorded.
-     *
-     * @return CrossSectionLine closest to "cross_section.km", might be null
-     *         if considered too far.
-     */
-    public FastCrossSectionLine searchCrossSectionLine() {
-        double TOO_FAR = 1d;
-        CrossSection crossSection = CrossSectionFactory
-            .getCrossSection(getDBID());
-
-        if (logger.isDebugEnabled()) {
-            logger.debug("dbid " + getDBID() + " : " + crossSection);
-        }
-
-        NavigableMap<Double, Integer> kms = CrossSectionKMService
-            .getKms(crossSection.getId());
-
-        Double wishKM = getKm();
-
-        Double floor = kms.floorKey(wishKM);
-        Double ceil  = kms.ceilingKey(wishKM);
-
-        Double nextKm;
-        Double prevKm;
-
-        double floorD = floor != null
-            ? Math.abs(floor - wishKM)
-            : Double.MAX_VALUE;
-
-        double ceilD = ceil != null
-            ? Math.abs(ceil - wishKM)
-            : Double.MAX_VALUE;
-
-        double km;
-        if (floorD < ceilD) {
-            km = floor;
-        }
-        else {
-            km = ceil;
-        }
-
-        // If we are too far from the wished km, return null.
-        if (Math.abs(km - wishKM) > TOO_FAR) {
-            return null;
-        }
-
-        // Store next and previous km.
-        nextKm = kms.higherKey(km);
-        prevKm = kms.lowerKey(km);
-
-        if (prevKm == null) {
-            prevKm = -1d;
-        }
-        if (nextKm == null) {
-            nextKm = -1d;
-        }
-
-        addStringData(DATA_PREV_KM, prevKm.toString());
-        addStringData(DATA_NEXT_KM, nextKm.toString());
-
-        return FastCrossSectionLineFactory
-            .getCrossSectionLine(crossSection, km);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/ExternalWMSArtifact.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,165 +0,0 @@
-package de.intevation.flys.artifacts;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Document;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.ArtifactFactory;
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.artifactdatabase.state.DefaultOutput;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.State;
-
-import de.intevation.flys.artifacts.states.WMSBackgroundState;
-
-
-public class ExternalWMSArtifact extends StaticFLYSArtifact {
-
-    public static final String NAME = "external_wms";
-
-    private static final Logger logger =
-        Logger.getLogger(ExternalWMSArtifact.class);
-
-
-    @Override
-    public String getName() {
-        return NAME;
-    }
-
-
-    @Override
-    public void setup(
-        String          identifier,
-        ArtifactFactory factory,
-        Object          context,
-        CallMeta        callMeta,
-        Document        data)
-    {
-        logger.info("ExternalWMSArtifact.setup");
-
-        super.setup(identifier, factory, context, callMeta, data);
-
-        String ids = getDatacageIDValue(data);
-
-        if (ids != null && ids.length() > 0) {
-            addStringData("ids", ids);
-        }
-        else {
-            throw new IllegalArgumentException("No attribute 'ids' found!");
-        }
-
-        List<Facet> fs = new ArrayList<Facet>();
-
-        WMSBackgroundState s = (WMSBackgroundState) getCurrentState(context);
-        s.computeInit(this, hash(), context, callMeta, fs);
-
-        if (!fs.isEmpty()) {
-            addFacets(getCurrentStateId(), fs);
-        }
-    }
-
-
-    @Override
-    protected void initialize(
-        Artifact artifact,
-        Object   context,
-        CallMeta callMeta)
-    {
-        // do nothing
-    }
-
-
-    @Override
-    public State getCurrentState(Object cc) {
-        State s = new ExternalWMSState(this);
-
-        List<Facet> fs = getFacets(getCurrentStateId());
-
-        DefaultOutput o = new DefaultOutput(
-            "floodmap",
-            "floodmap",
-            "image/png",
-            fs,
-            "map");
-
-        s.getOutputs().add(o);
-
-        return s;
-    }
-
-
-    /**
-     * 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(getCurrentState(context));
-
-        return states;
-    }
-
-
-    public static class ExternalWMSState extends WMSBackgroundState {
-
-        protected ExternalWMSArtifact artifact;
-
-        protected String ids;
-
-
-        public ExternalWMSState(ExternalWMSArtifact artifact) {
-            super();
-            this.artifact = artifact;
-        }
-
-        protected String getIds() {
-            if (ids == null || ids.length() == 0) {
-                ids = artifact.getDataAsString("ids");
-            }
-
-            return ids;
-        }
-
-        @Override
-        protected String getFacetType() {
-            return FLOODMAP_EXTERNAL_WMS;
-        }
-
-        @Override
-        protected String getSrid() {
-            return "";
-        }
-
-        @Override
-        protected String getUrl() {
-            String   ids   = getIds();
-            String[] parts = ids.split(";");
-
-            return parts[0];
-        }
-
-        @Override
-        protected String getLayer() {
-            String   ids   = getIds();
-            String[] parts = ids.split(";");
-
-            return parts[1];
-        }
-
-        @Override
-        protected String getTitle(CallMeta meta) {
-            String   ids   = getIds();
-            String[] parts = ids.split(";");
-
-            return parts[2];
-        }
-    } // end of class ExternalWMSState
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1564 +0,0 @@
-package de.intevation.flys.artifacts;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeMap;
-
-import javax.xml.xpath.XPathConstants;
-
-import net.sf.ehcache.Cache;
-
-import org.apache.log4j.Logger;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-
-import de.intevation.artifactdatabase.ArtifactDatabaseImpl;
-import de.intevation.artifactdatabase.DefaultArtifact;
-import de.intevation.artifactdatabase.ProtocolUtils;
-import de.intevation.artifactdatabase.data.DefaultStateData;
-import de.intevation.artifactdatabase.data.StateData;
-import de.intevation.artifactdatabase.state.DefaultFacet;
-import de.intevation.artifactdatabase.state.DefaultOutput;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.Output;
-import de.intevation.artifactdatabase.state.State;
-import de.intevation.artifactdatabase.state.StateEngine;
-import de.intevation.artifactdatabase.transition.TransitionEngine;
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.ArtifactDatabase;
-import de.intevation.artifacts.ArtifactDatabaseException;
-import de.intevation.artifacts.ArtifactFactory;
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.Message;
-import de.intevation.artifacts.common.ArtifactNamespaceContext;
-import de.intevation.artifacts.common.utils.XMLUtils;
-import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
-import de.intevation.flys.artifacts.cache.CacheFactory;
-import de.intevation.flys.artifacts.context.FLYSContext;
-import de.intevation.flys.artifacts.model.CalculationMessage;
-import de.intevation.flys.artifacts.states.DefaultState;
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-import de.intevation.flys.utils.FLYSUtils;
-
-/**
- * The default FLYS artifact with convenience added.
- * (Subclass to get fully functional artifacts).
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public abstract class FLYSArtifact extends DefaultArtifact {
-
-    /** The logger that is used in this artifact. */
-    private static Logger log = Logger.getLogger(FLYSArtifact.class);
-
-    public static final String COMPUTING_CACHE = "computed.values";
-
-    /** The XPath that points to the input data elements of the FEED document. */
-    public static final String XPATH_FEED_INPUT =
-        "/art:action/art:data/art:input";
-
-    /** The XPath that points to the name of the target state of ADVANCE. */
-    public static final String XPATH_ADVANCE_TARGET =
-        "/art:action/art:target/@art:name";
-
-    public static final String XPATH_MODEL_ARTIFACT =
-        "/art:action/art:template/@uuid";
-
-    public static final String XPATH_FILTER =
-        "/art:action/art:filter/art:out";
-
-    /** The constant string that shows that an operation was successful. */
-    public static final String OPERATION_SUCCESSFUL = "SUCCESS";
-
-    /** The constant string that shows that an operation failed. */
-    public static final String OPERATION_FAILED = "FAILURE";
-
-    /** The identifier of the current state. */
-    protected String currentStateId;
-
-    /** The identifiers of previous states on a stack. */
-    protected List<String> previousStateIds;
-
-    /** The name of the artifact. */
-    protected String name;
-
-    /** The data that have been inserted into this artifact. */
-    private Map<String, StateData> data;
-
-    /** Mapping of state names to created facets. */
-    private Map<String, List<Facet>> facets;
-
-    /**
-     * Used to generates "view" on the facets (hides facets not matching the
-     * filter in output of collection);  out -&gt; facets.
-     */
-    protected Map<String, List<Facet>> filterFacets;
-
-
-    /**
-     * The default constructor that creates an empty FLYSArtifact.
-     */
-    public FLYSArtifact() {
-        data             = new TreeMap<String, StateData>();
-        previousStateIds = new ArrayList<String>();
-        facets           = new HashMap<String, List<Facet>>();
-    }
-
-
-    /**
-     * This method appends the static data - that has already been inserted by
-     * the user - to the static node of the DESCRIBE document.
-     *
-     * @param doc The document.
-     * @param ui The root node.
-     * @param context The CallContext.
-     * @param uuid The identifier of the artifact.
-     */
-    protected void appendStaticUI(
-        Document    doc,
-        Node        ui,
-        CallContext context,
-        String uuid)
-    {
-        List<String> stateIds = getPreviousStateIds();
-
-        FLYSContext flysContext = FLYSUtils.getFlysContext(context);
-        StateEngine engine      = (StateEngine) flysContext.get(
-            FLYSContext.STATE_ENGINE_KEY);
-
-        boolean debug = log.isDebugEnabled();
-
-        for (String stateId: stateIds) {
-            if (debug) {
-                log.debug("Append static data for state: " + stateId);
-            }
-            DefaultState state = (DefaultState) engine.getState(stateId);
-
-            ui.appendChild(state.describeStatic(this, doc, ui, context, uuid));
-        }
-    }
-
-
-    /**
-     * Returns the name of the concrete artifact.
-     *
-     * @return the name of the concrete artifact.
-     */
-    public String getName() {
-        return name;
-    }
-
-
-    /**
-     * Initialize the artifact and insert new data if <code>data</code> contains
-     * information necessary for this artifact.
-     *
-     * @param identifier The UUID.
-     * @param factory The factory that is used to create this artifact.
-     * @param context The CallContext.
-     * @param data Some optional data.
-     */
-    @Override
-    public void setup(
-        String          identifier,
-        ArtifactFactory factory,
-        Object          context,
-        CallMeta        callMeta,
-        Document        data)
-    {
-        boolean debug = log.isDebugEnabled();
-
-        if (debug) {
-            log.debug("Setup this artifact with the uuid: " + identifier);
-        }
-
-        super.setup(identifier, factory, context, callMeta, data);
-
-        FLYSContext flysContext = FLYSUtils.getFlysContext(context);
-
-        List<State> states = getStates(context);
-
-        String name = getName();
-
-        if (debug) {
-            log.debug("setup(): Set initial state for artifact '" + name + "'");
-        }
-
-        if (states == null) {
-            log.error("No states found from which an initial "
-                + "state could be picked.");
-        }
-        setCurrentState(states.get(0));
-
-        String model = XMLUtils.xpathString(
-            data,
-            XPATH_MODEL_ARTIFACT,
-            ArtifactNamespaceContext.INSTANCE);
-
-        if (model != null && model.length() > 0) {
-            ArtifactDatabase db = (ArtifactDatabase) flysContext.get(
-                ArtifactDatabaseImpl.GLOBAL_CONTEXT_KEY);
-
-            try {
-                initialize(db.getRawArtifact(model), context, callMeta);
-            }
-            catch (ArtifactDatabaseException adbe) {
-                log.error(adbe, adbe);
-            }
-        }
-
-        filterFacets = buildFilterFacets(data);
-    }
-
-
-    /** Get copy of previous state ids as Strings in list. */
-    protected List<String> clonePreviousStateIds() {
-        return new ArrayList<String>(previousStateIds);
-    }
-
-
-    /**
-     * Copies data item from other artifact to this artifact.
-     *
-     * @param other Artifact from which to get data.
-     * @param name  Name of data.
-     */
-    protected void importData(FLYSArtifact other, final String name) {
-        if (other == null) {
-            log.error("No other art. to import data " + name + " from.");
-            return;
-        }
-
-        StateData sd = other.getData(name);
-
-        if (sd == null) {
-            log.warn("Other artifact has no data " + name + ".");
-            return;
-        }
-
-        this.addData(name, sd);
-    }
-
-
-    /** Clone the internal map of map of state-name to state-data. */
-    protected Map<String, StateData> cloneData() {
-        Map<String, StateData> copy = new TreeMap<String, StateData>();
-
-        for (Map.Entry<String, StateData> entry: data.entrySet()) {
-            copy.put(entry.getKey(), entry.getValue().deepCopy());
-        }
-
-        return copy;
-    }
-
-
-    /**
-     * Return a copy of the facet mapping.
-     * @return Mapping of state-ids to facets.
-     */
-    protected Map<String, List<Facet>> cloneFacets() {
-        Map<String, List<Facet>> copy = new HashMap<String, List<Facet>>();
-
-        for (Map.Entry<String, List<Facet>> entry: facets.entrySet()) {
-            List<Facet> facets      = entry.getValue();
-            List<Facet> facetCopies = new ArrayList<Facet>(facets.size());
-            for (Facet facet: facets) {
-                facetCopies.add(facet.deepCopy());
-            }
-            copy.put(entry.getKey(), facetCopies);
-        }
-
-        return copy;
-    }
-
-
-    /**
-     * (called from setup).
-     * @param artifact master-artifact (if any, otherwise initialize is not called).
-     */
-    protected void initialize(
-        Artifact artifact,
-        Object   context,
-        CallMeta callMeta)
-    {
-        if (!(artifact instanceof FLYSArtifact)) {
-            return;
-        }
-
-        FLYSArtifact flys = (FLYSArtifact)artifact;
-
-        currentStateId   = flys.currentStateId;
-        previousStateIds = flys.clonePreviousStateIds();
-        name             = flys.name;
-        data             = flys.cloneData();
-        facets           = flys.cloneFacets();
-        // Do not clone filter facets!
-
-        ArrayList<String> stateIds     = (ArrayList<String>) getPreviousStateIds();
-        ArrayList<String> toInitialize = (ArrayList<String>) stateIds.clone();
-
-        toInitialize.add(getCurrentStateId());
-
-        for (String stateId: toInitialize) {
-            State state = getState(context, stateId);
-
-            if (state != null) {
-                state.initialize(artifact, this, context, callMeta);
-            }
-        }
-    }
-
-
-    /**
-     * Builds filter facets from document.
-     * @see filterFacets
-     */
-    protected Map<String, List<Facet>> buildFilterFacets(Document document) {
-        if (log.isDebugEnabled()) {
-            log.debug("Building filter factes for artifact " + this.getName());
-        }
-
-        NodeList nodes = (NodeList)XMLUtils.xpath(
-            document,
-            XPATH_FILTER,
-            XPathConstants.NODESET,
-            ArtifactNamespaceContext.INSTANCE);
-
-        if (nodes == null || nodes.getLength() == 0) {
-            return null;
-        }
-
-        Map<String, List<Facet>> result = new HashMap<String, List<Facet>>();
-
-        for (int i = 0, N = nodes.getLength(); i < N; ++i) {
-            Element element = (Element)nodes.item(i);
-            String oName = element.getAttribute("name");
-            if (oName == null || oName.isEmpty()) {
-                continue;
-            }
-
-            List<Facet> facets = new ArrayList<Facet>();
-
-            NodeList facetNodes = element.getElementsByTagNameNS(
-                ArtifactNamespaceContext.NAMESPACE_URI,
-                "facet");
-
-            for (int j = 0, M = facetNodes.getLength(); j < M; ++j) {
-                Element facetElement = (Element)facetNodes.item(j);
-
-                String fName = facetElement.getAttribute("name");
-
-                int index;
-                try {
-                    index = Integer.parseInt(facetElement.getAttribute("index"));
-                }
-                catch (NumberFormatException nfe) {
-                    log.warn(nfe);
-                    index = 0;
-                }
-                if (log.isDebugEnabled()) {
-                    log.debug("Creating filter facet " + fName + " with  index " + index +
-                            " for out " + oName);
-                }
-                facets.add(new DefaultFacet(index, fName, ""));
-            }
-
-            if (!facets.isEmpty()) {
-                result.put(oName, facets);
-            }
-        }
-
-        return result;
-    }
-
-
-    /**
-     * Insert new data included in <code>input</code> into the current state.
-     *
-     * @param target XML document that contains new data.
-     * @param context The CallContext.
-     *
-     * @return a document that contains a SUCCESS or FAILURE message.
-     */
-    @Override
-    public Document feed(Document target, CallContext context) {
-        log.debug("FLYSArtifact.feed()");
-
-        Document doc = XMLUtils.newDocument();
-
-        XMLUtils.ElementCreator creator = new XMLUtils.ElementCreator(
-            doc,
-            ArtifactNamespaceContext.NAMESPACE_URI,
-            ArtifactNamespaceContext.NAMESPACE_PREFIX);
-
-        Element result = creator.create("result");
-        doc.appendChild(result);
-
-        try {
-            saveData(target, context);
-
-            compute(context, ComputeType.FEED, true);
-
-            return describe(target, context);
-        }
-        catch (IllegalArgumentException iae) {
-            // do not store state if validation fails.
-            context.afterCall(CallContext.NOTHING);
-            creator.addAttr(result, "type", OPERATION_FAILED, true);
-
-            result.setTextContent(iae.getMessage());
-        }
-
-        return doc;
-    }
-
-
-    /**
-     * This method returns a description of this artifact.
-     *
-     * @param data Some data.
-     * @param context The CallContext.
-     *
-     * @return the description of this artifact.
-     */
-    @Override
-    public Document describe(Document data, CallContext context) {
-
-        if (log.isDebugEnabled()) {
-            log.debug(
-                "Describe: the current state is: " + getCurrentStateId());
-            dumpArtifact();
-        }
-
-        FLYSContext flysContext = FLYSUtils.getFlysContext(context);
-
-        StateEngine stateEngine = (StateEngine) flysContext.get(
-            FLYSContext.STATE_ENGINE_KEY);
-
-        TransitionEngine transitionEngine = (TransitionEngine) flysContext.get(
-            FLYSContext.TRANSITION_ENGINE_KEY);
-
-        List<State> reachable = transitionEngine.getReachableStates(
-            this, getCurrentState(context), stateEngine);
-
-        Document description            = XMLUtils.newDocument();
-        XMLUtils.ElementCreator creator = new XMLUtils.ElementCreator(
-            description,
-            ArtifactNamespaceContext.NAMESPACE_URI,
-            ArtifactNamespaceContext.NAMESPACE_PREFIX);
-
-        Element root = ProtocolUtils.createRootNode(creator);
-        description.appendChild(root);
-
-        State current = getCurrentState(context);
-
-        ProtocolUtils.appendDescribeHeader(creator, root, identifier(), hash());
-        ProtocolUtils.appendState(creator, root, current);
-        ProtocolUtils.appendReachableStates(creator, root, reachable);
-
-        appendBackgroundActivity(creator, root, context);
-
-        Element ui = ProtocolUtils.createArtNode(
-            creator, "ui", null, null);
-
-        Element staticUI  = ProtocolUtils.createArtNode(
-            creator, "static", null, null);
-
-        Element outs = ProtocolUtils.createArtNode(
-            creator, "outputmodes", null, null);
-        appendOutputModes(description, outs, context, identifier());
-
-        appendStaticUI(description, staticUI, context, identifier());
-
-        Element name = ProtocolUtils.createArtNode(
-            creator, "name",
-            new String[] { "value" },
-            new String[] { getName() });
-
-        Element dynamic = current.describe(
-            this,
-            description,
-            root,
-            context,
-            identifier());
-
-        if (dynamic != null) {
-            ui.appendChild(dynamic);
-        }
-
-        ui.appendChild(staticUI);
-
-        root.appendChild(name);
-        root.appendChild(ui);
-        root.appendChild(outs);
-
-        return description;
-    }
-
-    /** Override me! */
-
-    protected void appendBackgroundActivity(
-        ElementCreator cr,
-        Element        root,
-        CallContext    context
-    ) {
-        LinkedList<Message> messages = context.getBackgroundMessages();
-
-        if (messages == null) {
-            return;
-        }
-
-        Element inBackground = cr.create("background-processing");
-        root.appendChild(inBackground);
-
-        cr.addAttr(
-            inBackground,
-            "value",
-            String.valueOf(context.isInBackground()),
-            true);
-
-        CalculationMessage message = (CalculationMessage) messages.getLast();
-        cr.addAttr(
-            inBackground,
-            "steps",
-            String.valueOf(message.getSteps()),
-            true);
-
-        cr.addAttr(
-            inBackground,
-            "currentStep",
-            String.valueOf(message.getCurrentStep()),
-            true);
-
-        inBackground.setTextContent(message.getMessage());
-    }
-
-    /**
-     * Append output mode nodes to a document.
-     */
-    protected void appendOutputModes(
-        Document    doc,
-        Element     outs,
-        CallContext context,
-        String      uuid)
-    {
-        List<Output> generated = getOutputs(context);
-
-        if (log.isDebugEnabled()) {
-            log.debug("This Artifact has " + generated.size() + " Outputs.");
-        }
-
-        ProtocolUtils.appendOutputModes(doc, outs, generated);
-    }
-
-
-    /**
-     * This method handles request for changing the current state of an
-     * artifact. It is possible to step forward or backward.
-     *
-     * @param target The incoming ADVANCE document.
-     * @param context The CallContext.
-     *
-     * @return a document that contains a SUCCESS or FAILURE message.
-     */
-    @Override
-    public Document advance(Document target, CallContext context) {
-
-        boolean debug = log.isDebugEnabled();
-
-        Document doc = XMLUtils.newDocument();
-
-        XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(
-            doc,
-            ArtifactNamespaceContext.NAMESPACE_URI,
-            ArtifactNamespaceContext.NAMESPACE_PREFIX);
-
-        Element result = ec.create("result");
-
-        String currentStateId = getCurrentStateId();
-        String targetState    = XMLUtils.xpathString(
-            target, XPATH_ADVANCE_TARGET, ArtifactNamespaceContext.INSTANCE);
-
-        if (debug) {
-            log.debug("FLYSArtifact.advance() to '" + targetState + "'");
-        }
-
-        if (!currentStateId.equals(targetState)
-            && isStateReachable(targetState, context))
-        {
-            if (debug) {
-                log.debug("Advance: Step forward");
-            }
-
-            List<String> prev = getPreviousStateIds();
-            prev.add(currentStateId);
-
-            setCurrentStateId(targetState);
-
-            if (debug) {
-                log.debug("Compute data for state: " + targetState);
-            }
-            compute(context, ComputeType.ADVANCE, true);
-
-            return describe(target, context);
-        }
-        else if (isPreviousState(targetState, context)) {
-            if (debug) {
-                log.debug("Advance: Step back to");
-            }
-
-            List<String> prevs   = getPreviousStateIds();
-            int targetIdx        = prevs.indexOf(targetState);
-            int start            = prevs.size() - 1;
-
-            destroyStates(prevs, context);
-
-            for (int i = start; i >= targetIdx; i--) {
-                String prev = prevs.get(i);
-                if (debug) {
-                    log.debug("Remove state id '" + prev + "'");
-                }
-
-                prevs.remove(prev);
-                facets.remove(prev);
-            }
-
-            destroyState(getCurrentStateId(), context);
-            setCurrentStateId(targetState);
-
-            return describe(target, context);
-        }
-
-        log.warn("Advance: Cannot advance to '" + targetState + "'");
-        ec.addAttr(result, "type", OPERATION_FAILED, true);
-
-        doc.appendChild(result);
-
-        return doc;
-    }
-
-
-    /**
-     * Returns the identifier of the current state.
-     *
-     * @return the identifier of the current state.
-     */
-    public String getCurrentStateId() {
-        return currentStateId;
-    }
-
-
-    /**
-     * Sets the identifier of the current state.
-     *
-     * @param id the identifier of a state.
-     */
-    protected void setCurrentStateId(String id) {
-        currentStateId = id;
-    }
-
-
-    /**
-     * Set the current state of this artifact. <b>NOTE</b>We don't store the
-     * State object itself - which is not necessary - but its identifier. So
-     * this method will just call the setCurrentStateId() method with the
-     * identifier of <i>state</i>.
-     *
-     * @param state The new current state.
-     */
-    protected void setCurrentState(State state) {
-        setCurrentStateId(state.getID());
-    }
-
-
-    /**
-     * Returns the current state of the artifact.
-     *
-     * @return the current State of the artifact.
-     */
-    public State getCurrentState(Object context) {
-        return getState(context, getCurrentStateId());
-    }
-
-
-    /**
-     * Get list of existant states for this Artifact.
-     * @param context Contex to get StateEngine from.
-     * @return list of states.
-     */
-    protected List<State> getStates(Object context) {
-        FLYSContext flysContext = FLYSUtils.getFlysContext(context);
-        StateEngine engine      = (StateEngine) flysContext.get(
-            FLYSContext.STATE_ENGINE_KEY);
-        return engine.getStates(getName());
-    }
-
-
-    /**
-     * Get state with given ID.
-     * @param context Context to get StateEngine from.
-     * @param stateID ID of state to get.
-     * @return state with given ID.
-     */
-    protected State getState(Object context, String stateID) {
-        FLYSContext flysContext = FLYSUtils.getFlysContext(context);
-        StateEngine engine      = (StateEngine) flysContext.get(
-            FLYSContext.STATE_ENGINE_KEY);
-        return engine.getState(stateID);
-    }
-
-
-    /**
-     * Returns the vector of previous state identifiers.
-     *
-     * @return the vector of previous state identifiers.
-     */
-    protected List<String> getPreviousStateIds() {
-        return previousStateIds;
-    }
-
-
-    /**
-     * Get all previous and the current state id.
-     * @return #getPreviousStateIds() + #getCurrentStateId()
-     */
-    public List<String> getStateHistoryIds() {
-        ArrayList<String> prevIds = (ArrayList) getPreviousStateIds();
-        ArrayList<String> allIds  = (ArrayList) prevIds.clone();
-
-        allIds.add(getCurrentStateId());
-        return allIds;
-    }
-
-
-    /**
-     * Adds a new StateData item to the data pool of this artifact.
-     *
-     * @param name the name of the data object.
-     * @param data the data object itself.
-     */
-    protected void addData(String name, StateData data) {
-        this.data.put(name, data);
-    }
-
-
-    /** Remove and return statedata associated to name. */
-    protected StateData removeData(String name) {
-        return this.data.remove(name);
-    }
-
-
-    /**
-     * This method returns a specific StateData object that is stored in the
-     * data pool of this artifact.
-     *
-     * @param name The name of the data object.
-     *
-     * @return the StateData object if existing, otherwise null.
-     */
-    public StateData getData(String name) {
-        return data.get(name);
-    }
-
-
-    /**
-     * A derived Artifact class can use this method to set the data
-     */
-    protected void setData(Map<String, StateData> data) {
-        this.data = data;
-    }
-
-
-    /** Return named data item, null if not found. */
-    public String getDataAsString(String name) {
-        StateData data = getData(name);
-        return data != null ? (String) data.getValue() : null;
-    }
-
-
-    /**
-     * This method returns the value of a StateData object stored in the data
-     * pool of this Artifact as Integer.
-     *
-     * @param name The name of the StateData object.
-     *
-     * @return an Integer representing the value of the data object or null if
-     * no object was found for <i>name</i>.
-     *
-     * @throws NumberFormatException if the value of the data object could not
-     * be transformed into an Integer.
-     */
-    public Integer getDataAsInteger(String name)
-    throws NumberFormatException
-    {
-        String value = getDataAsString(name);
-
-        if (value != null && value.length() > 0) {
-            return Integer.parseInt(value);
-        }
-
-        return null;
-    }
-
-
-    /**
-     * This method returns the value of a StateData object stored in the data
-     * pool of this Artifact as Double.
-     *
-     * @param name The name of the StateData object.
-     *
-     * @return an Double representing the value of the data object or null if
-     * no object was found for <i>name</i>.
-     *
-     * @throws NumberFormatException if the value of the data object could not
-     * be transformed into a Double.
-     */
-    public Double getDataAsDouble(String name)
-    throws NumberFormatException
-    {
-        String value = getDataAsString(name);
-
-        if (value != null && value.length() > 0) {
-            return Double.parseDouble(value);
-        }
-
-        return null;
-    }
-
-
-    /**
-     * This method returns the value of a StateData object stored in the data
-     * pool of this Artifact as Long.
-     *
-     * @param name The name of the StateData object.
-     *
-     * @return a Long representing the value of the data object or null if
-     * no object was found for <i>name</i>.
-     *
-     * @throws NumberFormatException if the value of the data object could not
-     * be transformed into a Long.
-     */
-    public Long getDataAsLong(String name)
-    throws NumberFormatException
-    {
-        String value = getDataAsString(name);
-
-        if (value != null && value.length() > 0) {
-            return Long.parseLong(value);
-        }
-
-        return null;
-    }
-
-
-    /**
-     * This method returns the value of a StateData object stored in the data
-     * pool of this Artifact is Boolean using Boolean.valueOf().
-     *
-     * @param name The name of the StateData object.
-     *
-     * @return a Boolean representing the value of the data object or null if no
-     * such object is existing.
-     */
-    public Boolean getDataAsBoolean(String name) {
-        String value = getDataAsString(name);
-
-        if (value == null || value.length() == 0) {
-            return null;
-        }
-
-        return Boolean.valueOf(value);
-    }
-
-
-    /**
-     * Add StateData containing a given string.
-     * @param name Name of the data object.
-     * @param value String to store.
-     */
-    public void addStringData(String name, String value) {
-        addData(name, new DefaultStateData(name, null, null, value));
-    }
-
-    /**
-     * This method returns all stored StateData in this artifact as a Collection
-     * @return a Collection of all StateData objects in this artifact
-     */
-    public Collection<StateData> getAllData() {
-        return data.values();
-    }
-
-
-    /** Return all produced facets. */
-    public List<Facet> getFacets() {
-        List<Facet> all = new ArrayList<Facet>();
-
-        // Iterate over facets of each state.
-        for (List<Facet> fs: facets.values()) {
-            all.addAll(fs);
-        }
-
-        return all;
-    }
-
-
-    /**
-     * Get facet as stored internally, with equalling name and index than given
-     * facet.
-     * @param facet that defines index and name of facet searched.
-     * @return facet instance or null if not found.
-     */
-    public Facet getNativeFacet(Facet facet) {
-        String name  = facet.getName();
-        int    index = facet.getIndex();
-
-        for (List<Facet> fs: facets.values()) {
-            for (Facet f: fs) {
-                if (f.getIndex() == index && f.getName().equals(name)) {
-                    return f;
-                }
-            }
-        }
-
-        log.warn("Could not find facet: " + name + " at " + index);
-        log.warn("Available facets for : " + getName() + " " + identifier() +
-                ": " + facets.values());
-        return null;
-    }
-
-
-    /**
-     * This method stores the data that is contained in the FEED document.
-     *
-     * @param feed The FEED document.
-     * @param xpath The XPath that points to the data nodes.
-     */
-    public void saveData(Document feed, CallContext context)
-    throws IllegalArgumentException
-    {
-        if (feed == null) {
-            throw new IllegalArgumentException("error_feed_no_data");
-        }
-
-        NodeList nodes = (NodeList) XMLUtils.xpath(
-            feed,
-            XPATH_FEED_INPUT,
-            XPathConstants.NODESET,
-            ArtifactNamespaceContext.INSTANCE);
-
-        if (nodes == null || nodes.getLength() == 0) {
-            throw new IllegalArgumentException("error_feed_no_data");
-        }
-
-        boolean debug = log.isDebugEnabled();
-
-        int count = nodes.getLength();
-
-        if (debug) {
-            log.debug("Try to save " + count + " data items.");
-        }
-
-        String uri = ArtifactNamespaceContext.NAMESPACE_URI;
-
-        DefaultState current = (DefaultState) getCurrentState(context);
-
-        FLYSContext flysContext = FLYSUtils.getFlysContext(context);
-        StateEngine engine      = (StateEngine) flysContext.get(
-            FLYSContext.STATE_ENGINE_KEY);
-
-        for (int i = 0; i < count; i++) {
-            Element node = (Element)nodes.item(i);
-
-            String name  = node.getAttributeNS(uri, "name");
-            String value = node.getAttributeNS(uri, "value");
-
-            if (name.length() > 0 && value.length() > 0) {
-                if (debug) {
-                    log.debug("Save data item for '" + name + "' : " + value);
-                }
-
-                StateData model = engine.getStateData(getName(), name);
-
-                StateData sd = model != null
-                    ? model.deepCopy()
-                    : new DefaultStateData(name, null, null, value);
-
-                addData(
-                    name, current.transform(this, context, sd, name, value));
-            }
-            else if (name.length() > 0 && value.length() == 0) {
-                if (removeData(name) != null && debug) {
-                    log.debug("Removed data '" + name + "' successfully.");
-                }
-            }
-        }
-
-        current.validate(this);
-    }
-
-
-    /**
-     * Determines if the state with the identifier <i>stateId</i> is reachable
-     * from the current state. The determination itself takes place in the
-     * TransitionEngine.
-     *
-     * @param stateId The identifier of a state.
-     * @param context The context object.
-     *
-     * @return true, if the state specified by <i>stateId</i> is reacahble,
-     * otherwise false.
-     */
-    protected boolean isStateReachable(String stateId, Object context) {
-
-        if (log.isDebugEnabled()) {
-            log.debug("Determine if the state '" + stateId + "' is reachable.");
-        }
-
-        FLYSContext flysContext = FLYSUtils.getFlysContext(context);
-
-        State currentState  = getCurrentState(context);
-        StateEngine sEngine = (StateEngine) flysContext.get(
-            FLYSContext.STATE_ENGINE_KEY);
-
-        TransitionEngine tEngine = (TransitionEngine) flysContext.get(
-            FLYSContext.TRANSITION_ENGINE_KEY);
-
-        return tEngine.isStateReachable(this, stateId, currentState, sEngine);
-    }
-
-
-    /**
-     * Determines if the state with the identifier <i>stateId</i> is a previous
-     * state of the current state.
-     *
-     * @param stateId The target state identifier.
-     * @param context The context object.
-     */
-    protected boolean isPreviousState(String stateId, Object context) {
-        if (log.isDebugEnabled()) {
-            log.debug("Determine if the state '" + stateId + "' is old.");
-        }
-
-        return getPreviousStateIds().contains(stateId);
-    }
-
-
-    /**
-     * Computes the hash code of the entered values.
-     *
-     * @return a hash code.
-     */
-    @Override
-    public String hash() {
-
-        long hash  = 0L;
-        int  shift = 3;
-
-        for (Map.Entry<String, StateData> entry: data.entrySet()) {
-            String key   = entry.getKey();
-            Object value = entry.getValue().getValue();
-
-            hash ^= ((long)key.hashCode() << shift)
-                 |  ((long)value.hashCode() << (shift + 3));
-            shift += 2;
-        }
-
-        return getCurrentStateId() + hash;
-    }
-
-
-    /**
-     * Return List of outputs, where combinations of outputname and filtername
-     * that match content in filterFacets is left out.
-     * @return filtered Outputlist.
-     */
-    protected List<Output> filterOutputs(List<Output> outs) {
-        if (filterFacets == null || filterFacets.isEmpty()) {
-            log.debug("No filter for Outputs.");
-            return outs;
-        }
-
-        boolean debug = log.isDebugEnabled();
-
-        if (debug) {
-            log.debug(
-                "Filter Facets with " + filterFacets.size() + " filters.");
-        }
-
-        List<Output> filtered = new ArrayList<Output>();
-
-        for (Output out: outs) {
-            String outName = out.getName();
-
-            if (debug) {
-                log.debug("  filter Facets for Output: " + outName);
-            }
-
-            List<Facet> fFacets = filterFacets.get(outName);
-            if (fFacets != null) {
-                if (debug) {
-                    log.debug("" + fFacets.size() + " filters for: " + outName);
-                    for (Facet tmp: fFacets) {
-                        log.debug("   filter = '" + tmp.getName() + "'");
-                    }
-                }
-
-                List<Facet> resultFacets = new ArrayList<Facet>();
-
-                for (Facet facet: out.getFacets()) {
-                    for (Facet fFacet: fFacets) {
-                        if (facet.getIndex() == fFacet.getIndex()
-                        &&  facet.getName().equals(fFacet.getName())) {
-                            resultFacets.add(facet);
-                            break;
-                        }
-                    }
-                }
-
-                if (debug) {
-                    log.debug(
-                        "Facets after filtering = " + resultFacets.size());
-                }
-
-                if (!resultFacets.isEmpty()) {
-                    DefaultOutput nout = new DefaultOutput(
-                        out.getName(),
-                        out.getDescription(),
-                        out.getMimeType(),
-                        resultFacets);
-                    filtered.add(nout);
-                }
-            }
-            else if (debug) {
-                log.debug("No filter Factes for Output: " + outName);
-            }
-        }
-
-        if (debug) {
-            log.debug("Number of outs after filtering = " + filtered.size());
-        }
-
-        return filtered;
-    }
-
-
-    /**
-     * Get all outputs that the Artifact can do in this state (which includes
-     * all previous states).
-     *
-     * @return list of outputs
-     */
-    public List<Output> getOutputs(Object context) {
-        if (log.isDebugEnabled()) {
-            log.debug("##### Get Outputs for: " + identifier() + " #####");
-            dumpArtifact();
-        }
-
-        List<String> stateIds  = getPreviousStateIds();
-        List<Output> generated = new ArrayList<Output>();
-
-        for (String stateId: stateIds) {
-            DefaultState state = (DefaultState) getState(context, stateId);
-            generated.addAll(getOutputForState(state));
-        }
-
-        generated.addAll(getCurrentOutputs(context));
-
-        return filterOutputs(generated);
-    }
-
-
-    /**
-     * Get output(s) for current state.
-     * @return list of outputs for current state.
-     */
-    public List<Output> getCurrentOutputs(Object context) {
-        DefaultState cur = (DefaultState) getCurrentState(context);
-
-        try {
-            if (cur.validate(this)) {
-                return getOutputForState(cur);
-            }
-        }
-        catch (IllegalArgumentException iae) { }
-
-        return new ArrayList<Output>();
-    }
-
-
-    /**
-     * Get output(s) for a specific state.
-     * @param state State of interest
-     * @return list of output(s) for given state.
-     */
-    protected List<Output> getOutputForState(DefaultState state) {
-
-        if (state == null) {
-            log.error("state == null: This should not happen!");
-            return new ArrayList<Output>();
-        }
-
-        boolean debug = log.isDebugEnabled();
-
-        if (debug) {
-            log.debug("Find Outputs for State: " + state.getID());
-        }
-
-        List<Output> list = state.getOutputs();
-        if (list == null || list.isEmpty()) {
-            if (debug) {
-                log.debug("-> No output modes for this state.");
-            }
-            return new ArrayList<Output>();
-        }
-
-        String stateId = state.getID();
-
-        List<Facet> fs = getFacets(stateId);
-
-        if (fs == null || fs.isEmpty()) {
-            if (debug) {
-                log.debug("No facets found.");
-            }
-            return new ArrayList<Output>();
-        }
-        if (debug) {
-            log.debug("State '" + stateId + "' has facets " + fs);
-        }
-
-        List<Output> gen = generateOutputs(list, fs);
-
-        if (debug) {
-            log.debug("State '" + stateId + "' has " + gen.size() + " outs");
-        }
-
-        return gen;
-    }
-
-
-    /**
-     * Generate a list of outputs with facets from fs if type is found in list
-     * of output.
-     *
-     * @param list List of outputs
-     * @param fs List of facets
-     */
-    protected List<Output> generateOutputs(List<Output> list, List<Facet> fs) {
-        List<Output> generated = new ArrayList<Output>();
-        log.debug("generateOutputs for Artifact " + getName() + " "
-                + identifier());
-
-        boolean debug = log.isDebugEnabled();
-
-        for (Output out: list) {
-            log.debug("check facets for output: " + out.getName());
-            Output o = new DefaultOutput(
-                out.getName(),
-                out.getDescription(),
-                out.getMimeType(),
-                out.getType());
-
-            Set<String> outTypes = new HashSet<String>();
-
-            for (Facet f: out.getFacets()) {
-                if (outTypes.add(f.getName()) && debug) {
-                    log.debug("configured facet " + f);
-                }
-            }
-
-            boolean facetAdded = false;
-            for (Facet f: fs) {
-                String type = f.getName();
-
-                if (outTypes.contains(type)) {
-                    if (debug) {
-                        log.debug("Add facet " + f);
-                    }
-                    facetAdded = true;
-                    o.addFacet(f);
-                }
-            }
-
-            if (facetAdded) {
-                generated.add(o);
-            }
-        }
-
-        return generated;
-    }
-
-
-    /**
-     * Dispatches the computation request to compute(CallContext context, String
-     * hash) with the current hash value of the artifact which is provided by
-     * hash().
-     *
-     * @param context The CallContext.
-     */
-    public Object compute(
-        CallContext context,
-        ComputeType type,
-        boolean     generateFacets
-    ) {
-        return compute(context, hash(), type, generateFacets);
-    }
-
-
-    /**
-     * Dispatches computation requests to the current state which needs to
-     * implement a createComputeCallback(String hash, FLYSArtifact artifact)
-     * method.
-     *
-     * @param context The CallContext.
-     * @param hash The hash value which is used to fetch computed data from
-     * cache.
-     *
-     * @return the computed data.
-     */
-    public Object compute(
-        CallContext context,
-        String      hash,
-        ComputeType type,
-        boolean     generateFacets
-    ) {
-        DefaultState current = (DefaultState) getCurrentState(context);
-        return compute(context, hash, current, type, generateFacets);
-    }
-
-
-    /**
-     * Like compute, but identify State by it id (string).
-     */
-    public Object compute(
-        CallContext context,
-        String      hash,
-        String      stateID,
-        ComputeType type,
-        boolean     generateFacets
-    ) {
-        DefaultState current =
-            (stateID == null)
-            ? (DefaultState)getCurrentState(context)
-            : (DefaultState)getState(context, stateID);
-
-        if (hash == null) {
-            hash = hash();
-        }
-
-        return compute(context, hash, current, type, generateFacets);
-    }
-
-
-    /**
-     * Let current state compute and register facets.
-     *
-     * @param key key of state
-     * @param state state
-     * @param type Type of compute
-     * @param generateFacets Whether new facets shall be generated.
-     */
-    public Object compute(
-        CallContext   context,
-        String        key,
-        DefaultState  state,
-        ComputeType   type,
-        boolean       generateFacets
-    ) {
-        String stateID = state.getID();
-
-        List<Facet> fs = (generateFacets) ? new ArrayList<Facet>() : null;
-
-        try {
-            Cache cache = CacheFactory.getCache(COMPUTING_CACHE);
-
-            Object old = null;
-
-            if (cache != null) {
-                net.sf.ehcache.Element element = cache.get(key);
-                if (element != null) {
-                    log.debug("Got computation result from cache.");
-                    old = element.getValue();
-                }
-            }
-            else {
-                log.debug("cache not configured.");
-            }
-
-            Object res;
-            switch (type) {
-                case FEED:
-                    res = state.computeFeed(this, key, context, fs, old);
-                    break;
-                case ADVANCE:
-                    res = state.computeAdvance(this, key, context, fs, old);
-                    break;
-                case INIT:
-                    res = state.computeInit(this, key, context, context.getMeta(), fs);
-                default:
-                    res = null;
-            }
-
-            if (cache != null && old != res && res != null) {
-                log.debug("Store computation result to cache.");
-                net.sf.ehcache.Element element =
-                    new net.sf.ehcache.Element(key, res);
-                cache.put(element);
-            }
-
-            return res;
-        }
-        finally {
-            if (generateFacets) {
-                if (fs.isEmpty()) {
-                    facets.remove(stateID);
-                }
-                else {
-                    addFacets(stateID, fs);
-                }
-            }
-        }
-    }
-
-    /**
-     * Sets the facets for an ID
-     *
-     * Normally the id is a state ID.
-     *
-     * @param id ID to map the facets to
-     * @param facets List of facets to be stored
-     */
-    protected void addFacets(String id, List<Facet> facets) {
-        this.facets.put(id, facets);
-    }
-
-
-    /**
-     * Method to dump the artifacts state/data.
-     */
-    protected void dumpArtifact() {
-        log.debug("++++++++++++++ DUMP ARTIFACT DATA +++++++++++++++++");
-        // Include uuid, type, name
-        log.debug(" - Name: " + getName());
-        log.debug(" - UUID: " + identifier());
-        log.debug(" - Class: " + this.getClass().getName());
-
-        log.debug("------ DUMP DATA ------");
-        Collection<StateData> allData = data.values();
-
-        for (StateData d: allData) {
-            String name  = d.getName();
-            String value = (String) d.getValue();
-
-            log.debug("- " + name + ": " + value);
-        }
-
-        log.debug("------ DUMP PREVIOUS STATES ------");
-        List<String> stateIds = getPreviousStateIds();
-
-        for (String id: stateIds) {
-            log.debug("- State: " + id);
-        }
-
-        log.debug("CURRENT STATE: " + getCurrentStateId());
-
-        debugFacets();
-        dumpFilterFacets();
-
-        log.debug("++++++++++++++ END ARTIFACT DUMP +++++++++++++++++");
-    }
-
-
-    protected void debugFacets() {
-        log.debug("######### FACETS #########");
-
-        for (Map.Entry<String, List<Facet>> entry: facets.entrySet()) {
-            String out = entry.getKey();
-            List<Facet> fs = entry.getValue();
-            for (Facet f: fs) {
-                log.debug("  # " + out + " : " + f.getName());
-            }
-        }
-
-        log.debug("######## FACETS END ########");
-    }
-
-
-    protected void dumpFilterFacets() {
-        log.debug("######## FILTER FACETS ########");
-
-        if (filterFacets == null || filterFacets.isEmpty()) {
-            log.debug("No Filter Facets defined.");
-            return;
-        }
-
-        for (Map.Entry<String, List<Facet>> entry: filterFacets.entrySet()) {
-            String      out     = entry.getKey();
-            List<Facet> filters = entry.getValue();
-
-            log.debug("There are " + filters.size() + " filters for: " +out);
-
-            for (Facet filter: filters) {
-                log.debug("  filter: " + filter.getName());
-            }
-        }
-
-        log.debug("######## FILTER FACETS END ########");
-    }
-
-
-    /** Destroy and clean up state with given id. */
-    protected void destroyState(String id, Object context) {
-        State s = getState(context, id);
-        s.endOfLife(this, context);
-    }
-
-
-    /**
-     * Calls endOfLife() for each state in the list <i>ids</i>.
-     *
-     * @param ids The State IDs that should be destroyed.
-     * @param context The FLYSContext.
-     */
-    protected void destroyStates(List<String> ids, Object context) {
-        for (int i = 0, num = ids.size(); i < num; i++) {
-            destroyState(ids.get(i), context);
-        }
-    }
-
-
-    /**
-     * Destroy the states.
-     */
-    @Override
-    public void endOfLife(Object context) {
-        if (log.isDebugEnabled()) {
-            log.debug("FLYSArtifact.endOfLife: " + identifier());
-        }
-
-        ArrayList<String> ids       = (ArrayList<String>) getPreviousStateIds();
-        ArrayList<String> toDestroy = (ArrayList<String>) ids.clone();
-
-        toDestroy.add(getCurrentStateId());
-
-        destroyStates(toDestroy, context);
-    }
-
-    /**
-     * Return the Facets which a state provides
-     * @param stateid String that identifies the state
-     * @return List of Facets belonging to the state identifier
-     */
-    protected List<Facet> getFacets(String stateid) {
-        return this.facets.get(stateid);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/FixationArtifact.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,81 +0,0 @@
-package de.intevation.flys.artifacts;
-
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.geom.Lines;
-import de.intevation.flys.artifacts.StaticWKmsArtifact;
-
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.WKms;
-import de.intevation.flys.artifacts.model.fixings.FixRealizingResult;
-
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-import de.intevation.flys.model.FastCrossSectionLine;
-
-import org.apache.log4j.Logger;
-
-/**
- * The default fixation analysis artifact.
- *
- * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
- */
-public class FixationArtifact
-extends      FLYSArtifact
-implements   WaterLineArtifact
-{
-    /** The logger for this class. */
-    private static Logger logger = Logger.getLogger(FixationArtifact.class);
-
-    /** The name of the artifact. */
-    public static final String ARTIFACT_NAME = "fixanalysis";
-
-    /* FacetActivity for this artifact is registered in FixAnalysisCompute . */
-
-    /**
-     * The default constructor.
-     */
-    public FixationArtifact() {
-        logger.debug("ctor()");
-    }
-
-    /**
-     * Returns the name of the concrete artifact.
-     *
-     * @return the name of the concrete artifact.
-     */
-    @Override
-    public String getName() {
-        return ARTIFACT_NAME;
-    }
-
-    @Override
-    public Lines.LineData getWaterLines(
-        int                  facetIdx,
-        FastCrossSectionLine      csl,
-        double                      d,
-        double                      w,
-        CallContext           context
-    ) {
-        FixRealizingResult result = (FixRealizingResult)
-            ((CalculationResult)this.compute(context, ComputeType.ADVANCE, false)).getData();
-
-        WKms wkms = result.getWQKms()[facetIdx];
-
-        double km = csl.getKm();
-
-        // Find W at km.
-        double wAtKm;
-
-        wAtKm = StaticWKmsArtifact.getWAtKm(wkms, km);
-
-        if (wAtKm == -1 || Double.isNaN(wAtKm)) {
-            logger.warn("Waterlevel at km " + km + " unknown.");
-            return new Lines.LineData(new double[][] {{}}, 0d, 0d);
-        }
-
-        // This should be FixRealizationResult, which can be getWQKms()ed
-        return Lines.createWaterLines(csl.getPoints(), wAtKm);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/FlowVelocityMeasurementArtifact.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,194 +0,0 @@
-package de.intevation.flys.artifacts;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-import org.w3c.dom.Document;
-
-import de.intevation.artifactdatabase.state.DefaultOutput;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.FacetActivity;
-import de.intevation.artifactdatabase.state.State;
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.ArtifactFactory;
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.common.utils.XMLUtils;
-import de.intevation.flys.model.FlowVelocityMeasurementValue;
-import de.intevation.flys.artifacts.model.minfo.FlowVelocityMeasurementFacet;
-import de.intevation.flys.artifacts.model.minfo.FlowVelocityMeasurementFactory;
-import de.intevation.flys.artifacts.states.StaticState;
-
-import de.intevation.flys.artifacts.model.FacetTypes;
-
-
-/** Artefact to access flow velocity measurements. */
-public class FlowVelocityMeasurementArtifact
-extends      StaticFLYSArtifact
-implements   FacetTypes
-{
-    /** The logger for this class. */
-    private static Logger logger =
-        Logger.getLogger(FlowVelocityMeasurementArtifact.class);
-
-    /** Artifact key name. */
-    private static final String NAME = "flowvelocitymeasurement";
-
-    /** 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.flowvelocitymeasurement.static";
-
-    /** One and only state to be in. */
-    protected transient State state = null;
-
-    protected String DATA_NAME = "ID";
-
-    /**
-     * Trivial Constructor.
-     */
-    public FlowVelocityMeasurementArtifact() {
-        logger.debug("FlowVelocityMeasurementArtifact.FlowVelocityMeasurementArtifact");
-    }
-
-
-    /** Get artifact key name. */
-    @Override
-    public String getName() {
-        return NAME;
-    }
-
-
-    /** Create a new state with bogus output. */
-    protected State spawnState() {
-        state = new StaticState(STATIC_STATE_NAME);
-        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("FlowVelocityMeasurementArtifact.setup");
-
-        state = new StaticState(STATIC_STATE_NAME);
-
-        if (logger.isDebugEnabled()) {
-            logger.debug(XMLUtils.toString(data));
-        }
-
-        List<Facet> fs = new ArrayList<Facet>();
-        String code = getDatacageIDValue(data);
-
-        if (code != null) {
-            // parse code, interact with factory, add real facets.
-            // store relevant parts of code as data.
-            Facet facet = new FlowVelocityMeasurementFacet(
-                FLOW_VELOCITY_MEASUREMENT,
-                "flowvelocity-name");
-            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("initialize");
-    }
-
-
-    /** Get the db-unbound flow velocity measurement value with given
-     * id. */
-    public FlowVelocityMeasurementValue.FastFlowVelocityMeasurementValue
-        getFlowVelocityMeasurementValue()
-    {
-        return FlowVelocityMeasurementFactory.getFlowVelocityMeasurement(
-            Integer.parseInt(getDataAsString(DATA_NAME)));
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/GaugeDischargeArtifact.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,187 +0,0 @@
-package de.intevation.flys.artifacts;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Document;
-
-import de.intevation.artifactdatabase.state.Facet;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.ArtifactFactory;
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.WQKms;
-
-import de.intevation.flys.artifacts.states.DefaultState;
-
-import de.intevation.flys.artifacts.model.Calculation;
-import de.intevation.flys.artifacts.model.CalculationResult;
-
-import de.intevation.flys.artifacts.model.DischargeTables;
-
-import de.intevation.flys.model.Gauge;
-import de.intevation.flys.model.River;
-
-import de.intevation.flys.utils.FLYSUtils;
-
-
-/**
- * Artifact to get discharge curves at gauges.
- */
-public class GaugeDischargeArtifact
-extends      WINFOArtifact
-implements   FacetTypes
-{
-    /** The logger for this class. */
-    private static Logger logger = Logger.getLogger(GaugeDischargeArtifact.class);
-
-    /** The name of the artifact. */
-    public static final String ARTIFACT_NAME = "gaugedischarge";
-
-
-    /**
-     * Trivial Constructor.
-     */
-    public GaugeDischargeArtifact() {
-        logger.debug("GaugeDischargeArtifact.GaugeDischargeArtifact()");
-    }
-
-
-    /**
-     * Gets called from factory, to set things up.
-     * Especially, when loaded via datacage mechanisms, provide the
-     * data document.
-     * @param data filled with stuff from dc, if any.
-     */
-    @Override
-    public void setup(
-        String          identifier,
-        ArtifactFactory factory,
-        Object          context,
-        CallMeta        callMeta,
-        Document        data)
-    {
-        logger.debug("GaugeDischargeArtifact.setup");
-        String ids = StaticFLYSArtifact.getDatacageIDValue(data);
-        addStringData("ids", ids);
-        logger.debug("id for gaugedischarge: " + ids);
-        super.setup(identifier, factory, context, callMeta, data);
-    }
-
-
-    /** Return the name of this artifact. */
-    public String getName() {
-        return ARTIFACT_NAME;
-    }
-
-
-    /**
-     * Setup state and facet, copy from master artifact.
-     */
-    @Override
-    protected void initialize(Artifact art, Object context, CallMeta meta) {
-        logger.debug("GaugeDischargeArtifact.initialize");
-        List<Facet> fs = new ArrayList<Facet>();
-        FLYSArtifact artifact = (FLYSArtifact) art;
-        importData(artifact, "river");
-
-        // Get the location(s)
-        //importData(artifact, "ld_mode", ld_from, ld_to, ld_locations
-        addStringData("ld_from", "0");
-        addStringData("ld_to", "1000");
-        addStringData("ld_mode", "distance");
-
-        DefaultState state = (DefaultState) getCurrentState(context);
-        state.computeInit(this, hash(), context, meta, fs);
-        if (!fs.isEmpty()) {
-            logger.debug("Facets to add in GaugeDischargeArtifact.initialize. ("
-                + state.getID() + "/ " + getCurrentStateId() + ").");
-            addFacets(getCurrentStateId(), fs);
-        }
-        else {
-            logger.debug("No facets to add in GaugeDischargeArtifact.initialize ("
-                + state.getID() + "/ "+getCurrentStateId()+").");
-        }
-    }
-
-
-    /** Get the Gauges name which came with datacage data-document. */
-    public String getGaugeName() {
-        return this.getDataAsString("ids");
-    }
-
-
-    /** Get the Gauges which came with datacage data-document. */
-    public Gauge getGauge() {
-        River river = FLYSUtils.getRiver(this);
-        return river.determineGaugeByName(getGaugeName());
-    }
-
-
-    /**
-     * Returns the data that is used to create discharge curves.
-     * @return CalculationResult with WQKms.
-     */
-    public CalculationResult getDischargeCurveData() {
-
-        River river = FLYSUtils.getRiver(this);
-        if (river == null) {
-            return error(new WQKms[0], "no.river.selected");
-        }
-        /*
-        // This one would allow to automatically pick the right Gauge.
-        double [] distance = FLYSUtils.getKmRange(this);
-        logger.debug("getDischargeCurveData: get range");
-
-        if (distance == null) {
-            return error(new WQKms[0], "no.range.found");
-        }
-
-        List<Gauge> gauges = river.determineGauges(distance[0], distance[1]);
-        logger.debug("getDischargeCurveData: got " + gauges.size() + " gauges");
-
-        if (gauges.isEmpty()) {
-            return error(new WQKms[0], "no.gauge.selected");
-        }
-
-        String [] names = new String[gauges.size()];
-
-        for (int i = 0; i < names.length; ++i) {
-            names[i] = gauges.get(i).getName();
-            logger.debug("getDischargeCurveData: name " + names[i]);
-        }
-        */
-
-        DischargeTables dt = new DischargeTables(river.getName(), getDataAsString("ids"));
-
-        Map<String, double [][]> map = dt.getValues(100);
-
-        ArrayList<WQKms> res = new ArrayList<WQKms>();
-
-        Gauge gauge = river.determineGaugeByName(this.getDataAsString("ids"));
-
-        String name = getGaugeName();
-        double [][] values = map.get(name);
-        if (values == null) {
-            logger.error("No values for this gauge / discharge found.");
-            return error(new WQKms[0], "no.gauge.found");
-        }
-        for (int i = 0 ; i < values[0].length; i++) {
-            values[0][i] += gauge.getDatum().doubleValue();
-        }
-        double [] kms = new double[values[0].length];
-        Arrays.fill(kms, gauge.getStation().doubleValue());
-        res.add(new WQKms(kms, values[0], values[1], name));
-
-        return new CalculationResult(
-            res.toArray(new WQKms[res.size()]),
-            new Calculation());
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/GaugeDischargeCurveArtifact.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,211 +0,0 @@
-package de.intevation.flys.artifacts;
-
-import java.math.BigDecimal;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Document;
-
-import de.intevation.artifactdatabase.state.DefaultOutput;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.data.DefaultStateData;
-
-import de.intevation.artifacts.ArtifactFactory;
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.artifacts.common.ArtifactNamespaceContext;
-import de.intevation.artifacts.common.utils.XMLUtils;
-
-import de.intevation.flys.artifacts.model.GaugeDischargeCurveFacet;
-import de.intevation.flys.artifacts.resources.Resources;
-import de.intevation.flys.artifacts.states.StaticState;
-
-import de.intevation.flys.model.Gauge;
-import de.intevation.flys.utils.FLYSUtils;
-
-
-/**
- * Artifact to calculate a discharge curve from a gauge overview info
- *
- * @author <a href="mailto:bjoern.ricks@intevation.de">Björn Ricks</a>
- */
-public class GaugeDischargeCurveArtifact
-extends      AbstractStaticStateArtifact
-{
-
-    private static final Logger logger =
-        Logger.getLogger(GaugeDischargeCurveArtifact.class);
-
-    public static final String XPATH_RIVER = "/art:action/art:river/@art:name";
-    public static final String XPATH_GAUGE = "/art:action/art:gauge/@art:reference";
-    public static final String NAME = "gaugedischargecurve";
-    public static final String STATIC_STATE_NAME = "state.gaugedischargecurve.static";
-    public static final String UIPROVIDER = "gauge_discharge_curve";
-    public static final String GAUGE_DISCHARGE_CURVE_FACET =
-        "gauge_discharge_curve";
-    public static final String GAUGE_DISCHARGE_CURVE_AT_EXPORT_FACET =
-        "at";
-    public static final String GAUGE_DISCHARGE_CURVE_OUT =
-        "discharge_curve";
-    public static final String GAUGE_DISCHARGE_CURVE_AT_EXPORT_OUT =
-        "computed_dischargecurve_at_export";
-    public static final String GAUGE_DISCHARGE_CURVE_EXPORT_OUT =
-        "computed_dischargecurve_export";
-    public static final String GAUGE_DISCHARGE_CURVE_CSV_FACET =
-        "csv";
-    public static final String GAUGE_DISCHARGE_CURVE_PDF_FACET =
-        "pdf";
-
-    private Facet atexportfacet;
-    private Facet curvefacet;
-    private Facet csvfacet;
-    private Facet pdffacet;
-
-    /**
-     * Setup initializes the data by extracting the river and gauge from
-     * the XML Document.
-     */
-    @Override
-    public void setup(
-        String          identifier,
-        ArtifactFactory factory,
-        Object          context,
-        CallMeta        callmeta,
-        Document        data)
-    {
-        logger.debug("GaugeDischargeCurveArtifact.setup");
-
-        if (logger.isDebugEnabled()) {
-            logger.debug("GaugeDischargeCurveArtifact.setup" + XMLUtils.toString(data));
-        }
-        String gaugeref = XMLUtils.xpathString(data, XPATH_GAUGE,
-                ArtifactNamespaceContext.INSTANCE);
-        String rivername = XMLUtils.xpathString(data, XPATH_RIVER,
-                ArtifactNamespaceContext.INSTANCE);
-
-        addData("river", new DefaultStateData("river",
-                    Resources.getMsg(callmeta,
-                        "facet.gauge_discharge_curve.river",
-                        "Name of the river"),
-                    "String", rivername));
-        addData("reference_gauge", new DefaultStateData("reference_gauge",
-                    Resources.getMsg(callmeta,
-                        "facet.gauge_discharge_curve.reference_gauge",
-                        "Gauge official number"),
-                    "Long", gaugeref));
-
-        Gauge gauge = FLYSUtils.getReferenceGauge(this);
-        String gaugename = "";
-        Double gaugelocation = null;
-        if (gauge != null) {
-            gaugename = gauge.getName();
-            BigDecimal station = gauge.getStation();
-            if (station != null) {
-                gaugelocation = station.doubleValue();
-            }
-        }
-
-        addData("gauge_name", new DefaultStateData("gauge_name",
-                    Resources.getMsg(callmeta,
-                        "facet.gauge_discharge_curve.gauge_name",
-                        "Name of the gauge"),
-                    "String", gaugename));
-
-        if (gaugelocation != null) {
-            addData("ld_locations", new DefaultStateData("ld_locations",
-                        Resources.getMsg(callmeta,
-                            "facet.gauge_discharge_curve.gauge_location",
-                            "Location of the gauge"),
-                        "Double", gaugelocation.toString()));
-        }
-
-        String description = Resources.format(callmeta,
-                "facet.gauge_discharge_curve.description",
-                "Discharge curve on gauge",
-                rivername,
-                gaugename);
-
-        List<Facet> fs = new ArrayList<Facet>(4);
-        curvefacet = new GaugeDischargeCurveFacet(
-                GAUGE_DISCHARGE_CURVE_FACET, description);
-        fs.add(curvefacet);
-
-        description = Resources.format(callmeta,
-                "facet.gauge_discharge_curve_at_export.description",
-                "Discharge curve AT export on gauge",
-                rivername,
-                gaugename);
-        atexportfacet = new GaugeDischargeCurveFacet(
-                GAUGE_DISCHARGE_CURVE_AT_EXPORT_FACET, description);
-        fs.add(atexportfacet);
-
-        description = Resources.format(callmeta,
-                "facet.computed_dischargecurve_export.csv",
-                "Discharge curve CSV export on gauge",
-                rivername,
-                gaugename);
-        csvfacet = new GaugeDischargeCurveFacet(
-                GAUGE_DISCHARGE_CURVE_CSV_FACET, description);
-        fs.add(csvfacet);
-
-        description = Resources.format(callmeta,
-                "facet.computed_dischargecurve_export.pdf",
-                "Discharge curve PDF export on gauge",
-                rivername,
-                gaugename);
-        pdffacet = new GaugeDischargeCurveFacet(
-                GAUGE_DISCHARGE_CURVE_PDF_FACET, description);
-        fs.add(pdffacet);
-
-        addFacets(STATIC_STATE_NAME, fs);
-
-        super.setup(identifier, factory, context, callmeta, data);
-    }
-
-    @Override
-    public String getName() {
-        return NAME;
-    }
-
-    @Override
-    protected void initStaticState() {
-        StaticState state = new StaticState(STATIC_STATE_NAME);
-
-        List<Facet> fs = new ArrayList<Facet>(1);
-        fs.add(curvefacet);
-
-        DefaultOutput output = new DefaultOutput(
-            GAUGE_DISCHARGE_CURVE_OUT,
-            "output.discharge_curve",
-            "image/png",
-            fs,
-            "chart");
-        state.addOutput(output);
-
-        fs = new ArrayList<Facet>(1);
-        fs.add(atexportfacet);
-        output = new DefaultOutput(
-            GAUGE_DISCHARGE_CURVE_AT_EXPORT_OUT,
-            "output.computed_dischargecurve_at_export",
-            "text/plain",
-            fs,
-            "export");
-        state.addOutput(output);
-
-        fs = new ArrayList<Facet>(2);
-        fs.add(csvfacet);
-        fs.add(pdffacet);
-        output = new DefaultOutput(
-            GAUGE_DISCHARGE_CURVE_EXPORT_OUT,
-            "output.computed_dischargecurve_export",
-            "text/plain",
-            fs,
-            "export");
-        state.addOutput(output);
-
-        state.setUIProvider(UIPROVIDER);
-        setStaticState(state);
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/HYKArtifact.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,124 +0,0 @@
-package de.intevation.flys.artifacts;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Document;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.ArtifactFactory;
-import de.intevation.artifacts.CallMeta;
-
-
-import de.intevation.flys.artifacts.states.DefaultState;
-
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.FacetActivity;
-
-
-/**
- * Artifact to get hydr zones (HYKs).
- */
-public class HYKArtifact extends StaticFLYSArtifact {
-
-    /** Name of Artifact. */
-    public static final String HYK_ARTIFACT_NAME = "hyk";
-
-    /** Name of data item keeping the hyk id to load formations from. */
-    public static final String HYK_ID = "hyk_artifact.data.id";
-
-    /** Name of data item keeping the km of cs master. */
-    public static final String HYK_KM = "hyk_artifact.data.km";
-
-    /** Own logger. */
-    private static final Logger logger =
-        Logger.getLogger(HYKArtifact.class);
-
-    static {
-        // TODO: Move to configuration.
-        FacetActivity.Registry.getInstance()
-            .register(HYK_ARTIFACT_NAME, FacetActivity.INACTIVE);
-    }
-
-    /** Return given name. */
-    @Override
-    public String getName() {
-        return HYK_ARTIFACT_NAME;
-    }
-
-
-    /** Store ids, do super.setup. */
-    @Override
-    public void setup(
-        String          identifier,
-        ArtifactFactory factory,
-        Object          context,
-        CallMeta        callMeta,
-        Document        data)
-    {
-        logger.info("HYKArtifact.setup");
-
-        String ids = getDatacageIDValue(data);
-
-        logger.info("HYKArtifact.setup: id is " + ids);
-
-        addStringData(HYK_ID, ids);
-
-        super.setup(identifier, factory, context, callMeta, data);
-    }
-
-
-    /** Set km as Data. */
-    public void setKm(double km) {
-        addStringData(HYK_KM, Double.toString(km));
-    }
-
-
-    /** Get km from state data. */
-    public double getKm() {
-        Double km = getDataAsDouble(HYK_KM);
-        if (km == null) {
-            // XXX returning 0 is to be compatible to older versions that had an
-            // own method getDataAsDouble that returned 0 if parsing the
-            // parameter failed.
-            return 0;
-        }
-        else {
-            return km;
-        }
-    }
-
-
-    /** Get hyk-id from state data. */
-    public int getHykId() {
-        return getDataAsInteger(HYK_ID);
-    }
-
-
-    /** Do not copy data from daddyfact. */
-    @Override
-    protected void initialize(
-        Artifact artifact,
-        Object   context,
-        CallMeta callMeta)
-    {
-        logger.debug("HYKArtifact.initialize");
-        importData((FLYSArtifact)artifact, "river");
-
-        List<Facet> fs = new ArrayList<Facet>();
-
-        DefaultState state = (DefaultState) getCurrentState(context);
-        state.computeInit(this, hash(), context, callMeta, fs);
-        if (!fs.isEmpty()) {
-            logger.debug("Facets to add in HYKArtifact.initialize .");
-            addFacets(getCurrentStateId(), fs);
-        }
-        else {
-            logger.debug("No facets to add in HYKArtifact.initialize ("
-                + state.getID() + ").");
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/MINFOArtifact.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,31 +0,0 @@
-package de.intevation.flys.artifacts;
-
-/**
- * The default MINFO artifact.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class MINFOArtifact
-extends      FLYSArtifact
-{
-    /** The name of the artifact. */
-    public static final String ARTIFACT_NAME = "minfo";
-
-
-    /**
-     * The default constructor.
-     */
-    public MINFOArtifact() {
-    }
-
-    /**
-     * Returns the name of the concrete artifact.
-     *
-     * @return the name of the concrete artifact.
-     */
-    @Override
-    public String getName() {
-        return ARTIFACT_NAME;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,394 +0,0 @@
-package de.intevation.flys.artifacts;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Document;
-
-import de.intevation.artifactdatabase.data.DefaultStateData;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.FacetActivity;
-import de.intevation.artifactdatabase.state.DefaultOutput;
-import de.intevation.artifactdatabase.state.State;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.ArtifactFactory;
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.flys.model.Gauge;
-import de.intevation.flys.model.MainValue;
-import de.intevation.flys.model.River;
-
-import de.intevation.flys.artifacts.access.RangeAccess;
-import de.intevation.flys.artifacts.model.Calculation;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.MainValuesQFacet;
-import de.intevation.flys.artifacts.model.MainValuesWFacet;
-import de.intevation.flys.artifacts.model.NamedDouble;
-import de.intevation.flys.artifacts.model.WstValueTable;
-import de.intevation.flys.artifacts.model.WstValueTableFactory;
-
-import de.intevation.flys.artifacts.states.StaticState;
-import de.intevation.flys.artifacts.resources.Resources;
-
-import de.intevation.flys.utils.FLYSUtils;
-
-
-/**
- * Artifact to access main and extreme values of a river.
- * This artifact neglects (Static)FLYSArtifacts capabilities of interaction
- * with the StateEngine by overriding the getState*-methods.
- */
-public class MainValuesArtifact
-extends      StaticFLYSArtifact
-implements   FacetTypes
-{
-    /** The logger for this class. */
-    private static Logger logger = Logger.getLogger(MainValuesArtifact.class);
-
-    /** The name of the artifact. */
-    public static final String ARTIFACT_NAME = "mainvalue";
-
-    /** The name of the static state for this artifact. */
-    public static final String STATIC_STATE_NAME = "state.mainvalue.static";
-
-    /** One and only state to be in. */
-    protected transient State state = null;
-
-
-    static {
-        // TODO: Move to configuration.
-        FacetActivity.Registry.getInstance().register(
-            ARTIFACT_NAME,
-            new FacetActivity() {
-                @Override
-                public Boolean isInitialActive(
-                    Artifact artifact,
-                    Facet    facet,
-                    String   outputName
-                ) {
-                    return outputName.equals("computed_discharge_curve")
-                        || outputName.equals("duration_curve");
-                }
-            });
-    }
-
-
-    /**
-     * Trivial Constructor.
-     */
-    public MainValuesArtifact() {
-        logger.debug("MainValuesArtifact.MainValuesartifact()");
-    }
-
-
-    /**
-     * Gets called from factory, to set things up.
-     */
-    @Override
-    public void setup(
-        String          identifier,
-        ArtifactFactory factory,
-        Object          context,
-        CallMeta        callMeta,
-        Document        data)
-    {
-        logger.debug("MainValuesArtifact.setup");
-        state = new StaticState(STATIC_STATE_NAME);
-
-        List<Facet> fs = new ArrayList<Facet>();
-        addFacets(state.getID(), fs);
-        spawnState();
-        super.setup(identifier, factory, context, callMeta, data);
-        String restriction = getDatacageIDValue(data);
-        logger.debug("mainvalue restriction " + restriction);
-        boolean restricted = restriction.endsWith("q") || restriction.endsWith("w");
-        if (!restricted || restriction.endsWith("q")) {
-            Facet qfacet0 = new MainValuesQFacet(
-                DURATION_MAINVALUES_Q,
-                Resources.getMsg(
-                    callMeta,
-                    "facet.discharge_curves.mainvalues.q",
-                    "facet.discharge_curves.mainvalues.q"),
-                false);
-            Facet qfacet1 = new MainValuesQFacet(
-                COMPUTED_DISCHARGE_MAINVALUES_Q,
-                Resources.getMsg(
-                    callMeta,
-                    "facet.discharge_curves.mainvalues.q",
-                    "facet.discharge_curves.mainvalues.q"),
-                false);
-            Facet qfacet2 = new MainValuesQFacet(
-                MAINVALUES_Q,
-                Resources.getMsg(
-                    callMeta,
-                    "facet.discharge_curves.mainvalues.q",
-                    "facet.discharge_curves.mainvalues.q"),
-                true);
-            Facet qfacet3 = new MainValuesQFacet(
-                HISTORICAL_DISCHARGE_MAINVALUES_Q,
-                Resources.getMsg(
-                    callMeta,
-                    "historical_discharge.mainvalues.q",
-                    "historical_discharge.mainvalues.q"),
-                false);
-
-            fs.add(qfacet0);
-            fs.add(qfacet1);
-            fs.add(qfacet2);
-            fs.add(qfacet3);
-        }
-        if (!restricted || restriction.endsWith("w")) {
-            Facet wfacet1 = new MainValuesWFacet(
-                COMPUTED_DISCHARGE_MAINVALUES_W,
-                Resources.getMsg(
-                    callMeta,
-                    "facet.discharge_curves.mainvalues.w",
-                    "facet.discharge_curves.mainvalues.w"),
-                false);
-            Facet wfacet2 = new MainValuesWFacet(
-                MAINVALUES_W,
-                Resources.getMsg(
-                    callMeta,
-                    "facet.discharge_curves.mainvalues.w",
-                    "facet.discharge_curves.mainvalues.w"),
-                true);
-            Facet wfacet3 = new MainValuesWFacet(
-                HISTORICAL_DISCHARGE_MAINVALUES_W,
-                Resources.getMsg(
-                    callMeta,
-                    "historical_discharge.mainvalues.w",
-                    "historical_discharge.mainvalues.w"),
-                true);
-            fs.add(wfacet1);
-            fs.add(wfacet2);
-            fs.add(wfacet3);
-        }
-    }
-
-
-    /**
-     * Create "the" state.
-     */
-    protected State spawnState() {
-        state = new StaticState(STATIC_STATE_NAME);
-        List<Facet> fs = (List<Facet>) getFacets(STATIC_STATE_NAME);
-
-        DefaultOutput mainValuesOutput = new DefaultOutput(
-            "computed_discharge_curve",
-            "output.computed_discharge_curve", "image/png",
-            fs,
-            "chart");
-
-        state.getOutputs().add(mainValuesOutput);
-        return state;
-    }
-
-
-    /** Get important data from the 'calling' artifact. */
-    @Override
-    protected void initialize(Artifact artifact, Object context, CallMeta meta) {
-        logger.debug("MainValuesArtifact.initialize");
-        FLYSArtifact winfo = (FLYSArtifact) artifact;
-        RangeAccess rangeAccess = new RangeAccess(winfo, null);
-        double [] locations = rangeAccess.getKmRange();
-
-        if (locations != null) {
-            double location = locations[0];
-            addData("ld_locations", new DefaultStateData("ld_locations", null, null,
-                    String.valueOf(location)));
-        }
-        else {
-            logger.error("No location for mainvalues given.");
-        }
-        importData(winfo, "river");
-        // In the case of DischargeWQCurves, there are no locations, but a gauge.
-        if (getDataAsString("ld_locations") == null) {
-            // TODO its a tad difficult to remodel Range/Gauge-Access to
-            // do this.
-            String refGaugeID = winfo.getDataAsString("reference_gauge");
-            if (refGaugeID != null) {
-                Gauge g = Gauge.getGaugeByOfficialNumber(Integer.parseInt(refGaugeID));
-                addData("ld_locations", new DefaultStateData("ld_locations", null, null,
-                    String.valueOf(g.getStation())));
-            }
-            else {
-                logger.error("MainValuesArtifact: No location/gauge.");
-            }
-        }
-    }
-
-
-    /**
-     * 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.
-     * @param cc ignored.
-     * @return the "current" 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) {
-        if (state != null)
-            return state;
-        else
-            return spawnState();
-    }
-
-
-    /**
-     * Access the Gauge that the mainvalues are taken from.
-     * @return Gauge that main values are taken from or null in case of
-     *         invalid parameterization.
-     */
-    protected Gauge getGauge() {
-        River river = FLYSUtils.getRiver(this);
-
-        // TODO use helper to get location as double
-        String locationStr = getDataAsString("ld_locations");
-
-        if (river == null) {
-            logger.error("River is null");
-            return null;
-        }
-
-        if (locationStr == null) {
-            logger.error("Locationstr is null");
-            return null;
-        }
-
-        double location = Double.parseDouble(locationStr);
-
-        return river.determineGaugeByPosition(location);
-    }
-
-
-    /**
-     * Get current location.
-     * @return the location.
-     */
-    public double getLocation() {
-        double location = Double.parseDouble(getDataAsString("ld_locations"));
-        return location;
-    }
-
-
-    /**
-     * Get a list of "Q" main values.
-     * @return list of Q main values.
-     */
-    public List<NamedDouble> getMainValuesQ(boolean atGauge) {
-        List<NamedDouble> filteredList = new ArrayList<NamedDouble>();
-        Gauge gauge = getGauge();
-        WstValueTable interpolator = WstValueTableFactory.getTable(FLYSUtils.getRiver(this));
-        Calculation c = new Calculation();
-        double w_out[] = {0.0f};
-        double q_out[] = {0.0f};
-        double kms[] = {getLocation()};
-        double gaugeStation = gauge.getStation().doubleValue();
-        if (gauge != null) {
-            List<MainValue> orig = gauge.getMainValues();
-            for (MainValue mv : orig) {
-                if (mv.getMainValue().getType().getName().equals("Q")) {
-                    if (atGauge) {
-                        q_out[0] = mv.getValue().doubleValue();
-                    }
-                    else {
-                        interpolator.interpolate(mv.getValue().doubleValue(),
-                            gaugeStation, kms, w_out, q_out, c);
-                    }
-                    filteredList.add(new NamedDouble(
-                                mv.getMainValue().getName(),
-                                q_out[0]
-                                ));
-                }
-            }
-        }
-        return filteredList;
-    }
-
-
-    /**
-     * Get a list of "W" main values.
-     * @param atGauge if true, do not interpolate
-     * @return list of W main values.
-     */
-    public List<NamedDouble> getMainValuesW(boolean atGauge) {
-        List<NamedDouble> filteredList = new ArrayList<NamedDouble>();
-        Gauge gauge = getGauge();
-        WstValueTable interpolator = WstValueTableFactory.getTable(FLYSUtils.getRiver(this));
-        Calculation c = new Calculation();
-
-        double gaugeStation = gauge.getStation().doubleValue();
-        double w_out[] = {0.0f};
-        double q_out[] = {0.0f};
-        double kms[] = {getLocation()};
-        if (gauge != null) {
-            List<MainValue> orig = gauge.getMainValues();
-            for (MainValue mv : orig) {
-                if (atGauge) {
-                    if (mv.getMainValue().getType().getName().equals("W")) {
-                        filteredList.add(new NamedDouble(mv.getMainValue().getName(),
-                                mv.getValue().doubleValue()));
-
-                    }
-                } else
-                // We cannot interpolate the W values, so derive them
-                // from given Q values.
-                if (mv.getMainValue().getType().getName().equals("Q")) {
-                    interpolator.interpolate(mv.getValue().doubleValue(),
-                            gaugeStation, kms, w_out, q_out, c);
-                    filteredList.add(new NamedDouble(
-                                "W(" + mv.getMainValue().getName() +")",
-                                w_out[0]
-                                ));
-                }
-            }
-        }
-        return filteredList;
-    }
-
-
-    /**
-     * Returns the name of this artifact ('mainvalue').
-     *
-     * @return 'mainvalue'
-     */
-    public String getName() {
-        return ARTIFACT_NAME;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/ManualPointsArtifact.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,145 +0,0 @@
-package de.intevation.flys.artifacts;
-
-import java.awt.geom.Point2D;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.w3c.dom.Document;
-
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.ArtifactFactory;
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.CallMeta;
-import de.intevation.flys.artifacts.geom.Lines;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.states.DefaultState;
-import de.intevation.flys.model.FastCrossSectionLine;
-
-
-/**
- * Artifact to store user-added points and water lines.
- */
-public class ManualPointsArtifact
-extends      StaticFLYSArtifact
-implements   FacetTypes, WaterLineArtifact
-{
-    private static final long serialVersionUID = 7096025125474986011L;
-
-    /** The logger for this class. */
-    private static Logger logger = Logger.getLogger(ManualPointsArtifact.class);
-
-    /** The name of the artifact. */
-    public static final String ARTIFACT_NAME = "manualpoints";
-
-
-    public ManualPointsArtifact() {
-        logger.debug("ManualPointsArtifact.ManualPointsArtifact()");
-    }
-
-
-    /**
-     * Gets called from factory to set things up.
-     */
-    @Override
-    public void setup(
-            String          identifier,
-            ArtifactFactory factory,
-            Object          context,
-            CallMeta        callMeta,
-            Document        data)
-    {
-        logger.debug("ManualPointsArtifact.setup");
-        super.setup(identifier, factory, context, callMeta, data);
-        initialize(null, context, callMeta);
-    }
-
-
-    /** Return the name of this artifact. */
-    @Override
-    public String getName() {
-        return ARTIFACT_NAME;
-    }
-
-
-    /** Access state data storing the jsonstring with points. */
-    public String getPointsData(String facetName) {
-        return getDataAsString(facetName + ".data");
-    }
-
-
-    /**
-     * Access state data storing the jsonstring with lines.
-     * @param facetName Name of facet or null if the so far
-     *                  only known case should be picked.
-     * @return (String) value of data element (expect json).
-     */
-    public String getLinesData(String facetName) {
-        if (facetName == null)
-            return getDataAsString("cross_section.manualpoints.lines");
-        // TODO .lineS?
-        return getDataAsString(facetName + ".line");
-    }
-
-
-    /** Setup state and facet. */
-    @Override
-    protected void initialize(Artifact artifact, Object context, CallMeta meta) {
-        logger.debug("ManualPointsArtifact.initialize");
-        List<Facet> fs = new ArrayList<Facet>();
-
-        DefaultState state = (DefaultState) getCurrentState(context);
-        state.computeInit(this, hash(), context, meta, fs);
-        if (!fs.isEmpty()) {
-            logger.debug("Facets to add in ManualPointsArtifact.initialize .");
-            addFacets(getCurrentStateId(), fs);
-        }
-        else {
-            logger.debug("No facets to add in ManualPointsArtifact.initialize ("
-                    + state.getID() + ").");
-        }
-    }
-
-
-    /**
-     * Get value of line at index.
-     * @param index index in json array defining lines.
-     * @return water height of line at given index.
-     */
-    protected double getLine(int index) {
-        try {
-            JSONArray lines = new JSONArray(getLinesData(null));
-            JSONArray array = lines.getJSONArray(index);
-
-            return array.getDouble(0);
-        }
-        catch(JSONException e){
-            logger.error("Could not decode json for line.");
-            return 0d;
-        }
-    }
-
-
-    /**
-     * Get the water line "surface".
-     * @param index index of facets data.
-     * @param csl 'ground' against which to determine water surface.
-     * @param a (ignored in this implementation).
-     * @param b (ignored in this implementation).
-     * @param context (ignored in this implementation).
-     */
-    @Override
-    public Lines.LineData getWaterLines(
-            int                  index,
-            FastCrossSectionLine csl,
-            double a, double b,
-            CallContext context
-            ) {
-        List<Point2D> points = csl.getPoints();
-        return Lines.createWaterLines(points, getLine(index));
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/MapArtifact.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,188 +0,0 @@
-package de.intevation.flys.artifacts;
-
-import de.intevation.artifactdatabase.ProtocolUtils;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.Output;
-import de.intevation.artifactdatabase.state.State;
-import de.intevation.artifactdatabase.state.StateEngine;
-import de.intevation.artifacts.ArtifactFactory;
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
-import de.intevation.flys.artifacts.RiverAxisArtifact.RiverAxisState;
-import de.intevation.flys.artifacts.context.FLYSContext;
-import de.intevation.flys.artifacts.model.map.WMSDBLayerFacet;
-import de.intevation.flys.artifacts.states.DefaultState;
-import de.intevation.flys.model.River;
-import de.intevation.flys.utils.FLYSUtils;
-import de.intevation.flys.utils.MapUtils;
-
-import java.util.List;
-
-import org.apache.log4j.Logger;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-
-public class MapArtifact extends FLYSArtifact {
-
-    private static final Logger logger =
-        Logger.getLogger(MapArtifact.class);
-
-    @Override
-    public void setup(
-        String          identifier,
-        ArtifactFactory factory,
-        Object          context,
-        CallMeta        callmeta,
-        Document        data)
-    {
-        logger.debug("MapArtifact.setup");
-        this.identifier = identifier;
-        name = "new_map";
-
-        List<State> states = getStates(context);
-
-        setCurrentState(states.get(0));
-    }
-
-
-    @Override
-    protected void appendBackgroundActivity(
-        ElementCreator cr,
-        Element        root,
-        CallContext    context
-    ) {
-        Element inBackground = cr.create("background-processing");
-        root.appendChild(inBackground);
-
-        cr.addAttr(
-            inBackground,
-            "value",
-            String.valueOf(context.isInBackground()),
-            true);
-    }
-
-
-    /**
-     * Append output mode nodes to a document.
-     */
-    @Override
-    protected void appendOutputModes(
-        Document    doc,
-        Element     outs,
-        CallContext context,
-        String      uuid)
-    {
-        List<String> stateIds = getPreviousStateIds();
-
-        FLYSContext flysContext = FLYSUtils.getFlysContext(context);
-        StateEngine engine      = (StateEngine) flysContext.get(
-            FLYSContext.STATE_ENGINE_KEY);
-
-        for (String stateId: stateIds) {
-            logger.debug("Append output modes for state: " + stateId);
-            DefaultState state = (DefaultState) engine.getState(stateId);
-
-            List<Output> list = state.getOutputs();
-            if (list == null || list.isEmpty()) {
-                logger.debug("-> No output modes for this state.");
-                continue;
-            }
-
-            List<Facet> fs = getFacets(stateId);
-
-            if (fs == null || fs.isEmpty()) {
-                logger.debug("No facets for previous state found.");
-                continue;
-            }
-
-            logger.debug("Found " + fs.size() + " facets in previous states.");
-
-            List<Output> generated = generateOutputs(list, fs);
-
-            ProtocolUtils.appendOutputModes(doc, outs, generated);
-        }
-
-        try {
-            DefaultState cur = (DefaultState) getCurrentState(context);
-            if (cur.validate(this)) {
-                List<Output> list = cur.getOutputs();
-                if (list != null && list.size() > 0) {
-                    logger.debug(
-                        "Append output modes for current state: " + cur.getID());
-
-                    List<Facet> fs = getFacets(cur.getID());
-
-                    if (fs != null && fs.size() > 0) {
-                        List<Output> generated = generateOutputs(list, fs);
-
-                        logger.debug("Found " + fs.size() + " current facets.");
-                        if (!generated.isEmpty()) {
-                            ProtocolUtils.appendOutputModes(
-                                doc, outs, generated);
-                        }
-                    }
-                    else {
-                        logger.debug("No facets found for the current state.");
-                    }
-                }
-            }
-        }
-        catch (IllegalArgumentException iae) {
-            // state is not valid, so we do not append its outputs.
-        }
-    }
-
-    public static class MapState extends RiverAxisState {
-
-        @Override
-        public Object computeAdvance(
-            FLYSArtifact artifact,
-            String       hash,
-            CallContext  context,
-            List<Facet>  facets,
-            Object       old)
-        {
-            logger.debug("MapState.computeAdvance");
-
-            this.artifact = artifact;
-
-            String type = getFacetType();
-
-            WMSDBLayerFacet facet = new WMSDBLayerFacet(
-                0,
-                type,
-                getTitle(context.getMeta()),
-                ComputeType.ADVANCE,
-                getID(), hash,
-                getUrl());
-
-            String name = artifact.getDataAsString("river");
-
-            facet.addLayer(name);
-            facet.setExtent(getExtent(false));
-            facet.setOriginalExtent(getExtent(true));
-            facet.setSrid(getSrid());
-            facet.setData(getDataString());
-            facet.setFilter(getFilter());
-            facet.setGeometryType(getGeometryType());
-            facet.setConnection(MapUtils.getConnection());
-            facet.setConnectionType(MapUtils.getConnectionType());
-            facet.setLabelItem(getLabelItem());
-
-            facets.add(facet);
-
-            return null;
-        }
-
-        @Override
-        public int getRiverId() {
-            River r = FLYSUtils.getRiver(artifact);
-            int riverId = r.getId();
-
-            return riverId;
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/QSectorArtifact.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,131 +0,0 @@
-package de.intevation.flys.artifacts;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Document;
-
-import de.intevation.artifactdatabase.state.Facet;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.ArtifactFactory;
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.model.GaugeFinder;
-import de.intevation.flys.artifacts.model.GaugeFinderFactory;
-import de.intevation.flys.artifacts.model.GaugeRange;
-import de.intevation.flys.artifacts.model.NamedDouble;
-
-import de.intevation.flys.artifacts.services.FixingsKMChartService;
-
-import de.intevation.flys.artifacts.states.DefaultState;
-
-import de.intevation.flys.artifacts.resources.Resources;
-
-
-/**
- * Artifact to produce sector markers.
- */
-public class QSectorArtifact
-extends      StaticFLYSArtifact
-{
-    /** The logger for this class. */
-    private static Logger logger = Logger.getLogger(QSectorArtifact.class);
-
-    /** The name of the artifact. */
-    public static final String ARTIFACT_NAME = "qsector";
-
-
-    /**
-     * Trivial Constructor.
-     */
-    public QSectorArtifact() {
-        logger.debug("QSectorArtifact.QSectorArtifact()");
-    }
-
-
-    /**
-     * Gets called from factory, to set things up.
-     */
-    @Override
-    public void setup(
-        String          identifier,
-        ArtifactFactory factory,
-        Object          context,
-        CallMeta        callMeta,
-        Document        data)
-    {
-        logger.debug("QSectorArtifact.setup");
-        super.setup(identifier, factory, context, callMeta, data);
-        initialize(null, context, callMeta);
-    }
-
-
-    /** Return the name of this artifact. */
-    public String getName() {
-        return ARTIFACT_NAME;
-    }
-
-
-    /** Get list of NamedDouble s (QSectors). */
-    public Object getQSectors(double km, CallContext context) {
-
-        String river = getDataAsString("river");
-        List<NamedDouble> qsectors = new ArrayList<NamedDouble>();
-
-        GaugeFinderFactory ggf = GaugeFinderFactory.getInstance();
-        GaugeFinder        gf  = ggf.getGaugeFinder(river);
-
-        if (gf == null) {
-            logger.warn("No gauge finder found for river '" + river + "'");
-            return null;
-        }
-
-        GaugeRange gr = gf.find(km);
-        if (gr == null) {
-            logger.debug("No gauge range found for km "
-                + km + " on river " + river + ".");
-            return null;
-        }
-
-        if (logger.isDebugEnabled()) {
-            logger.debug(gr);
-        }
-
-        for (int i = 0; i < FixingsKMChartService.I18N_Q_SECTOR_BOARDERS.length; ++i) {
-            String key   = FixingsKMChartService.I18N_Q_SECTOR_BOARDERS[i];
-            String def   = FixingsKMChartService.DEFAULT_Q_SECTOR_BORDERS[i];
-            String label = Resources.getMsg(context.getMeta(), key, def);
-
-            qsectors.add(new NamedDouble(label, gr.getSectorBorder(i)));
-        }
-
-        return qsectors;
-    }
-
-
-    /** Setup state and facet. */
-    @Override
-    protected void initialize(Artifact artifact, Object context, CallMeta meta) {
-        logger.debug("QSectorArtifact.initialize");
-        List<Facet> fs = new ArrayList<Facet>();
-
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-        importData(flys, "river");
-
-        DefaultState state = (DefaultState) getCurrentState(context);
-        state.computeInit(this, hash(), context, meta, fs);
-        if (!fs.isEmpty()) {
-            logger.debug("Facets to add in QSectorArtifact.initialize .");
-            addFacets(getCurrentStateId(), fs);
-        }
-        else {
-            logger.debug("No facets to add in QSectorArtifact.initialize ("
-                + state.getID() + ").");
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/RiverAxisArtifact.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,185 +0,0 @@
-package de.intevation.flys.artifacts;
-
-import com.vividsolutions.jts.geom.Envelope;
-import com.vividsolutions.jts.geom.Geometry;
-
-import de.intevation.artifactdatabase.state.DefaultOutput;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.State;
-import de.intevation.artifacts.ArtifactFactory;
-import de.intevation.artifacts.CallMeta;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.RiverFactory;
-import de.intevation.flys.model.River;
-import de.intevation.flys.model.RiverAxis;
-import de.intevation.flys.utils.FLYSUtils;
-import de.intevation.flys.utils.GeometryUtils;
-
-import java.util.List;
-
-import org.apache.log4j.Logger;
-import org.w3c.dom.Document;
-
-@Deprecated
-public class RiverAxisArtifact extends WMSDBArtifact {
-
-    public static final String NAME = "riveraxis";
-
-
-    private static final Logger logger =
-        Logger.getLogger(RiverAxisArtifact.class);
-
-
-    @Override
-    public void setup(
-        String          identifier,
-        ArtifactFactory factory,
-        Object          context,
-        CallMeta        callMeta,
-        Document        data)
-    {
-        logger.debug("RiverAxisArtifact.setup");
-
-        super.setup(identifier, factory, context, callMeta, data);
-    }
-
-
-    @Override
-    public String getName() {
-        return NAME;
-    }
-
-
-    @Override
-    public State getCurrentState(Object cc) {
-        State s = new RiverAxisState(this);
-
-        List<Facet> fs = getFacets(getCurrentStateId());
-
-        DefaultOutput o = new DefaultOutput(
-            "floodmap",
-            "floodmap",
-            "image/png",
-            fs,
-            "map");
-
-        s.getOutputs().add(o);
-
-        return s;
-    }
-
-
-    public static class RiverAxisState extends WMSDBState implements FacetTypes
-    {
-        private static final Logger logger =
-            Logger.getLogger(RiverAxisState.class);
-
-        protected Geometry geom;
-        protected int      riverId;
-
-        public RiverAxisState(){}
-
-        public RiverAxisState(FLYSArtifact artifact) {
-            super(artifact);
-        }
-
-        @Override
-        protected String getFacetType() {
-            return FLOODMAP_RIVERAXIS;
-        }
-
-        @Override
-        protected String getLayer() {
-            return RiverFactory.getRiver(getRiverId()).getName();
-        }
-
-        @Override
-        protected String getUrl() {
-            return FLYSUtils.getRiverWMSUrl();
-        }
-
-        @Override
-        protected String getSrid() {
-            River river = RiverFactory.getRiver(getRiverId());
-            return FLYSUtils.getRiverSrid(river.getName());
-        }
-
-        @Override
-        protected Envelope getExtent(boolean reproject) {
-            River river = RiverFactory.getRiver(getRiverId());
-            List<RiverAxis> axes;
-
-            String kind = getIdPart(2);
-
-            if (kind != null && kind.equals("1")) {
-                axes = RiverAxis.getRiverAxis(river.getName(),
-                        Integer.parseInt(kind));
-            } else if (kind != null) {
-                axes = RiverAxis.getRiverAxis(river.getName(),
-                        getName(), Integer.parseInt(kind));
-            } else {
-                if (reproject) {
-                    logger.debug("Query extent for RiverAxis with Srid: " + getSrid());
-                    return GeometryUtils.transform(
-                            GeometryUtils.getRiverBoundary(river.getName()),
-                            getSrid());
-                } else {
-                    return GeometryUtils.transform(
-                            GeometryUtils.getRiverBoundary(river.getName()),
-                            "31467");
-                }
-            }
-
-            Envelope max = null;
-
-            for (RiverAxis ax: axes) {
-                Envelope env = ax.getGeom().getEnvelopeInternal();
-
-                if (max == null) {
-                    max = env;
-                    continue;
-                }
-
-                max.expandToInclude(env);
-            }
-
-            return max != null && reproject
-                ? GeometryUtils.transform(max, getSrid())
-                : max;
-        }
-
-        @Override
-        protected String getFilter() {
-            String kind = getIdPart(2);
-            if (kind != null && kind.equals("1")) {
-                return "river_id=" + String.valueOf(getRiverId()) +
-                    " AND kind_id=" + kind;
-            } else if (kind != null) {
-                return "river_id=" + String.valueOf(getRiverId()) +
-                    " AND kind_id=" + kind +
-                    " AND name='" + getName() + "'";
-            } else if (getIdPart(1) != null) {
-                return "river_id=" + String.valueOf(getRiverId()) +
-                    " AND name='" + getName() + "'";
-            }
-            return "river_id=" + String.valueOf(getRiverId()) +
-                " AND kind_id=" + kind;
-        }
-
-        @Override
-        protected String getDataString() {
-            if (FLYSUtils.isUsingOracle()) {
-                return "geom FROM river_axes USING SRID " + getSrid();
-            }
-            else {
-                return "geom FROM river_axes USING UNIQUE id";
-            }
-        }
-
-        @Override
-        protected String getGeometryType() {
-            return "LINE";
-        }
-    } // end of WMSKmState
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/SQRelationArtifact.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,158 +0,0 @@
-package de.intevation.flys.artifacts;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-import org.w3c.dom.Document;
-
-import de.intevation.artifactdatabase.data.DefaultStateData;
-import de.intevation.artifactdatabase.state.DefaultOutput;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifacts.ArtifactFactory;
-import de.intevation.artifacts.ArtifactNamespaceContext;
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.common.utils.XMLUtils;
-import de.intevation.flys.artifacts.model.sq.StaticSQRelation;
-import de.intevation.flys.artifacts.resources.Resources;
-import de.intevation.flys.artifacts.states.StaticState;
-import de.intevation.flys.artifacts.states.sq.SQStaticState;
-
-
-public class SQRelationArtifact
-extends AbstractStaticStateArtifact
-{
-    private static final Logger logger =
-        Logger.getLogger(SQRelationArtifact.class);
-
-    public static final String XPATH_RIVER = "/art:action/art:river/@art:name";
-    public static final String XPATH_STATION =
-        "/art:action/art:measurement_station/@art:number";
-    public static final String NAME = "staticsqrelation";
-    public static final String STATIC_STATE_NAME = "state.sqrelation.static";
-    public static final String UIPROVIDER = "static_sqrelation";
-    public static final String SQ_RELATION_OUT_A = "sq_relation_a";
-    public static final String SQ_RELATION_OUT_B = "sq_relation_b";
-    public static final String SQ_RELATION_OUT_C = "sq_relation_c";
-    public static final String SQ_RELATION_OUT_D = "sq_relation_d";
-    public static final String SQ_RELATION_OUT_E = "sq_relation_e";
-    public static final String SQ_RELATION_OUT_F = "sq_relation_f";
-
-
-    @Override
-    public void setup(
-        String          identifier,
-        ArtifactFactory factory,
-        Object          context,
-        CallMeta        callmeta,
-        Document        data
-    ) {
-        logger.debug("SQRelationArtifact.setup()");
-
-        String river = XMLUtils.xpathString(
-            data,
-            XPATH_RIVER,
-            ArtifactNamespaceContext.INSTANCE);
-        String station = XMLUtils.xpathString(
-            data,
-            XPATH_STATION,
-            ArtifactNamespaceContext.INSTANCE);
-
-        addData(
-            "river",
-            new DefaultStateData(
-                "river",
-                Resources.getMsg(callmeta, "static.sq.river", "Rivername"),
-                "String",
-                river));
-        addData(
-            "station",
-            new DefaultStateData(
-                "station",
-                Resources.getMsg(callmeta, "static.sq.station", "Station"),
-                "String",
-                station));
-        super.setup(identifier, factory, context, callmeta, data);
-    }
-
-    @Override
-    protected void initStaticState() {
-        StaticState state = new SQStaticState(STATIC_STATE_NAME);
-
-        List<Facet> fs = new ArrayList<Facet>();
-        state.staticCompute(fs, this);
-
-        if (hasParameter(StaticSQRelation.Parameter.A, fs)) {
-            DefaultOutput outputA = new DefaultOutput(
-                SQ_RELATION_OUT_A,
-                "output.static.sqrelation.a",
-                "image/png",
-                fs,
-                "chart");
-            state.addOutput(outputA);
-        }
-        if (hasParameter(StaticSQRelation.Parameter.B, fs)) {
-            DefaultOutput outputB = new DefaultOutput(
-                SQ_RELATION_OUT_B,
-                "output.static.sqrelation.b",
-                "image/png",
-                fs,
-                "chart");
-            state.addOutput(outputB);
-        }
-        if (hasParameter(StaticSQRelation.Parameter.C, fs)) {
-            DefaultOutput outputC = new DefaultOutput(
-                SQ_RELATION_OUT_C,
-                "output.static.sqrelation.c",
-                "image/png",
-                fs,
-                "chart");
-            state.addOutput(outputC);
-        }
-        if (hasParameter(StaticSQRelation.Parameter.D, fs)) {
-            DefaultOutput outputD = new DefaultOutput(
-                SQ_RELATION_OUT_D,
-                "output.static.sqrelation.d",
-                "image/png",
-                fs,
-                "chart");
-            state.addOutput(outputD);
-        }
-        if (hasParameter(StaticSQRelation.Parameter.E, fs)) {
-            DefaultOutput outputE = new DefaultOutput(
-                SQ_RELATION_OUT_E,
-                "output.static.sqrelation.e",
-                "image/png",
-                fs,
-                "chart");
-            state.addOutput(outputE);
-        }
-        if (hasParameter(StaticSQRelation.Parameter.F, fs)) {
-            DefaultOutput outputF = new DefaultOutput(
-                SQ_RELATION_OUT_F,
-                "output.static.sqrelation.f",
-                "image/png",
-                fs,
-                "chart");
-            state.addOutput(outputF);
-        }
-        addFacets(STATIC_STATE_NAME, fs);
-        state.setUIProvider(UIPROVIDER);
-        setStaticState(state);
-    }
-
-    @Override
-    public String getName() {
-        return NAME;
-    }
-
-    private boolean hasParameter(StaticSQRelation.Parameter p, List<Facet> fs) {
-        for (Facet f : fs) {
-            if (f.getName().equals("sq_" +
-                p.toString().toLowerCase() + "_curve")) {
-                return true;
-            }
-        }
-        return false;
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/StaticFLYSArtifact.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,135 +0,0 @@
-package de.intevation.flys.artifacts;
-
-import java.util.Collection;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-import de.intevation.artifacts.ArtifactNamespaceContext;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.artifactdatabase.data.StateData;
-import de.intevation.artifactdatabase.ProtocolUtils;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.Output;
-import de.intevation.artifactdatabase.state.State;
-
-import de.intevation.artifacts.common.utils.XMLUtils;
-import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
-
-/**
- * A basic FLYSArtifact.
- */
-public abstract class StaticFLYSArtifact extends FLYSArtifact {
-
-    /** Private logger. */
-    private static final Logger logger =
-        Logger.getLogger(StaticFLYSArtifact.class);
-
-    /** Path to 'ids' (data) in doc that comes from datacage. */
-    public static final String XPATH_IDS = "/art:action/art:ids/@value";
-
-    /**
-     * Create description document which includes outputmodes.
-     * @param data ignored.
-     */
-    @Override
-    public Document describe(Document data, CallContext cc) {
-        logger.debug("Describe artifact: " + identifier());
-
-        Document desc = XMLUtils.newDocument();
-
-        ElementCreator creator = new ElementCreator(
-            desc,
-            ArtifactNamespaceContext.NAMESPACE_URI,
-            ArtifactNamespaceContext.NAMESPACE_PREFIX);
-
-        Element root = ProtocolUtils.createRootNode(creator);
-        desc.appendChild(root);
-
-        Element name = ProtocolUtils.createArtNode(
-            creator, "name",
-            new String[] { "value" },
-            new String[] { getName() });
-
-        root.appendChild(name);
-
-        ProtocolUtils.appendDescribeHeader(creator, root, identifier(), hash());
-        root.appendChild(createOutputModes(cc, desc, creator));
-
-        // Add the data to an anonymous state.
-        Collection<StateData> datas = getAllData();
-        if (datas.size() > 0) {
-            Element ui = creator.create("ui");
-            Element staticE = creator.create("static");
-            Element state = creator.create("state");
-            ui.appendChild(staticE);
-            staticE.appendChild(state);
-            root.appendChild(ui);
-
-            for (StateData dataItem : datas) {
-                Element itemelent = creator.create("data");
-                creator.addAttr(itemelent, "name", dataItem.getName(), true);
-                creator.addAttr(itemelent, "type", dataItem.getType(), true);
-                state.appendChild(itemelent);
-                Element valuement = creator.create("item");
-                creator.addAttr(valuement, "label", dataItem.getDescription(), true);
-                creator.addAttr(valuement, "value", dataItem.getValue().toString(), true);
-                itemelent.appendChild(valuement);
-            }
-        }
-
-        return desc;
-    }
-
-
-    /**
-     * Return the value of id element in Datacage data document.
-     * @param data Document as passed by datacage.
-     * @return the id element value of data document.
-     */
-    public static String getDatacageIDValue(Document data) {
-        return XMLUtils.xpathString(data, XPATH_IDS,
-            ArtifactNamespaceContext.INSTANCE);
-    }
-
-
-    protected Element createOutputModes(
-        CallContext    cc,
-        Document       doc,
-        ElementCreator creator)
-    {
-        logger.debug("createOutputModes");
-
-        Element outs = ProtocolUtils.createArtNode(
-            creator, "outputmodes", null, null);
-
-        State state       = getCurrentState(cc);
-
-        logger.debug("Current state is " + state.getID());
-
-        List<Output> list = state.getOutputs();
-
-        if (list != null && list.size() > 0) {
-            List<Facet> fs = getFacets(state.getID());
-            if (fs != null && fs.size() > 0) {
-                List<Output> generated = generateOutputs(list, fs);
-
-                logger.debug("Found " + fs.size() + " current facets.");
-                if (!generated.isEmpty()) {
-                    ProtocolUtils.appendOutputModes(
-                        doc, outs, generated);
-                }
-            }
-            else {
-                logger.debug("No facets found for the current state.");
-            }
-        }
-
-        return outs;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/StaticMorphWidthArtifact.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,108 +0,0 @@
-package de.intevation.flys.artifacts;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-import org.w3c.dom.Document;
-
-import de.intevation.artifactdatabase.state.DefaultOutput;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.FacetActivity;
-import de.intevation.artifacts.ArtifactFactory;
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.common.utils.XMLUtils;
-import de.intevation.flys.artifacts.model.minfo.MorphologicWidthFacet;
-import de.intevation.flys.artifacts.resources.Resources;
-import de.intevation.flys.artifacts.states.StaticState;
-
-public class StaticMorphWidthArtifact
-extends      AbstractStaticStateArtifact
-{
-    /** The logger for this class. */
-    private static Logger logger =
-        Logger.getLogger(StaticMorphWidthArtifact.class);
-
-    private static final String NAME = "morph-width";
-    private static final String STATIC_FACET_NAME = "morph-width";
-
-    static {
-        // TODO: Move to configuration.
-        FacetActivity.Registry.getInstance()
-            .register(NAME, FacetActivity.INACTIVE);
-    }
-
-    public static final String STATIC_STATE_NAME =
-        "state.morph-width.static";
-
-    /**
-     * Trivial Constructor.
-     */
-    public StaticMorphWidthArtifact() {
-    }
-
-    @Override
-    public String getName() {
-        return NAME;
-    }
-
-    /**
-     * Gets called from factory, to set things up.
-     */
-    @Override
-    public void setup(
-        String          identifier,
-        ArtifactFactory factory,
-        Object          context,
-        CallMeta        callMeta,
-        Document        data)
-    {
-        logger.debug("setup");
-
-        if (logger.isDebugEnabled()) {
-            logger.debug(XMLUtils.toString(data));
-        }
-
-        String code = getDatacageIDValue(data);
-
-        if (code != null) {
-                Facet facet = new MorphologicWidthFacet(
-                        STATIC_FACET_NAME,
-                        Resources.getMsg(
-                            callMeta,
-                            "facet.morphologic.width",
-                            "morphologische Breite"));
-                addStringData("width_id", code);
-                ArrayList<Facet> facets = new ArrayList<Facet>(1);
-                facets.add(facet);
-
-                addFacets(STATIC_STATE_NAME, facets);
-        }
-        super.setup(identifier, factory, context, callMeta, data);
-    }
-
-    @Override
-    protected void initStaticState() {
-
-        logger.debug("initStaticState " + getName() + " " + identifier());
-
-        StaticState state = new StaticState(STATIC_STATE_NAME);
-        DefaultOutput output = new DefaultOutput(
-                "general",
-                "general",
-                "image/png",
-                "chart");
-
-        List<Facet> facets = getFacets(STATIC_STATE_NAME);
-        output.addFacets(facets);
-        state.addOutput(output);
-
-        setStaticState(state);
-    }
-
-    @Override
-    protected void initialize(Artifact artifact, Object context, CallMeta meta) {
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/StaticWKmsArtifact.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,427 +0,0 @@
-package de.intevation.flys.artifacts;
-
-import de.intevation.artifactdatabase.state.DefaultOutput;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.FacetActivity;
-import de.intevation.artifactdatabase.state.State;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.ArtifactFactory;
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.artifacts.common.utils.XMLUtils;
-
-import de.intevation.flys.artifacts.geom.Lines;
-
-import de.intevation.flys.artifacts.math.Distance;
-import de.intevation.flys.artifacts.math.Linear;
-
-import de.intevation.flys.artifacts.model.CrossSectionWaterLineFacet;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.RelativePointFacet;
-import de.intevation.flys.artifacts.model.WKms;
-import de.intevation.flys.artifacts.model.WKmsFacet;
-import de.intevation.flys.artifacts.model.WKmsFactory;
-
-import de.intevation.flys.artifacts.resources.Resources;
-
-import de.intevation.flys.artifacts.states.StaticState;
-
-import de.intevation.flys.model.FastCrossSectionLine;
-
-import java.awt.geom.Point2D;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Document;
-
-/**
- * Artifact to access additional "waterlevel"-type of data, like the height
- * of protective measures (dikes).
- *
- * This artifact neglects (Static)FLYSArtifacts capabilities of interaction
- * with the StateEngine by overriding the getState*-methods.
- */
-public class StaticWKmsArtifact
-extends      StaticFLYSArtifact
-implements   FacetTypes, WaterLineArtifact
-{
-    /** The logger for this class. */
-    private static Logger logger =
-        Logger.getLogger(StaticWKmsArtifact.class);
-
-    private static final String NAME = "staticwkms";
-
-    static {
-        // TODO: Move to configuration.
-        FacetActivity.Registry.getInstance()
-            .register(NAME, FacetActivity.INACTIVE);
-    }
-
-    public static final String STATIC_STATE_NAME =
-        "state.additional_wkms.static";
-
-    /** Data Item name to know whether we are Heighmarks and reveive
-     * some data slightly different. */
-    public static final String DATA_HEIGHT_TYPE =
-        "height_marks";
-
-    /** One and only state to be in. */
-    protected transient State state = null;
-
-
-    /**
-     * Trivial Constructor.
-     */
-    public StaticWKmsArtifact() {
-        logger.debug("StaticWKmsArtifact.StaticWKmsArtifact");
-    }
-
-    @Override
-    public String getName() {
-        return NAME;
-    }
-
-    /**
-     * Gets called from factory, to set things up.
-     */
-    @Override
-    public void setup(
-        String          identifier,
-        ArtifactFactory factory,
-        Object          context,
-        CallMeta        callMeta,
-        Document        data)
-    {
-        logger.debug("StaticWKmsArtifact.setup");
-
-        state = new StaticState(STATIC_STATE_NAME);
-
-        if (logger.isDebugEnabled()) {
-            logger.debug(XMLUtils.toString(data));
-        }
-
-        List<Facet> fs = new ArrayList<Facet>();
-        String code = getDatacageIDValue(data);
-
-        // TODO Go for JSON, one day.
-        //ex.: flood_protection-wstv-114-12
-        if (code != null) {
-            String [] parts = code.split("-");
-
-            if (parts.length >= 4) {
-                int col = -1;
-                int wst = Integer.parseInt(parts[3]);
-
-                if (!parts[2].equals("A")) {
-                    col = Integer.parseInt(parts[2]);
-                }
-
-                addStringData("col_pos", parts[2]);
-                addStringData("wst_id",  parts[3]);
-
-                String wkmsName;
-                if (col >= 0) {
-                    wkmsName = WKmsFactory.getWKmsName(col, wst);
-                }
-                else {
-                    wkmsName = WKmsFactory.getWKmsName(wst);
-                }
-
-                String name;
-                if (parts[0].equals(HEIGHTMARKS_POINTS)) {
-                    name = HEIGHTMARKS_POINTS;
-                    addStringData(DATA_HEIGHT_TYPE, "true");
-                }
-                else if (parts[0].equals("additionalsmarks")) {
-                    name = STATIC_WKMS_MARKS;
-                }
-                else {
-                    name = STATIC_WKMS;
-                }
-
-                String facetDescription = Resources.getMsg(
-                    callMeta, wkmsName, wkmsName);
-                Facet wKmsFacet = new WKmsFacet(
-                    name,
-                    facetDescription);
-                Facet csFacet = new CrossSectionWaterLineFacet(0,
-                    facetDescription);
-                Facet rpFacet = new RelativePointFacet(facetDescription);
-
-                fs.add(wKmsFacet);
-                fs.add(csFacet);
-                fs.add(rpFacet);
-                addFacets(state.getID(), fs);
-            }
-        }
-
-        spawnState();
-        super.setup(identifier, factory, context, callMeta, data);
-    }
-
-
-    /**
-     * Initialize the static state with output.
-     * @return static state
-     */
-    protected State spawnState() {
-        state = new StaticState(STATIC_STATE_NAME);
-        List<Facet> fs = getFacets(STATIC_STATE_NAME);
-        DefaultOutput output = new DefaultOutput(
-            "general",
-            "general", "image/png",
-            fs,
-            "chart");
-
-        state.getOutputs().add(output);
-        return state;
-    }
-
-
-    /**
-     * Called via setup.
-     *
-     * @param artifact The master-artifact.
-     */
-    @Override
-    protected void initialize(
-        Artifact artifact,
-        Object context,
-        CallMeta meta)
-    {
-        logger.debug("StaticWKmsArtifact.initialize");
-        FLYSArtifact winfo = (FLYSArtifact) artifact;
-        // TODO: The river is of no interest, so far.
-        addData("river", winfo.getData("river"));
-    }
-
-
-    /**
-     * 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();
-    }
-
-
-    /**
-     * Get WKms from factory.
-     * @param idx param is not needed (TODO?)
-     * @return WKms according to parameterization (can be null);
-     */
-    public WKms getWKms(int idx) {
-        logger.debug("StaticWKmsArtifact.getWKms");
-
-        return WKmsFactory.getWKms(
-            Integer.parseInt(getDataAsString("col_pos")),
-            Integer.parseInt(getDataAsString("wst_id")));
-    }
-
-
-    /**
-     * Returns W at Km of WKms, linearly interpolated.
-     * Returns -1 if not found.
-     */
-    public static double getWAtKmLin(WKms wkms, double km) {
-        // Uninformed search.
-        int size = wkms.size();
-        if (size == 0) {
-            return -1;
-        }
-        int idx = 0;
-
-        boolean kmIncreasing;
-        if (size == 1) {
-            kmIncreasing = true;
-        }
-        else {
-            kmIncreasing = (wkms.getKm(0) < wkms.getKm(wkms.size()-1))
-                ? true : false;
-        }
-        if (kmIncreasing) {
-            while (idx < size && wkms.getKm(idx) < km) {
-                idx++;
-            }
-        }
-        else {
-            idx = wkms.size() -1;
-            while (idx > 0 && wkms.getKm(idx) > km) {
-                idx--;
-            }
-        }
-
-       if (wkms.getKm(idx) == km) {
-           return wkms.getW(idx);
-       }
-
-        if (idx == size -1 || idx == 0) {
-            return -1;
-        }
-
-        // Do linear interpolation
-        int mod = kmIncreasing ? -1 : +1;
-        return Linear.linear(km, wkms.getKm(idx+mod), wkms.getKm(idx), wkms.getW(idx+mod), wkms.getW(idx));
-    }
-
-
-    /**
-     * Get the W at a specific km, only if it is closer to km than to any of
-     * the other given km.
-     * Return Double.NaN otherwise
-     *
-     * @param wkms WKms in which to search for a spatially close W value.
-     * @param km the input km, which is compared to values from wkms.
-     * @param next the next available input km (-1 if unavailable).
-     * @param prev the previous available input km (-1 if unavailable).
-     *
-     * @return W in wkms that is closer to km than to next and prev, or Double.NaN.
-     */
-    public double getWAtCloseKm(WKms wkms, double km, double next, double prev) {
-        // TODO symbolic "-1" pr next/prev is a bad idea (tm), as we compare
-        //      distances to these values later.
-        // TODO issue888
-
-        int size = wkms.size();
-        for (int i = 0; i < size; i++) {
-            double wkmsKm = wkms.getKm(i);
-            double dist = Distance.distance(wkmsKm, km);
-            if (dist == 0d) {
-                return wkms.getW(i);
-            }
-
-            // Problematic Cases:
-            // X == km , | and | == prev and next, (?) == wkmsKm
-            //
-            // Standard case:
-            // ----------|----X-----|-------
-            //     (1)    (2)    (3)   (4)
-            //
-            // With prev==-1
-            // -1 ------X-------|------
-            //    (5)      (6)     (7)
-            //
-            // With next==-1
-            //
-            // ---|-----X----- -1
-            // (8)  (9)   (10)
-
-            if (dist <= Distance.distance(wkmsKm, prev)
-                && dist <= Distance.distance(wkmsKm, next)) {
-                return wkms.getW(i);
-            }
-        }
-
-        return Double.NaN;
-    }
-
-
-    /**
-     * Returns W at Km of WKms, searching linearly.
-     * Returns -1 if not found.
-     * @param wkms the WKms object to search for given km.
-     * @param km The searched km.
-     * @return W at given km if in WKms, -1 if not found.
-     */
-    public static double getWAtKm(WKms wkms, double km) {
-        // Uninformed search, intolerant.
-        double TOLERANCE = 0.0d;
-        int size = wkms.size();
-        for (int i = 0; i < size; i++) {
-            if (Distance.within(wkms.getKm(i), km, TOLERANCE)) {
-                return wkms.getW(i);
-            }
-        }
-
-        return -1;
-    }
-
-
-    /**
-     * Get points of line describing the surface of water at cross section.
-     *
-     * @param idx Index of facet and in wkms array.
-     * @param csl FastCrossSectionLine to compute water surface agains.
-     * @param next The km of the next crosssectionline.
-     * @param prev The km of the previous crosssectionline.
-     * @param context Ignored in this implementation.
-     *
-     * @return an array holding coordinates of points of surface of water (
-     *         in the form {{x1, x2}, {y1, y2}} ).
-     */
-    @Override
-    public Lines.LineData getWaterLines(int idx, FastCrossSectionLine csl,
-        double next, double prev, CallContext context
-    ) {
-        logger.debug("getWaterLines(" + idx + ")/" + identifier());
-
-        List<Point2D> points = csl.getPoints();
-
-        WKms wkms = getWKms(0);
-
-        double km = csl.getKm();
-
-        // Find W at km.
-        double wAtKm;
-
-        // If heightmarks, only deliver if data snaps.
-        if (getDataAsString(DATA_HEIGHT_TYPE) != null &&
-            getDataAsString(DATA_HEIGHT_TYPE).equals("true")) {
-            wAtKm = getWAtCloseKm(wkms, km, next, prev);
-        }
-        else {
-            wAtKm = getWAtKm(wkms, km);
-        }
-
-        if (wAtKm == -1 || Double.isNaN(wAtKm)) {
-            logger.warn("Waterlevel at km " + km + " unknown.");
-            return new Lines.LineData(new double[][] {{}}, 0d, 0d);
-        }
-
-        return Lines.createWaterLines(points, wAtKm);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/StaticWQKmsArtifact.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,209 +0,0 @@
-package de.intevation.flys.artifacts;
-
-import java.awt.geom.Point2D;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Document;
-
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.FacetActivity;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.ArtifactFactory;
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.artifacts.common.utils.XMLUtils;
-
-import de.intevation.flys.artifacts.geom.Lines;
-
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.model.FastCrossSectionLine;
-import de.intevation.flys.artifacts.model.WKms;
-import de.intevation.flys.artifacts.model.WQKms;
-import de.intevation.flys.artifacts.model.WKmsFactory;
-import de.intevation.flys.artifacts.model.WQKmsFacet;
-import de.intevation.flys.artifacts.model.WQKmsFactory;
-
-import de.intevation.flys.artifacts.states.DefaultState;
-
-
-/**
- * Artifact to access additional "waterlevel/discharge"-type of data, like
- * fixation measurements.
- *
- * This artifact neglects (Static)FLYSArtifacts capabilities of interaction
- * with the StateEngine by overriding the getState*-methods.
- */
-public class StaticWQKmsArtifact
-extends      StaticFLYSArtifact
-implements   FacetTypes, WaterLineArtifact
-{
-    /** The logger for this class. */
-    private static Logger logger =
-        Logger.getLogger(StaticWQKmsArtifact.class);
-
-    public static final String STATIC_STATE_NAME =
-        "state.additional_wqkms.static";
-
-    private static final String NAME = "staticwqkms";
-
-    static {
-        // TODO: Move to configuration.
-        FacetActivity.Registry.getInstance()
-            .register(NAME, FacetActivity.INACTIVE);
-    }
-
-    /**
-     * Trivial Constructor.
-     */
-    public StaticWQKmsArtifact() {
-        logger.debug("StaticWQKmsArtifact.StaticWQKmsArtifact");
-    }
-
-
-    /**
-     * Gets called from factory, to set things up.
-     */
-    @Override
-    public void setup(
-        String          identifier,
-        ArtifactFactory factory,
-        Object          context,
-        CallMeta        callMeta,
-        Document        data)
-    {
-        logger.debug("StaticWQKmsArtifact.setup");
-
-        // Store the 'ids' (from datacage).
-        if (logger.isDebugEnabled()) {
-            logger.debug("StaticWQKmsArtiact.setup" + XMLUtils.toString(data));
-        }
-
-        String code = getDatacageIDValue(data);
-        addStringData("ids", code);
-        if (code != null) {
-            String [] parts = code.split("-");
-
-            if (parts.length >= 4) {
-                int col = Integer.parseInt(parts[2]);
-                int wst = Integer.parseInt(parts[3]);
-
-                addStringData("col_pos", parts[2]);
-                addStringData("wst_id",  parts[3]);
-            }
-        }
-
-        // Do this AFTER we have set the col_pos etc.
-        super.setup(identifier, factory, context, callMeta, data);
-    }
-
-
-    /**
-     * Called via setup.
-     *
-     * @param artifact The master-artifact.
-     */
-    @Override
-    protected void initialize(
-        Artifact artifact,
-        Object context,
-        CallMeta meta)
-    {
-        logger.debug("StaticWQKmsArtifact.initialize");
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-        // TODO: The river is of no interest, so far., also use importData
-        importData(flys, "river");
-
-        List<Facet> fs = new ArrayList<Facet>();
-
-        DefaultState state = (DefaultState) getCurrentState(context);
-        state.computeInit(this, hash(), context, meta, fs);
-        if (!fs.isEmpty()) {
-            logger.debug("Facets to add in StaticWQKmsArtifact.initialize .");
-            addFacets(getCurrentStateId(), fs);
-        }
-        else {
-            logger.debug("No facets to add in StaticWQKmsArtifact.initialize ("
-                + state.getID() + ").");
-        }
-    }
-
-
-    /**
-     * Get WQKms from factory.
-     * @return WQKms according to parameterization (can be null);
-     */
-    public WQKms getWQKms() {
-        logger.debug("StaticWQKmsArtifact.getWQKms");
-
-        int col = Integer.parseInt(getDataAsString("col_pos"));
-        int wst = Integer.parseInt(getDataAsString("wst_id"));
-
-        /** TODO do not run twice against db to do this. */
-        String wkmsName = WKmsFactory.getWKmsName(col, wst);
-
-        WQKms res = WQKmsFactory.getWQKms(col, wst);
-        res.setName(wkmsName);
-        return res;
-    }
-
-    /** Return specific name. */
-    @Override
-    public String getName() {
-        return NAME;
-    }
-
-
-    /**
-     * Get points of line describing the surface of water at cross section.
-     *
-     * @param idx Index of facet and in wkms array.
-     * @param csl FastCrossSectionLine to compute water surface agains.
-     * @param next The km of the next crosssectionline.
-     * @param prev The km of the previous crosssectionline.
-     * @param context Ignored in this implementation.
-     *
-     * @return an array holding coordinates of points of surface of water (
-     *         in the form {{x1, x2}, {y1, y2}} ).
-     */
-    @Override
-    public Lines.LineData getWaterLines(int idx, FastCrossSectionLine csl,
-        double next, double prev, CallContext context
-    ) {
-        logger.debug("getWaterLines(" + idx + ")/" + identifier());
-
-        List<Point2D> points = csl.getPoints();
-
-        WKms wkms = getWQKms();
-
-        double km = csl.getKm();
-
-        // Find W at km.
-        double wAtKm;
-
-        // If heightmarks, only deliver if data snaps.
-        /*
-        if (getDataAsString(DATA_HEIGHT_TYPE) != null &&
-            getDataAsString(DATA_HEIGHT_TYPE).equals("true")) {
-            wAtKm = getWAtCloseKm(wkms, km, next, prev);
-        }
-        else {
-        */
-            wAtKm = StaticWKmsArtifact.getWAtKm(wkms, km);
-        //}
-
-        if (wAtKm == -1 || Double.isNaN(wAtKm)) {
-            logger.warn("Waterlevel at km " + km + " unknown.");
-            return new Lines.LineData(new double[][] {{}}, 0d, 0d);
-        }
-
-        return Lines.createWaterLines(points, wAtKm);
-    }
-    // TODO implement deepCopy.
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1185 +0,0 @@
-package de.intevation.flys.artifacts;
-
-import de.intevation.artifactdatabase.data.StateData;
-
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.FacetActivity;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.artifacts.common.utils.StringUtils;
-
-import de.intevation.flys.artifacts.access.Calculation4Access;
-import de.intevation.flys.artifacts.access.RangeAccess;
-
-import de.intevation.flys.artifacts.geom.Lines;
-
-import de.intevation.flys.artifacts.model.Calculation1;
-import de.intevation.flys.artifacts.model.Calculation2;
-import de.intevation.flys.artifacts.model.Calculation3;
-import de.intevation.flys.artifacts.model.Calculation4;
-import de.intevation.flys.artifacts.model.Calculation5;
-import de.intevation.flys.artifacts.model.Calculation6;
-import de.intevation.flys.artifacts.model.Calculation;
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.DischargeTables;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.WQCKms;
-import de.intevation.flys.artifacts.model.WQKms;
-import de.intevation.flys.artifacts.model.WW;
-import de.intevation.flys.artifacts.model.WstValueTable;
-import de.intevation.flys.artifacts.model.WstValueTableFactory;
-
-import de.intevation.flys.artifacts.model.extreme.ExtremeResult;
-
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-import de.intevation.flys.artifacts.states.LocationDistanceSelect;
-
-import de.intevation.flys.model.DischargeTable;
-import de.intevation.flys.model.FastCrossSectionLine;
-import de.intevation.flys.model.Gauge;
-import de.intevation.flys.model.River;
-
-import de.intevation.flys.utils.DoubleUtil;
-import de.intevation.flys.utils.FLYSUtils;
-
-import gnu.trove.TDoubleArrayList;
-
-import java.awt.geom.Point2D;
-
-import java.util.Arrays;
-import java.util.Calendar;
-import java.util.GregorianCalendar;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.log4j.Logger;
-
-
-/**
- * The default WINFO artifact.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class WINFOArtifact
-extends      FLYSArtifact
-implements   FacetTypes, WaterLineArtifact {
-
-    /** The logger for this class. */
-    private static Logger logger = Logger.getLogger(WINFOArtifact.class);
-
-    /** The name of the artifact. */
-    public static final String ARTIFACT_NAME = "winfo";
-
-    /** XPath */
-    public static final String XPATH_STATIC_UI ="/art:result/art:ui/art:static";
-
-    /** The default number of steps between the start end end of a selected Q
-     * range. */
-    public static final int DEFAULT_Q_STEPS = 30;
-
-    /** The default step width between the start end end kilometer. */
-    public static final double DEFAULT_KM_STEPS = 0.1;
-
-    private static final String [] INACTIVES = new String[] {
-        LONGITUDINAL_Q,
-        DURATION_Q,
-        HISTORICAL_DISCHARGE_MAINVALUES_W,
-        HISTORICAL_DISCHARGE_MAINVALUES_Q
-    };
-
-    static {
-        // TODO: Move to configuration.
-        FacetActivity.Registry.getInstance().register(
-            ARTIFACT_NAME,
-            new FacetActivity() {
-                @Override
-                public Boolean isInitialActive(
-                    Artifact artifact,
-                    Facet    facet,
-                    String   outputName
-                ) {
-                    String fname = facet.getName();
-                    if ((fname.equals(COMPUTED_DISCHARGE_MAINVALUES_Q)
-                        || fname.equals(COMPUTED_DISCHARGE_MAINVALUES_W)
-                        || fname.equals(MAINVALUES_Q)
-                        || fname.equals(MAINVALUES_W))
-                        && outputName.equals("computed_discharge_curve"))
-                    {
-                        return Boolean.FALSE;
-                    }
-                    return !StringUtils.contains(fname, INACTIVES);
-                }
-            });
-    }
-
-    /**
-     * The default constructor.
-     */
-    public WINFOArtifact() {
-    }
-
-
-
-    /**
-     * Returns the name of the concrete artifact.
-     *
-     * @return the name of the concrete artifact.
-     */
-    @Override
-    public String getName() {
-        return ARTIFACT_NAME;
-    }
-
-    protected static boolean reportGeneratedWs(
-        Calculation report,
-        double []   ws
-    ) {
-        if (ws == null || ws.length < 2) {
-            return false;
-        }
-
-        double  lastW = ws[0];
-        boolean alreadyReported = false;
-
-        for (int i = 1; i < ws.length; ++i) {
-            if (Math.abs(lastW - ws[i]) < 1e-5) {
-                if (!alreadyReported) {
-                    alreadyReported = true;
-                    report.addProblem("more.than.one.q.for.w", ws[i]);
-                }
-            }
-            else {
-                alreadyReported = false;
-            }
-            lastW = ws[i];
-        }
-
-        return true;
-    }
-
-
-    //
-    // METHODS FOR RETRIEVING COMPUTED DATA FOR DIFFERENT CHART TYPES
-    //
-    //
-    /**
-     * Returns the data that is computed by a waterlevel computation.
-     *
-     * @return an array of data triples that consist of W, Q and Kms.
-     */
-    public CalculationResult getWaterlevelData() {
-        return this.getWaterlevelData(null);
-    }
-
-    // THIS IS FREAKY BULLSHIT! Felix, why do you call the calculation directly????
-    protected CalculationResult getDischargeLongitudinalSectionData() {
-        // XXX: THIS AN _EXPENSIVE_ CALCULATION! CACHE IT!
-        return new Calculation4(new Calculation4Access(this)).calculate();
-    }
-
-    /**
-     * Returns the data that is computed by a waterlevel computation.
-     *
-     * @return an array of data triples that consist of W, Q and Kms.
-     */
-    public CalculationResult getWaterlevelData(CallContext context)
-    {
-        logger.debug("WINFOArtifact.getWaterlevelData");
-
-        String calculationMode = getDataAsString("calculation_mode");
-
-        if (calculationMode.equals("calc.discharge.longitudinal.section")
-        ) {
-            return getDischargeLongitudinalSectionData();
-        }
-        else if (calculationMode.equals("calc.extreme.curve")) {
-            return (CalculationResult)
-                this.compute(context, ComputeType.ADVANCE, false);
-        }
-
-        River river = FLYSUtils.getRiver(this);
-        if (river == null) {
-            return error(new WQKms[0], "no.river.selected");
-        }
-
-        double[] kms = getKms();
-        if (kms == null) {
-            return error(new WQKms[0], "no.kms.selected");
-        }
-
-        double[] qs   = getQs();
-        double[] ws   = null;
-        boolean  qSel = true;
-
-        Calculation report = new Calculation();
-
-        if (qs == null) {
-            logger.debug("Determine Q values based on a set of W values.");
-            qSel = false;
-            ws   = getWs();
-            double [][] qws = getQsForWs(ws);
-            if (qws == null || qws.length == 0) {
-                return error(new WQKms[0], "converting.ws.to.qs.failed");
-            }
-            qs = qws[0];
-
-            if (reportGeneratedWs(report, qws[1])) {
-                ws = qws[1];
-            }
-        }
-
-        WstValueTable wst = WstValueTableFactory.getTable(river);
-        if (wst == null) {
-            return error(new WQKms[0], "no.wst.for.selected.river");
-        }
-
-        RangeAccess rangeAccess = new RangeAccess(this, null);
-        double [] range = rangeAccess.getKmRange();
-        if (range == null) {
-            return error(new WQKms[0], "no.range.found");
-        }
-
-        double refKm;
-
-        if (isFreeQ() || isFreeW()) {
-            refKm = range[0];
-            logger.debug("'free' calculation (km " + refKm + ")");
-        }
-        else {
-            Gauge gauge = river.determineGaugeByPosition(range[0]);
-            if (gauge == null) {
-                return error(
-                    new WQKms[0], "no.gauge.found.for.km", range[0]);
-            }
-
-            refKm = gauge.getStation().doubleValue();
-
-            logger.debug(
-                "reference gauge: " + gauge.getName() + " (km " + refKm + ")");
-        }
-
-        return computeWaterlevelData(kms, qs, ws, wst, refKm, report);
-    }
-
-
-    /**
-     * Computes the data of a waterlevel computation based on the interpolation
-     * in WstValueTable.
-     *
-     * @param kms The kilometer values.
-     * @param qs The discharge values.
-     * @param wst The WstValueTable used for the interpolation.
-     *
-     * @return an array of data triples that consist of W, Q and Kms.
-     */
-    public static CalculationResult computeWaterlevelData(
-        double []     kms,
-        double []     qs,
-        double []     ws,
-        WstValueTable wst,
-        double        refKm,
-        Calculation   report
-    ) {
-        logger.info("WINFOArtifact.computeWaterlevelData");
-
-        Calculation1 calc1 = new Calculation1(kms, qs, ws, refKm);
-
-        if (report != null) {
-            calc1.addProblems(report);
-        }
-
-        return calc1.calculate(wst);
-    }
-
-
-    /**
-     * Returns the data that is computed by a duration curve computation.
-     *
-     * @return the data computed by a duration curve computation.
-     */
-    public CalculationResult getDurationCurveData() {
-        logger.debug("WINFOArtifact.getDurationCurveData");
-
-        River r = FLYSUtils.getRiver(this);
-
-        if (r == null) {
-            return error(null, "no.river.selected");
-        }
-
-        Gauge g = getGauge();
-
-        if (g == null) {
-           return error(null, "no.gauge.selected");
-        }
-
-        RangeAccess rangeAccess = new RangeAccess(this, null);
-        double[] locations = rangeAccess.getLocations();
-
-        if (locations == null) {
-            return error(null, "no.locations.selected");
-        }
-
-        WstValueTable wst = WstValueTableFactory.getTable(r);
-        if (wst == null) {
-            return error(null, "no.wst.for.river");
-        }
-
-        return computeDurationCurveData(g, wst, locations[0]);
-    }
-
-
-    /**
-     * Computes the data used to create duration curves.
-     *
-     * @param gauge The selected gauge.
-     * @param location The selected location.
-     *
-     * @return the computed data.
-     */
-    public static CalculationResult computeDurationCurveData(
-        Gauge         gauge,
-        WstValueTable wst,
-        double        location)
-    {
-        logger.info("WINFOArtifact.computeDurationCurveData");
-
-        Object[] obj = gauge.fetchDurationCurveData();
-
-        int[]    days = (int[]) obj[0];
-        double[] qs   = (double[]) obj[1];
-
-        Calculation3 calculation = new Calculation3(location, days, qs);
-
-        return calculation.calculate(wst);
-    }
-
-
-    /**
-     * Returns the data that is computed by a discharge curve computation.
-     *
-     * @return the data computed by a discharge curve computation.
-     */
-    public CalculationResult getComputedDischargeCurveData()
-    throws NullPointerException
-    {
-        logger.debug("WINFOArtifact.getComputedDischargeCurveData");
-
-        River r = FLYSUtils.getRiver(this);
-
-        if (r == null) {
-            return error(new WQKms[0], "no.river.selected");
-        }
-
-        RangeAccess rangeAccess = new RangeAccess(this, null);
-        double[] locations = rangeAccess.getLocations();
-
-        if (locations == null) {
-            return error(new WQKms[0], "no.locations.selected");
-        }
-
-        WstValueTable wst = WstValueTableFactory.getTable(r);
-        if (wst == null) {
-            return error(new WQKms[0], "no.wst.for.river");
-        }
-
-        return computeDischargeCurveData(wst, locations[0]);
-    }
-
-
-    /**
-     * Computes the data used to create computed discharge curves.
-     *
-     * @param wst The WstValueTable that is used for the interpolation (river-
-     *            bound).
-     * @param location The location where the computation should be based on.
-     *
-     * @return an object that contains tuples of W/Q values at the specified
-     * location.
-     */
-    public static CalculationResult computeDischargeCurveData(
-        WstValueTable wst,
-        double location)
-    {
-        logger.info("WINFOArtifact.computeDischargeCurveData");
-
-        Calculation2 calculation = new Calculation2(location);
-
-        return calculation.calculate(wst);
-    }
-
-
-    /** Create CalculationResult with data and message. */
-    protected static final CalculationResult error(Object data, String msg) {
-        return new CalculationResult(data, new Calculation(msg));
-    }
-
-    /** Create CalculationResult with data and message with args. */
-    protected static final CalculationResult error(Object data, String msg, Object ... args) {
-        return new CalculationResult(data, new Calculation(msg, args));
-    }
-
-
-    /**
-     * Returns the data that is computed by a reference curve computation.
-     *
-     * @return the data computed by a reference curve computation.
-     */
-    public CalculationResult getReferenceCurveData(CallContext context) {
-
-        Double startKm = getReferenceStartKm();
-
-        if (startKm == null) {
-            return error(new WW[0], "no.reference.start.km");
-        }
-
-        double [] endKms = getReferenceEndKms();
-
-        if (endKms == null || endKms.length == 0) {
-            return error(new WW[0], "no.reference.end.kms");
-        }
-
-        Calculation5 calc5 = new Calculation5(startKm, endKms);
-
-        River r = FLYSUtils.getRiver(this);
-        if (r == null) {
-            return error(new WW[0], "no.river.found");
-        }
-
-        WstValueTable wst = WstValueTableFactory.getTable(r);
-        if (wst == null) {
-            return error(new WW[0], "no.wst.for.river");
-        }
-
-        Map<Double, Double> kms2gaugeDatums = r.queryGaugeDatumsKMs();
-
-        return calc5.calculate(wst, kms2gaugeDatums, context);
-    }
-
-
-    /** Get reference (start) km. */
-    public Double getReferenceStartKm() {
-        StateData sd = getData("reference_startpoint");
-
-        if (sd == null) {
-            logger.warn("no reference start given.");
-            return null;
-        }
-
-        logger.debug("Reference start km given: " + sd.getValue());
-
-        String input = (String) sd.getValue();
-
-        if (input == null || (input = input.trim()).length() == 0) {
-            logger.warn("reference start string is empty.");
-            return null;
-        }
-
-        try {
-            return Double.valueOf(input);
-        }
-        catch (NumberFormatException nfe) {
-            logger.warn("reference start string is not numeric.");
-        }
-
-        return null;
-    }
-
-
-    /**
-     * Get end kms for reference curve (null if none).
-     */
-    public double [] getReferenceEndKms() {
-        StateData sd = getData("reference_endpoint");
-
-        if (sd == null) {
-            logger.warn("no reference end given.");
-            return null;
-        }
-        else {
-            logger.debug("Reference end km : " + sd.getValue());
-        }
-
-        String input = (String) sd.getValue();
-
-        if (input == null || (input = input.trim()).length() == 0) {
-            logger.warn("reference end string is empty.");
-            return null;
-        }
-
-        TDoubleArrayList endKms = new TDoubleArrayList();
-
-        for (String part: input.split("\\s+")) {
-            try {
-                double km = Double.parseDouble(part);
-                if (!endKms.contains(km)) {
-                    endKms.add(km);
-                }
-            }
-            catch (NumberFormatException nfe) {
-                logger.warn("reference end string is not numeric.");
-            }
-        }
-
-        return endKms.toNativeArray();
-    }
-
-
-    public CalculationResult getHistoricalDischargeData() {
-        Gauge  gauge        = FLYSUtils.getReferenceGauge(this);
-        String rawTimerange = getDataAsString("year_range");
-        String rawValues    = getDataAsString("historical_values");
-        int    mode         = getDataAsInteger("historical_mode");
-
-        long[]   timerange = FLYSUtils.longArrayFromString(rawTimerange);
-        double[] values    = FLYSUtils.doubleArrayFromString(rawValues);
-
-        Calendar start = new GregorianCalendar();
-        start.setTimeInMillis(timerange[0]);
-        Calendar end   = new GregorianCalendar();
-        end.setTimeInMillis(timerange[1]);
-
-        Calculation6 calc = new Calculation6(
-            mode,
-            new long[] { start.getTimeInMillis(), end.getTimeInMillis() },
-            values);
-
-        return calc.calculate(gauge);
-    }
-
-
-    /**
-     * Get corrected waterline against surface/profile.
-     */
-    public Lines.LineData waterLineC(int idx, FastCrossSectionLine csl) {
-        List<Point2D> points = csl.getPoints();
-
-        WQKms[] wqckms = (WQKms[])
-            getDischargeLongitudinalSectionData().getData();
-
-        // Find index of km.
-        double wishKM = csl.getKm();
-
-        // Find W/C at km, linear naive approach.
-        WQCKms triple = (WQCKms) wqckms[idx-1];
-
-        int old_idx = 0;
-
-        if (triple.size() == 0) {
-            logger.warn("Calculation of c/waterline is empty.");
-            return Lines.createWaterLines(points, 0.0f);
-        }
-
-        // Linear seach in WQKms for closest km.
-        double old_dist_wish = Math.abs(wishKM - triple.getKm(0));
-        double last_c = triple.getC(0);
-
-        for (int i = 0, T = triple.size(); i < T; i++) {
-            double diff = Math.abs(wishKM - triple.getKm(i));
-            if (diff > old_dist_wish) {
-                break;
-            }
-            last_c = triple.getC(i);
-            old_dist_wish = diff;
-        }
-
-        return Lines.createWaterLines(points, last_c);
-    }
-
-
-    /**
-     * Get points of line describing the surface of water at cross section.
-     *
-     * @param idx Index for getWaterlevelData.
-     * @param csl The profile/surface to fill with water.
-     * @param nextIgnored Ignored in this implementation of WaterLineArtifact.
-     * @param prevIgnored Ignored in this implementation of WaterLineArtifact.
-     *
-     * @return an array holding coordinates of points of surface of water (
-     *         in the form {{x1, x2} {y1, y2}} ).
-     */
-    @Override
-    public Lines.LineData getWaterLines(int idx, FastCrossSectionLine csl,
-        double nextIgnored, double prevIgnored, CallContext context) {
-        logger.debug("getWaterLines(" + idx + ")");
-
-        List<Point2D> points = csl.getPoints();
-
-        // Need W at km
-        Object waterlevelResult = getWaterlevelData(context).getData();
-        WQKms [] wqkms;
-
-        if (waterlevelResult instanceof ExtremeResult) {
-            wqkms = ((ExtremeResult) waterlevelResult).getWQKms();
-        }
-        else {
-            wqkms = (WQKms[]) waterlevelResult;
-        }
-
-        if (wqkms.length == 0) {
-            logger.error("No WQKms found.");
-            return Lines.createWaterLines(points, 0.0f);
-        }
-
-        if (wqkms.length <= idx) {
-            logger.error("getWaterLines() requested index ("
-                         + idx + " not found.");
-            return waterLineC(idx, csl);
-        }
-
-        // Find W at km, linear naive approach.
-        WQKms triple = wqkms[idx];
-
-        // Find index of km.
-        double wishKM = csl.getKm();
-
-        if (triple.size() == 0) {
-            logger.warn("Calculation of waterline is empty.");
-            return Lines.createWaterLines(points, 0.0f);
-        }
-
-        // Early abort if we would need to extrapolate.
-        int T = triple.size();
-        double max_km = triple.getKm(T-1), min_km = triple.getKm(0);
-        if (wishKM < min_km || wishKM > max_km) {
-            // TODO Does this have to be done in the other WaterlineArtifact
-            //      implementations, too?
-            logger.warn("Will not extrapolate waterlevels.");
-            return Lines.createWaterLines(points, 0.0f);
-        }
-
-        int old_idx = 0;
-
-        // Linear seach in WQKms for closest km.
-        double old_dist_wish = Math.abs(wishKM - triple.getKm(0));
-        double last_w = triple.getW(0);
-
-        for (int i = 0; i < T; i++) {
-            double diff = Math.abs(wishKM - triple.getKm(i));
-            if (diff > old_dist_wish) {
-                break;
-            }
-            last_w = triple.getW(i);
-            old_dist_wish = diff;
-        }
-
-        return Lines.createWaterLines(points, last_w);
-    }
-
-
-    /**
-     * Returns the Qs for a number of Ws. This method makes use of
-     * DischargeTables.getQForW().
-     *
-     * @param ws An array of W values.
-     *
-     * @return an array of Q values.
-     */
-    public double [][] getQsForWs(double[] ws) {
-
-        if (ws == null) {
-            logger.error("getQsForWs: ws == null");
-            return null;
-        }
-
-        boolean debug = logger.isDebugEnabled();
-
-        if (debug) {
-            logger.debug("FLYSArtifact.getQsForWs");
-        }
-
-        River r = FLYSUtils.getRiver(this);
-        if (r == null) {
-            logger.warn("no river found");
-            return null;
-        }
-
-        RangeAccess rangeAccess = new RangeAccess(this, null);
-        double [] range = rangeAccess.getKmRange();
-        if (range == null) {
-            logger.warn("no ranges found");
-            return null;
-        }
-
-        if (isFreeW()) {
-            logger.debug("Bezugslinienverfahren I: W auf freier Strecke");
-            // The simple case of the "Bezugslinienverfahren"
-            // "W auf freier Strecke".
-            WstValueTable wst = WstValueTableFactory.getTable(r);
-            if (wst == null) {
-                logger.warn("no wst value table found");
-                return null;
-            }
-            double km = range[0];
-
-            TDoubleArrayList outQs = new TDoubleArrayList(ws.length);
-            TDoubleArrayList outWs = new TDoubleArrayList(ws.length);
-
-            boolean generatedWs = false;
-
-            for (int i = 0; i < ws.length; ++i) {
-                double w = ws[i];
-                if (debug) {
-                    logger.debug("getQsForWs: lookup Q for W: " + w);
-                }
-                // There could bemore than one Q per W.
-                double [] qs = wst.findQsForW(km, w);
-                for (int j = 0; j < qs.length; ++j) {
-                    outWs.add(ws[i]);
-                    outQs.add(qs[j]);
-                }
-                generatedWs |= qs.length != 1;
-            }
-
-            if (debug) {
-                logger.debug("getQsForWs: number of Qs: " + outQs.size());
-            }
-
-            return new double [][] {
-                outQs.toNativeArray(),
-                generatedWs ? outWs.toNativeArray() : null };
-        }
-
-        if (debug) {
-            logger.debug("range: " + Arrays.toString(range));
-        }
-
-        Gauge g = r.determineGaugeByPosition(range[0]);
-        if (g == null) {
-            logger.warn("no gauge found for km: " + range[0]);
-            return null;
-        }
-
-        if (debug) {
-            logger.debug("convert w->q with gauge '" + g.getName() + "'");
-        }
-
-        DischargeTable dt = g.fetchMasterDischargeTable();
-
-        if (dt == null) {
-            logger.warn("No master discharge table found for gauge '"
-                + g.getName() + "'");
-            return null;
-        }
-
-        double [][] values = DischargeTables.loadDischargeTableValues(dt, 1);
-
-        TDoubleArrayList wsOut = new TDoubleArrayList(ws.length);
-        TDoubleArrayList qsOut = new TDoubleArrayList(ws.length);
-
-        boolean generatedWs = false;
-
-        for (int i = 0; i < ws.length; i++) {
-            if (Double.isNaN(ws[i])) {
-                logger.warn("W is NaN: ignored");
-                continue;
-            }
-            double w = ws[i] / 100d;
-            double [] qs = DischargeTables.getQsForW(values, w);
-
-            if (qs.length == 0) {
-                logger.warn("No Qs found for W = " + ws[i]);
-            }
-            else {
-                for (double q: qs) {
-                    wsOut.add(ws[i]);
-                    qsOut.add(q * 100d);
-                }
-            }
-            generatedWs |= qs.length != 1;
-        }
-
-        return new double [][] {
-            qsOut.toNativeArray(),
-            generatedWs ? wsOut.toNativeArray() : null
-        };
-    }
-
-
-    /**
-     * Determines the selected mode of distance/range input.
-     *
-     * @return true, if the range mode is selected otherwise false.
-     */
-    public boolean isRange() {
-        StateData mode = getData("ld_mode");
-
-        if (mode == null) {
-            logger.warn("No mode location/range chosen. Defaults to range.");
-            return true;
-        }
-
-        String value = (String) mode.getValue();
-
-        return value.equals("distance");
-    }
-
-
-    /**
-     * Returns the selected distance based on a given range (from, to).
-     *
-     * @param dFrom The StateData that contains the lower value.
-     * @param dTo The StateData that contains the upper value.
-     *
-     * @return the selected distance.
-     */
-    protected double[] getDistanceByRange(StateData dFrom, StateData dTo) {
-        double from = Double.parseDouble((String) dFrom.getValue());
-        double to   = Double.parseDouble((String) dTo.getValue());
-
-        return new double[] { from, to };
-    }
-
-
-    /**
-     * Returns the selected Kms.
-     *
-     * @param distance An 2dim array with [lower, upper] values.
-     *
-     * @return the selected Kms.
-     */
-    public double[] getKms(double[] distance) {
-        StateData dStep = getData("ld_step");
-
-        if (dStep == null) {
-            logger.warn("No step width given. Cannot compute Kms.");
-            return null;
-        }
-
-        double step = Double.parseDouble((String) dStep.getValue());
-
-        // transform step from 'm' into 'km'
-        step = step / 1000;
-
-        if (step == 0d) {
-            step = DEFAULT_KM_STEPS;
-        }
-
-        return DoubleUtil.explode(distance[0], distance[1], step);
-    }
-
-
-    /**
-     * Returns the selected Kms.
-     *
-     * @return the selected kms.
-     */
-    public double[] getKms() {
-        if (isRange()) {
-            RangeAccess rangeAccess = new RangeAccess(this, null);
-            double [] distance = rangeAccess.getKmRange();
-            return getKms(distance);
-
-        }
-        else {
-            return LocationDistanceSelect.getLocations(this);
-        }
-    }
-
-
-    public double [] getFromToStep() {
-        if (!isRange()) {
-            return null;
-        }
-        RangeAccess rangeAccess = new RangeAccess(this, null);
-        double [] fromTo = rangeAccess.getKmRange();
-
-        if (fromTo == null) {
-            return null;
-        }
-
-        StateData dStep = getData("ld_step");
-        if (dStep == null) {
-            return null;
-        }
-
-        double [] result = new double[3];
-        result[0] = fromTo[0];
-        result[1] = fromTo[1];
-
-        try {
-            String step = (String)dStep.getValue();
-            result[2] = DoubleUtil.round(Double.parseDouble(step) / 1000d);
-        }
-        catch (NumberFormatException nfe) {
-            return null;
-        }
-
-        return result;
-    }
-
-
-    /**
-     * Returns the gauge based on the current distance and river.
-     *
-     * @return the gauge.
-     */
-    public Gauge getGauge() {
-        return FLYSUtils.getGauge(this);
-    }
-
-
-
-
-    /**
-     * This method returns the Q values.
-     *
-     * @return the selected Q values or null, if no Q values are selected.
-     */
-    public double[] getQs() {
-        StateData dMode      = getData("wq_isq");
-        StateData dSelection = getData("wq_isrange");
-
-        boolean isRange = dSelection != null
-            ? Boolean.valueOf((String)dSelection.getValue())
-            : false;
-
-        if (isQ()) {
-            if (!isRange) {
-                return getSingleWQValues();
-            }
-            else {
-                return getWQTriple();
-            }
-        }
-        else {
-            logger.warn("You try to get Qs, but W has been inserted.");
-            return null;
-        }
-    }
-
-
-    public boolean isQ() {
-        StateData mode = getData("wq_isq");
-        String value = (mode != null) ? (String) mode.getValue() : null;
-        return value != null ? Boolean.valueOf(value) : false;
-    }
-
-    public boolean isW() {
-        StateData mode = getData("wq_isq");
-        String value = (mode != null) ? (String) mode.getValue() : null;
-        return value != null ? !Boolean.valueOf(value) : false;
-    }
-
-    public boolean isFreeW() {
-        if(!isW()) {
-            return false;
-        }
-        StateData mode = getData("wq_isfree");
-        String value =  (mode != null) ? (String) mode.getValue() : null;
-
-        return value != null ? Boolean.valueOf(value) : false;
-    }
-
-
-    /**
-     * Returns true, if the parameter is set to compute data on a free range.
-     * Otherwise it returns false, which tells the calculation that it is bound
-     * to a gauge.
-     *
-     * @return true, if the calculation should compute on a free range otherwise
-     * false and the calculation is bound to a gauge.
-     */
-    public boolean isFreeQ() {
-        if(!isQ()) {
-            return false;
-        }
-        StateData mode  = getData("wq_isfree");
-        String    value = (mode != null) ? (String) mode.getValue() : null;
-
-        logger.debug("isFreeQ: " + value);
-
-        return value != null && Boolean.valueOf(value);
-    }
-
-
-    /**
-     * Returns the Q values based on a specified kilometer range.
-     *
-     * @param range A 2dim array with lower and upper kilometer range.
-     *
-     * @return an array of Q values.
-     */
-    public double[] getQs(double[] range) {
-        StateData dMode   = getData("wq_isq");
-
-        if (isQ()) {
-            return getWQForDist(range);
-        }
-
-        logger.warn("You try to get Qs, but Ws has been inserted.");
-        return null;
-    }
-
-
-    /**
-     * Returns the W values based on a specified kilometer range.
-     *
-     * @param range A 2dim array with lower and upper kilometer range.
-     *
-     * @return an array of W values.
-     */
-    public double[] getWs(double[] range) {
-        if (isW()) {
-            return getWQForDist(range);
-        }
-
-        logger.warn("You try to get Ws, but Qs has been inserted.");
-        return null;
-    }
-
-
-    /**
-     * This method returns the W values.
-     *
-     * @return the selected W values or null, if no W values are selected.
-     */
-    public double[] getWs() {
-        if (isW()) {
-            StateData dSingle = getData("wq_single");
-            if (dSingle != null) {
-                return getSingleWQValues();
-            }
-            else {
-                return getWQTriple();
-            }
-        }
-        else {
-            logger.warn("You try to get Ws, but Q has been inserted.");
-            return null;
-        }
-    }
-
-    /**
-     * This method returns the given W or Q values for a specific range
-     * (inserted in the WQ input panel for discharge longitudinal sections).
-     *
-     * @param dist A 2dim array with lower und upper kilometer values.
-     *
-     * @return an array of W or Q values.
-     */
-    protected double[] getWQForDist(double[] dist) {
-        logger.debug("Search wq values for range: " + dist[0] + " - " + dist[1]);
-        StateData data = getData("wq_values");
-
-        if (data == null) {
-            logger.warn("Missing wq values!");
-            return null;
-        }
-
-        String dataString = (String) data.getValue();
-        String[]   ranges = dataString.split(":");
-
-        for (String range: ranges) {
-            String[] parts = range.split(";");
-
-            double lower = Double.parseDouble(parts[0]);
-            double upper = Double.parseDouble(parts[1]);
-
-            if (lower <= dist[0] && upper >= dist[1]) {
-                String[] values = parts[2].split(",");
-
-                int      num = values.length;
-                double[] res = new double[num];
-
-                for (int i = 0; i < num; i++) {
-                    try {
-                        res[i] = Double.parseDouble(values[i]);
-                    }
-                    catch (NumberFormatException nfe) {
-                        logger.warn(nfe, nfe);
-                    }
-                }
-
-                return res;
-            }
-        }
-
-        logger.warn("Specified range for WQ not found!");
-
-        return null;
-    }
-
-
-    /**
-     * This method returns an array of inserted WQ triples that consist of from,
-     * to and the step width.
-     *
-     * @return an array of from, to and step width.
-     */
-    protected double[] getWQTriple() {
-        StateData dFrom = getData("wq_from");
-        StateData dTo   = getData("wq_to");
-
-        if (dFrom == null || dTo == null) {
-            logger.warn("Missing start or end value for range.");
-            return null;
-        }
-
-        double from = Double.parseDouble((String) dFrom.getValue());
-        double to   = Double.parseDouble((String) dTo.getValue());
-
-        StateData dStep = getData("wq_step");
-
-        if (dStep == null) {
-            logger.warn("No step width given. Cannot compute Qs.");
-            return null;
-        }
-
-        double step  = Double.parseDouble((String) dStep.getValue());
-
-        // if no width is given, the DEFAULT_Q_STEPS is used to compute the step
-        // width. Maybe, we should round the value to a number of digits.
-        if (step == 0d) {
-            double diff = to - from;
-            step = diff / DEFAULT_Q_STEPS;
-        }
-
-        return DoubleUtil.explode(from, to, step);
-    }
-
-
-    /**
-     * Returns an array of inserted WQ double values stored as whitespace
-     * separated list.
-     *
-     * @return an array of W or Q values.
-     */
-    protected double[] getSingleWQValues() {
-        StateData dSingle = getData("wq_single");
-
-        if (dSingle == null) {
-            logger.warn("Cannot determine single WQ values. No data given.");
-            return null;
-        }
-
-        String   tmp       = (String) dSingle.getValue();
-        String[] strValues = tmp.split(" ");
-
-        TDoubleArrayList values = new TDoubleArrayList();
-
-        for (String strValue: strValues) {
-            try {
-                values.add(Double.parseDouble(strValue));
-            }
-            catch (NumberFormatException nfe) {
-                logger.warn(nfe, nfe);
-            }
-        }
-
-        values.sort();
-
-        return values.toNativeArray();
-    }
-
-    /**
-     * Returns the WstValueTable of current river.
-     */
-    public WstValueTable getWstValueTable() {
-        River r = FLYSUtils.getRiver(this);
-        return WstValueTableFactory.getTable(r);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/WMSBackgroundArtifact.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,48 +0,0 @@
-package de.intevation.flys.artifacts;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.artifactdatabase.state.Facet;
-
-import de.intevation.flys.artifacts.states.DefaultState;
-
-
-public class WMSBackgroundArtifact extends StaticFLYSArtifact {
-
-    public static final String NAME = "wmsbackground";
-
-    private static final Logger logger =
-        Logger.getLogger(WMSBackgroundArtifact.class);
-
-
-    @Override
-    public String getName() {
-        return NAME;
-    }
-
-
-    @Override
-    protected void initialize(Artifact artifact, Object context, CallMeta meta) {
-        logger.debug("Initialize internal state with: "+ artifact.identifier());
-
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-        addData("river", flys.getData("river"));
-
-        List<Facet> fs = new ArrayList<Facet>();
-
-        // TODO Add CallMeta
-        DefaultState state = (DefaultState) getCurrentState(context);
-        state.computeInit(this, hash(), context, meta, fs);
-
-        if (!fs.isEmpty()) {
-            addFacets(getCurrentStateId(), fs);
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/WMSBuildingsArtifact.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,165 +0,0 @@
-package de.intevation.flys.artifacts;
-
-import java.util.List;
-
-import org.w3c.dom.Document;
-
-import org.apache.log4j.Logger;
-
-import com.vividsolutions.jts.geom.Envelope;
-
-import de.intevation.artifacts.ArtifactFactory;
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.artifactdatabase.state.DefaultOutput;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.State;
-
-import de.intevation.flys.model.River;
-import de.intevation.flys.model.Building;
-
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.RiverFactory;
-import de.intevation.flys.utils.FLYSUtils;
-import de.intevation.flys.utils.GeometryUtils;
-
-
-public class WMSBuildingsArtifact extends WMSDBArtifact {
-
-    public static final String NAME = "buildings";
-
-
-    private static final Logger logger =
-        Logger.getLogger(WMSBuildingsArtifact.class);
-
-
-    @Override
-    public void setup(
-        String          identifier,
-        ArtifactFactory factory,
-        Object          context,
-        CallMeta        callMeta,
-        Document        data)
-    {
-        logger.debug("WMSBuildingsArtifact.setup");
-
-        super.setup(identifier, factory, context, callMeta, data);
-    }
-
-
-    @Override
-    public String getName() {
-        return NAME;
-    }
-
-
-    @Override
-    public State getCurrentState(Object cc) {
-        State s = new BuildingsState(this);
-
-        List<Facet> fs = getFacets(getCurrentStateId());
-
-        DefaultOutput o = new DefaultOutput(
-            "floodmap",
-            "floodmap",
-            "image/png",
-            fs,
-            "map");
-
-        s.getOutputs().add(o);
-
-        return s;
-    }
-
-
-    public static class BuildingsState extends WMSDBState implements FacetTypes
-    {
-        private static final Logger logger =
-            Logger.getLogger(BuildingsState.class);
-
-        protected int riverId;
-
-        public BuildingsState(WMSDBArtifact artifact) {
-            super(artifact);
-            riverId = 0;
-        }
-
-        @Override
-        protected String getFacetType() {
-            return FLOODMAP_BUILDINGS;
-        }
-
-        @Override
-        protected String getUrl() {
-            return FLYSUtils.getUserWMSUrl(artifact.identifier());
-        }
-
-        @Override
-        protected String getSrid() {
-            River river = RiverFactory.getRiver(getRiverId());
-            return FLYSUtils.getRiverSrid(river.getName());
-        }
-
-        @Override
-        protected Envelope getExtent(boolean reproject) {
-            List<Building> buildings;
-            String kind = getIdPart(2);
-
-            if (kind != null) {
-                buildings = Building.getBuildings(getRiverId(), Integer.parseInt(kind));
-            } else {
-                buildings = Building.getBuildings(getRiverId(), name);
-            }
-
-            Envelope max = null;
-
-            for (Building b: buildings) {
-                Envelope env = b.getGeom().getEnvelopeInternal();
-
-                if (max == null) {
-                    max = env;
-                    continue;
-                }
-
-                max.expandToInclude(env);
-            }
-
-            return max != null && reproject
-                ? GeometryUtils.transform(max, getSrid())
-                : max;
-        }
-
-        @Override
-        protected String getFilter() {
-            // The expected id string is:
-            // river_id;layer-name;kind
-
-            String kind = getIdPart(2);
-            if (kind != null) {
-                // If kind is provided we filter by kind
-                return "river_id=" + String.valueOf(getRiverId()) +
-                    " AND kind_id=" + kind;
-            }
-            return "river_id=" + String.valueOf(getRiverId()) +
-                " AND name='" + getName() + "'";
-        }
-
-        @Override
-        protected String getDataString() {
-            String srid = getSrid();
-
-            if (FLYSUtils.isUsingOracle()) {
-                return "geom FROM buildings USING SRID " + srid;
-            }
-            else {
-                return "geom FROM buildings USING UNIQUE id USING SRID " + srid;
-            }
-        }
-
-        @Override
-        protected String getGeometryType() {
-            return "LINE";
-        }
-    } // end of WMSKmState
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/WMSDBArtifact.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,281 +0,0 @@
-package de.intevation.flys.artifacts;
-
-import com.vividsolutions.jts.geom.Envelope;
-
-import de.intevation.artifactdatabase.data.DefaultStateData;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.State;
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.ArtifactFactory;
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.common.utils.FileTools;
-import de.intevation.flys.artifacts.model.map.WMSDBLayerFacet;
-import de.intevation.flys.artifacts.resources.Resources;
-import de.intevation.flys.artifacts.states.DefaultState;
-import de.intevation.flys.utils.FLYSUtils;
-import de.intevation.flys.utils.MapUtils;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-import org.w3c.dom.Document;
-
-
-public abstract class WMSDBArtifact extends StaticFLYSArtifact {
-
-    private static final Logger logger = Logger.getLogger(WMSDBArtifact.class);
-
-    @Override
-    public void setup(
-        String          identifier,
-        ArtifactFactory factory,
-        Object          context,
-        CallMeta        callMeta,
-        Document        data)
-    {
-        logger.debug("WMSDBArtifact.setup");
-
-        super.setup(identifier, factory, context, callMeta, data);
-
-        String ids = getDatacageIDValue(data);
-
-        if (ids != null && ids.length() > 0) {
-            addData("ids", new DefaultStateData("ids", null, null, ids));
-        }
-        else {
-            throw new IllegalArgumentException("No attribute 'ids' found!");
-        }
-
-        List<Facet> fs = new ArrayList<Facet>();
-
-        WMSDBState state = (WMSDBState) getCurrentState(context);
-        state.computeInit(this, hash(), context, callMeta, fs);
-
-        if (!fs.isEmpty()) {
-            addFacets(getCurrentStateId(), fs);
-        }
-    }
-
-
-    @Override
-    protected void initialize(
-        Artifact artifact,
-        Object   context,
-        CallMeta callMeta)
-    {
-        // do nothing
-    }
-
-
-    @Override
-    protected State getState(Object context, String stateID) {
-        return getCurrentState(context);
-    }
-
-
-    /**
-     * 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(getCurrentState(context));
-
-        return states;
-    }
-
-
-
-    public static abstract class WMSDBState extends DefaultState {
-        private static final Logger logger = Logger.getLogger(WMSDBState.class);
-
-        protected FLYSArtifact artifact;
-
-        protected String name;
-        protected int    riverId;
-
-
-        public WMSDBState() {}
-
-        public WMSDBState(FLYSArtifact artifact) {
-            this.artifact = artifact;
-            this.name     = null;
-            this.riverId  = 0;
-        }
-
-        @Override
-        public Object computeInit(
-            FLYSArtifact artifact,
-            String       hash,
-            Object       context,
-            CallMeta     meta,
-            List<Facet>  facets
-        ) {
-            logger.debug("WMSDBState.computeInit");
-
-            String type = getFacetType();
-
-            WMSDBLayerFacet facet = new WMSDBLayerFacet(
-                0,
-                type,
-                getTitle(meta),
-                ComputeType.INIT,
-                getID(), hash,
-                getUrl());
-
-            facet.addLayer(getLayer());
-            facet.setExtent(getExtent());
-            facet.setOriginalExtent(getExtent(true));
-            facet.setSrid(getSrid());
-            facet.setData(getDataString());
-            facet.setFilter(getFilter());
-            facet.setGeometryType(getGeometryType());
-            facet.setConnection(MapUtils.getConnection());
-            facet.setConnectionType(MapUtils.getConnectionType());
-            facet.setLabelItem(getLabelItem());
-
-            facets.add(facet);
-
-            return null;
-        }
-
-        protected String getLabelItem() {
-            return null;
-        }
-
-        public int getRiverId() {
-            if (riverId == 0) {
-                String rid = getIdPart(0);
-
-                try {
-                    riverId = Integer.parseInt(rid);
-                }
-                catch (NumberFormatException nfe) {
-                    logger.error("Cannot parse river id from '" +
-                            artifact.getDataAsString("ids") + "'");
-                }
-            }
-            return riverId;
-        }
-
-        protected String getLayer() {
-            String type = getFacetType();
-            String name = type + "-" + artifact.identifier();
-            return name;
-        }
-
-        /**
-         * Returns the name of the WMS layer. This method extracts the name
-         * from 'ids' data string. It is expected, that the 'ids' string is
-         * seperated by ';' and that the name is placed at index 1.
-         *
-         * @return the name of the WMS layer.
-         */
-        public String getName() {
-            if (name == null) {
-                name = getIdPart(1);
-            }
-
-            return name;
-        }
-
-        /**
-         * Returns a part of the ID string. This method splits the
-         * 'ids' data string. It is expected, that the 'ids' string is
-         * seperated by ';'.
-         *
-         * @param number the position of the id data string
-         *
-         * @return the part of the id string at position number.
-         *         Null if number was out of bounds.
-         */
-        public String getIdPart(int number) {
-            String ids = artifact.getDataAsString("ids");
-
-            String parts[] = ids != null ? ids.split(";") : null;
-
-            if (parts != null && parts.length >= number + 1) {
-                return parts[number];
-            }
-            return null;
-        }
-
-
-        /**
-         * Returns the name of the layer (returned by getName()) or the layer
-         * type if the name is empty. The layer type is created by an i18n
-         * string of getFacetType().
-         *
-         * @param meta A CallMeta used for i18n.
-         *
-         * @return the name of the layer or its type if name is empty.
-         */
-        protected String getTitle(CallMeta meta) {
-            String name = getName();
-
-            return name != null && name.length() > 0
-                ? name
-                : Resources.getMsg(
-                    meta,
-                    getFacetType(),
-                    getFacetType());
-        }
-
-
-        @Override
-        public void endOfLife(Artifact owner, Object context) {
-            logger.info("Destroy WMSDBState: " + getID());
-
-            String p = FLYSUtils.getXPathString(FLYSUtils.XPATH_FLOODMAP_SHAPEFILE_DIR);
-            File dir = new File(p, owner.identifier());
-
-            if (dir != null && dir.exists()) {
-                logger.debug("Try to delete directory '" + dir + "'");
-
-                FileTools.deleteRecursive(dir);
-            }
-        }
-
-        /**
-         * This method returns the extent of a DB layer in the projection of the
-         * database.
-         *
-         * @return the extent of the DB layer in the projection of the database.
-         */
-        protected Envelope getExtent() {
-            return getExtent(false);
-        }
-
-
-        protected abstract String getFacetType();
-
-        protected abstract String getUrl();
-
-        protected abstract String getSrid();
-
-        /**
-         * This method returns the extent of the DB layer. The projection of the
-         * extent depends on the <i>reproject</i> parameter. If reproject is set,
-         * the extent is reprojected into the original projection which is
-         * specified in the configuration. Otherwise, the projection of the
-         * database is used.
-         *
-         * @param reproject True, to reproject the extent into the projection
-         * specified in the configuration.
-         *
-         * @return the extent of the database layer.
-         */
-        protected abstract Envelope getExtent(boolean reproject);
-
-        protected abstract String getFilter();
-
-        protected abstract String getDataString();
-
-        protected abstract String getGeometryType();
-    } // end of WMSDBState
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/WMSFixpointsArtifact.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,148 +0,0 @@
-package de.intevation.flys.artifacts;
-
-import java.util.List;
-
-import org.w3c.dom.Document;
-
-import org.apache.log4j.Logger;
-
-import com.vividsolutions.jts.geom.Envelope;
-
-import de.intevation.artifacts.ArtifactFactory;
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.artifactdatabase.state.DefaultOutput;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.State;
-
-import de.intevation.flys.model.River;
-import de.intevation.flys.model.Fixpoint;
-
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.RiverFactory;
-import de.intevation.flys.utils.FLYSUtils;
-import de.intevation.flys.utils.GeometryUtils;
-
-
-public class WMSFixpointsArtifact extends WMSDBArtifact {
-
-    public static final String NAME = "fixpoints";
-
-
-    private static final Logger logger =
-        Logger.getLogger(WMSFixpointsArtifact.class);
-
-
-    @Override
-    public void setup(
-        String          identifier,
-        ArtifactFactory factory,
-        Object          context,
-        CallMeta        callMeta,
-        Document        data)
-    {
-        logger.debug("WMSFixpointsArtifact.setup");
-
-        super.setup(identifier, factory, context, callMeta, data);
-    }
-
-
-    @Override
-    public String getName() {
-        return NAME;
-    }
-
-
-    @Override
-    public State getCurrentState(Object cc) {
-        State s = new FixpointsState(this);
-
-        List<Facet> fs = getFacets(getCurrentStateId());
-
-        DefaultOutput o = new DefaultOutput(
-            "floodmap",
-            "floodmap",
-            "image/png",
-            fs,
-            "map");
-
-        s.getOutputs().add(o);
-
-        return s;
-    }
-
-
-    public static class FixpointsState extends WMSDBState implements FacetTypes
-    {
-        private static final Logger logger =
-            Logger.getLogger(FixpointsState.class);
-
-        protected int riverId;
-
-        public FixpointsState(WMSDBArtifact artifact) {
-            super(artifact);
-            riverId = 0;
-        }
-
-        @Override
-        protected String getFacetType() {
-            return FLOODMAP_FIXPOINTS;
-        }
-
-        @Override
-        protected String getUrl() {
-            return FLYSUtils.getUserWMSUrl(artifact.identifier());
-        }
-
-        @Override
-        protected String getSrid() {
-            River river = RiverFactory.getRiver(getRiverId());
-            return FLYSUtils.getRiverSrid(river.getName());
-        }
-
-        @Override
-        protected Envelope getExtent(boolean reproject) {
-            List<Fixpoint> fixpoints = Fixpoint.getFixpoints(getRiverId());
-
-            Envelope max = null;
-
-            for (Fixpoint f: fixpoints) {
-                Envelope env = f.getGeom().getEnvelopeInternal();
-
-                if (max == null) {
-                    max = env;
-                    continue;
-                }
-
-                max.expandToInclude(env);
-            }
-
-            return max != null && reproject
-                ? GeometryUtils.transform(max, getSrid())
-                : max;
-        }
-
-        @Override
-        protected String getFilter() {
-            return "river_id=" + String.valueOf(getRiverId());
-        }
-
-        @Override
-        protected String getDataString() {
-            String srid = getSrid();
-
-            if (FLYSUtils.isUsingOracle()) {
-                return "geom FROM fixpoints USING SRID " + srid;
-            }
-            else {
-                return "geom FROM fixpoints USING UNIQUE id USING SRID " + srid;
-            }
-        }
-
-        @Override
-        protected String getGeometryType() {
-            return "POINT";
-        }
-    } // end of WMSKmState
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/WMSFloodmapsArtifact.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,150 +0,0 @@
-package de.intevation.flys.artifacts;
-
-import java.util.List;
-
-import org.w3c.dom.Document;
-
-import org.apache.log4j.Logger;
-
-import com.vividsolutions.jts.geom.Envelope;
-
-import de.intevation.artifacts.ArtifactFactory;
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.artifactdatabase.state.DefaultOutput;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.State;
-
-import de.intevation.flys.model.River;
-import de.intevation.flys.model.Floodmaps;
-
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.RiverFactory;
-import de.intevation.flys.utils.FLYSUtils;
-import de.intevation.flys.utils.GeometryUtils;
-
-
-public class WMSFloodmapsArtifact extends WMSDBArtifact {
-
-    public static final String NAME = "floodmaps";
-
-
-    private static final Logger logger =
-        Logger.getLogger(WMSFloodmapsArtifact.class);
-
-
-    @Override
-    public void setup(
-        String          identifier,
-        ArtifactFactory factory,
-        Object          context,
-        CallMeta        callMeta,
-        Document        data)
-    {
-        logger.debug("WMSFloodmapsArtifact.setup");
-
-        super.setup(identifier, factory, context, callMeta, data);
-    }
-
-
-    @Override
-    public String getName() {
-        return NAME;
-    }
-
-
-    @Override
-    public State getCurrentState(Object cc) {
-        State s = new FloodmapsState(this);
-
-        List<Facet> fs = getFacets(getCurrentStateId());
-
-        DefaultOutput o = new DefaultOutput(
-            "floodmap",
-            "floodmap",
-            "image/png",
-            fs,
-            "map");
-
-        s.getOutputs().add(o);
-
-        return s;
-    }
-
-
-    public static class FloodmapsState extends WMSDBState implements FacetTypes
-    {
-        private static final Logger logger =
-            Logger.getLogger(FloodmapsState.class);
-
-        protected int    riverId;
-
-        public FloodmapsState(WMSDBArtifact artifact) {
-            super(artifact);
-            riverId      = 0;
-        }
-
-        @Override
-        protected String getFacetType() {
-            return FLOODMAP_FLOODMAPS;
-        }
-
-        @Override
-        protected String getUrl() {
-            return FLYSUtils.getUserWMSUrl(artifact.identifier());
-        }
-
-        @Override
-        protected String getSrid() {
-            River river = RiverFactory.getRiver(getRiverId());
-            return FLYSUtils.getRiverSrid(river.getName());
-        }
-
-        @Override
-        protected Envelope getExtent(boolean reproject) {
-            List<Floodmaps> floodmaps =
-                Floodmaps.getFloodmaps(getRiverId(), getName());
-
-            Envelope max = null;
-
-            for (Floodmaps f: floodmaps) {
-                Envelope env = f.getGeom().getEnvelopeInternal();
-
-                if (max == null) {
-                    max = env;
-                    continue;
-                }
-
-                max.expandToInclude(env);
-            }
-
-            return max != null && reproject
-                ? GeometryUtils.transform(max, getSrid())
-                : max;
-        }
-
-        @Override
-        protected String getFilter() {
-            return "river_id=" + String.valueOf(getRiverId())
-                + " AND name='" + getName() + "'";
-        }
-
-        @Override
-        protected String getDataString() {
-            String srid = getSrid();
-
-            if (FLYSUtils.isUsingOracle()) {
-                return "geom FROM floodmaps USING SRID " + srid;
-            }
-            else {
-                return "geom FROM floodmaps USING UNIQUE id USING SRID " + srid;
-            }
-        }
-
-        @Override
-        protected String getGeometryType() {
-            return "POLYGON";
-        }
-    } // end of WMSKmState
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/WMSFloodmarksArtifact.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,159 +0,0 @@
-package de.intevation.flys.artifacts;
-
-import java.util.List;
-
-import org.w3c.dom.Document;
-
-import org.apache.log4j.Logger;
-
-import com.vividsolutions.jts.geom.Envelope;
-
-import de.intevation.artifacts.ArtifactFactory;
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.artifactdatabase.state.DefaultOutput;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.State;
-
-import de.intevation.flys.model.River;
-import de.intevation.flys.model.Floodmark;
-
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.RiverFactory;
-import de.intevation.flys.utils.FLYSUtils;
-import de.intevation.flys.utils.GeometryUtils;
-
-
-public class WMSFloodmarksArtifact extends WMSDBArtifact {
-
-    public static final String NAME = "floodmarks";
-
-
-    private static final Logger logger =
-        Logger.getLogger(WMSFloodmarksArtifact.class);
-
-
-    @Override
-    public void setup(
-        String          identifier,
-        ArtifactFactory factory,
-        Object          context,
-        CallMeta        callMeta,
-        Document        data)
-    {
-        logger.debug("WMSFloodmarksArtifact.setup");
-
-        super.setup(identifier, factory, context, callMeta, data);
-    }
-
-
-    @Override
-    public String getName() {
-        return NAME;
-    }
-
-
-    @Override
-    public State getCurrentState(Object cc) {
-        State s = new FloodmarksState(this);
-
-        List<Facet> fs = getFacets(getCurrentStateId());
-
-        DefaultOutput o = new DefaultOutput(
-            "floodmap",
-            "floodmap",
-            "image/png",
-            fs,
-            "map");
-
-        s.getOutputs().add(o);
-
-        return s;
-    }
-
-
-    public static class FloodmarksState extends WMSDBState implements FacetTypes
-    {
-        private static final Logger logger =
-            Logger.getLogger(FloodmarksState.class);
-
-        protected int riverId;
-
-        public FloodmarksState(WMSDBArtifact artifact) {
-            super(artifact);
-        }
-
-        @Override
-        protected String getFacetType() {
-            return FLOODMAP_FLOODMARKS;
-        }
-
-        @Override
-        protected String getUrl() {
-            return FLYSUtils.getUserWMSUrl(artifact.identifier());
-        }
-
-        @Override
-        protected String getSrid() {
-            River river = RiverFactory.getRiver(getRiverId());
-            return FLYSUtils.getRiverSrid(river.getName());
-        }
-
-        @Override
-        protected Envelope getExtent(boolean reproject) {
-            String year = getIdPart(2);
-            List<Floodmark> floodmarks;
-            if (year != null) {
-                floodmarks = Floodmark.getFloodmarks(getRiverId(),
-                        Integer.parseInt(year));
-            } else {
-                floodmarks = Floodmark.getFloodmarks(getRiverId());
-            }
-
-            Envelope max = null;
-
-            for (Floodmark f: floodmarks) {
-                Envelope env = f.getGeom().getEnvelopeInternal();
-
-                if (max == null) {
-                    max = env;
-                    continue;
-                }
-
-                max.expandToInclude(env);
-            }
-
-            return max != null && reproject
-                ? GeometryUtils.transform(max, getSrid())
-                : max;
-        }
-
-        @Override
-        protected String getFilter() {
-            String year = getIdPart(2);
-            if (year != null) {
-                return "river_id=" + String.valueOf(getRiverId()) +
-                    " AND year=" + getIdPart(2);
-            }
-            return "river_id=" + String.valueOf(getRiverId());
-        }
-
-        @Override
-        protected String getDataString() {
-            String srid = getSrid();
-
-            if (FLYSUtils.isUsingOracle()) {
-                return "geom FROM flood_marks USING SRID " + srid;
-            }
-            else {
-                return "geom FROM flood_marks USING UNIQUE id USING SRID " + srid;
-            }
-        }
-
-        @Override
-        protected String getGeometryType() {
-            return "POINT";
-        }
-    } // end of WMSKmState
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/WMSFloodplainArtifact.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,169 +0,0 @@
-package de.intevation.flys.artifacts;
-
-import java.util.List;
-
-import org.w3c.dom.Document;
-
-import org.apache.log4j.Logger;
-
-import com.vividsolutions.jts.geom.Envelope;
-
-import de.intevation.artifacts.ArtifactFactory;
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.artifactdatabase.state.DefaultOutput;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.State;
-
-import de.intevation.flys.model.Floodplain;
-import de.intevation.flys.model.River;
-
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.RiverFactory;
-import de.intevation.flys.utils.FLYSUtils;
-import de.intevation.flys.utils.GeometryUtils;
-
-
-public class WMSFloodplainArtifact extends WMSDBArtifact {
-
-    public static final String NAME = "floodplain";
-
-
-    private static final Logger logger =
-        Logger.getLogger(WMSFloodplainArtifact.class);
-
-
-    @Override
-    public void setup(
-        String          identifier,
-        ArtifactFactory factory,
-        Object          context,
-        CallMeta        callMeta,
-        Document        data)
-    {
-        logger.debug("WMSFloodplainArtifact.setup");
-
-        super.setup(identifier, factory, context, callMeta, data);
-    }
-
-
-    @Override
-    public String getName() {
-        return NAME;
-    }
-
-
-    @Override
-    public State getCurrentState(Object cc) {
-        State s = new FloodplainState(this);
-
-        List<Facet> fs = getFacets(getCurrentStateId());
-
-        DefaultOutput o = new DefaultOutput(
-            "floodmap",
-            "floodmap",
-            "image/png",
-            fs,
-            "map");
-
-        s.getOutputs().add(o);
-
-        return s;
-    }
-
-
-    public static class FloodplainState extends WMSDBState implements FacetTypes
-    {
-        private static final Logger logger =
-            Logger.getLogger(FloodplainState.class);
-
-        protected int riverId;
-
-        public FloodplainState(WMSDBArtifact artifact) {
-            super(artifact);
-            riverId = 0;
-        }
-
-        protected River getRiver() {
-            return RiverFactory.getRiver(getRiverId());
-        }
-
-        @Override
-        protected String getFacetType() {
-            return FLOODMAP_FLOODPLAIN;
-        }
-
-        @Override
-        protected String getUrl() {
-            return FLYSUtils.getUserWMSUrl(artifact.identifier());
-        }
-
-        @Override
-        protected String getSrid() {
-            River river = getRiver();
-            return FLYSUtils.getRiverSrid(river.getName());
-        }
-
-        @Override
-        protected Envelope getExtent(boolean reproject) {
-            River river = getRiver();
-            List<Floodplain> fps;
-
-            String kind = getIdPart(2);
-
-            if (kind != null && ! kind.equals("1")) {
-                fps = Floodplain.getFloodplains(river.getName(),
-                        getName(), Integer.parseInt(kind));
-            } else {
-                fps = Floodplain.getFloodplains(river.getName(), 1);
-            }
-
-            Envelope max = null;
-
-            for (Floodplain fp: fps) {
-                Envelope env = fp.getGeom().getEnvelopeInternal();
-
-                if (max == null) {
-                    max = env;
-                    continue;
-                }
-
-                max.expandToInclude(env);
-            }
-
-            return max != null && reproject
-                ? GeometryUtils.transform(max, getSrid())
-                : max;
-        }
-
-        @Override
-        protected String getFilter() {
-            String kind = getIdPart(2);
-            if (kind != null && ! kind.equals("1")) {
-                return "river_id=" + String.valueOf(getRiverId()) +
-                    " AND kind_id=" + kind +
-                    " AND name='" + getName() + "'";
-            }
-            return "river_id=" + String.valueOf(getRiverId()) +
-                    " AND kind_id=1";
-        }
-
-        @Override
-        protected String getDataString() {
-            String srid = getSrid();
-
-            if (FLYSUtils.isUsingOracle()) {
-                return "geom FROM floodplain USING SRID " + srid;
-            }
-            else {
-                return "geom FROM floodplain USING UNIQUE id USING SRID " +srid;
-            }
-        }
-
-        @Override
-        protected String getGeometryType() {
-            return "POLYGON";
-        }
-    } // end of WMSKmState
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/WMSGaugeLocationArtifact.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,153 +0,0 @@
-package de.intevation.flys.artifacts;
-
-import java.util.List;
-
-import org.w3c.dom.Document;
-
-import org.apache.log4j.Logger;
-
-import com.vividsolutions.jts.geom.Envelope;
-import com.vividsolutions.jts.geom.Geometry;
-
-import de.intevation.artifacts.ArtifactFactory;
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.DefaultOutput;
-import de.intevation.artifactdatabase.state.State;
-
-import de.intevation.flys.model.River;
-import de.intevation.flys.model.GaugeLocation;
-
-import de.intevation.flys.artifacts.WMSDBArtifact.WMSDBState;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.RiverFactory;
-import de.intevation.flys.utils.FLYSUtils;
-import de.intevation.flys.utils.GeometryUtils;
-
-
-public class WMSGaugeLocationArtifact extends WMSDBArtifact {
-
-    public static final String NAME = "wmsgaugelocation";
-
-
-    private static final Logger logger =
-        Logger.getLogger(WMSGaugeLocationArtifact.class);
-
-
-    @Override
-    public void setup(
-        String          identifier,
-        ArtifactFactory factory,
-        Object          context,
-        CallMeta        callMeta,
-        Document        data)
-    {
-        logger.debug("WMSGaugeLocationArtifact.setup");
-
-        super.setup(identifier, factory, context, callMeta, data);
-    }
-
-
-    @Override
-    public String getName() {
-        return NAME;
-    }
-
-
-    @Override
-    public State getCurrentState(Object cc) {
-        State s = new WMSGaugeLocationState(this);
-
-        List<Facet> fs = getFacets(getCurrentStateId());
-
-        DefaultOutput o = new DefaultOutput(
-            "floodmap",
-            "floodmap",
-            "image/png",
-            fs,
-            "map");
-
-        s.getOutputs().add(o);
-
-        return s;
-    }
-
-
-
-    public static class WMSGaugeLocationState extends WMSDBState implements FacetTypes {
-
-        private static final Logger logger =
-            Logger.getLogger(WMSGaugeLocationState.class);
-
-        protected Geometry geom;
-
-        public WMSGaugeLocationState(WMSDBArtifact artifact) {
-            super(artifact);
-        }
-
-        @Override
-        protected String getFacetType() {
-            return FLOODMAP_GAUGE_LOCATION;
-        }
-
-        @Override
-        protected String getUrl() {
-            return FLYSUtils.getUserWMSUrl(artifact.identifier());
-        }
-
-        @Override
-        protected String getSrid() {
-            River river = RiverFactory.getRiver(getRiverId());
-            return FLYSUtils.getRiverSrid(river.getName());
-        }
-
-        @Override
-        protected Envelope getExtent(boolean reproject) {
-            List<GaugeLocation> gauges =
-                GaugeLocation.getGaugeLocations(getRiverId(), getName());
-
-            Envelope max = null;
-
-            for (GaugeLocation gauge: gauges) {
-                Envelope env = gauge.getGeom().getEnvelopeInternal();
-
-                if (max == null) {
-                    max = env;
-                    continue;
-                }
-
-                max.expandToInclude(env);
-            }
-
-            return max != null && reproject
-                ? GeometryUtils.transform(max, getSrid())
-                : max;
-        }
-
-        @Override
-        protected String getFilter() {
-            return "river_id=" + String.valueOf(getRiverId()) +
-                " and name='" + getName() + "'";
-        }
-
-        @Override
-        protected String getDataString() {
-            String srid = getSrid();
-
-            if (FLYSUtils.isUsingOracle()) {
-                return "geom FROM gauge_location USING SRID " + srid;
-            }
-            else {
-                return "geom FROM gauge_location " +
-                       "USING UNIQUE id USING SRID " + srid;
-            }
-        }
-
-        @Override
-        protected String getGeometryType() {
-            return "POINT";
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/WMSHWSLinesArtifact.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,165 +0,0 @@
-package de.intevation.flys.artifacts;
-
-import java.util.List;
-
-import org.w3c.dom.Document;
-
-import org.apache.log4j.Logger;
-
-import com.vividsolutions.jts.geom.Envelope;
-
-import de.intevation.artifacts.ArtifactFactory;
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.artifactdatabase.state.DefaultOutput;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.State;
-
-import de.intevation.flys.model.HWSLine;
-import de.intevation.flys.model.River;
-
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.RiverFactory;
-import de.intevation.flys.utils.FLYSUtils;
-import de.intevation.flys.utils.GeometryUtils;
-
-
-public class WMSHWSLinesArtifact extends WMSDBArtifact {
-
-    public static final String NAME = "hws_lines";
-
-
-    private static final Logger logger =
-        Logger.getLogger(WMSHWSLinesArtifact.class);
-
-
-    @Override
-    public void setup(
-        String          identifier,
-        ArtifactFactory factory,
-        Object          context,
-        CallMeta        callMeta,
-        Document        data)
-    {
-        logger.debug("WMSHWSLinesArtifact.setup");
-
-        super.setup(identifier, factory, context, callMeta, data);
-    }
-
-
-    @Override
-    public String getName() {
-        return NAME;
-    }
-
-
-    @Override
-    public State getCurrentState(Object cc) {
-        State s = new HWSLinesState(this);
-
-        List<Facet> fs = getFacets(getCurrentStateId());
-
-        DefaultOutput o = new DefaultOutput(
-            "floodmap",
-            "floodmap",
-            "image/png",
-            fs,
-            "map");
-
-        s.getOutputs().add(o);
-
-        return s;
-    }
-
-
-    public static class HWSLinesState extends WMSDBState implements FacetTypes
-    {
-        private static final Logger logger =
-            Logger.getLogger(HWSLinesState.class);
-
-        protected int riverId;
-
-        public HWSLinesState(WMSDBArtifact artifact) {
-            super(artifact);
-            riverId = 0;
-        }
-
-        public int getRiverId() {
-            if (riverId == 0) {
-                String   ids   = artifact.getDataAsString("ids");
-                String[] parts = ids.split(";");
-
-                try {
-                    riverId = Integer.parseInt(parts[0]);
-                }
-                catch (NumberFormatException nfe) {
-                    logger.error("Cannot parse river id from '" + parts[0] + "'");
-                }
-            }
-
-            return riverId;
-        }
-
-        @Override
-        protected String getFacetType() {
-            return FLOODMAP_HWS_LINES;
-        }
-
-        @Override
-        protected String getUrl() {
-            return FLYSUtils.getUserWMSUrl(artifact.identifier());
-        }
-
-        @Override
-        protected String getSrid() {
-            River river = RiverFactory.getRiver(getRiverId());
-            return FLYSUtils.getRiverSrid(river.getName());
-        }
-
-        @Override
-        protected Envelope getExtent(boolean reproject) {
-            List<HWSLine> hws = HWSLine.getLines(getRiverId(), getName());
-
-            Envelope max = null;
-
-            for (HWSLine h: hws) {
-                Envelope env = h.getGeom().getEnvelopeInternal();
-
-                if (max == null) {
-                    max = env;
-                    continue;
-                }
-
-                max.expandToInclude(env);
-            }
-
-            return max != null && reproject
-                ? GeometryUtils.transform(max, getSrid())
-                : max;
-        }
-
-        @Override
-        protected String getFilter() {
-            return "river_id=" + String.valueOf(getRiverId())
-                + " AND name='" + getName() + "'";
-        }
-
-        @Override
-        protected String getDataString() {
-            String srid = getSrid();
-
-            if (FLYSUtils.isUsingOracle()) {
-                return "geom FROM hws_lines USING SRID " + srid;
-            }
-            else {
-                return "geom FROM hws_lines USING UNIQUE id USING SRID " + srid;
-            }
-        }
-
-        @Override
-        protected String getGeometryType() {
-            return "LINE";
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/WMSHWSPointsArtifact.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,165 +0,0 @@
-package de.intevation.flys.artifacts;
-
-import java.util.List;
-
-import org.w3c.dom.Document;
-
-import org.apache.log4j.Logger;
-
-import com.vividsolutions.jts.geom.Envelope;
-
-import de.intevation.artifacts.ArtifactFactory;
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.artifactdatabase.state.DefaultOutput;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.State;
-
-import de.intevation.flys.model.HWSPoint;
-import de.intevation.flys.model.River;
-
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.RiverFactory;
-import de.intevation.flys.utils.FLYSUtils;
-import de.intevation.flys.utils.GeometryUtils;
-
-
-public class WMSHWSPointsArtifact extends WMSDBArtifact {
-
-    public static final String NAME = "hws_points";
-
-
-    private static final Logger logger =
-        Logger.getLogger(WMSHWSPointsArtifact.class);
-
-
-    @Override
-    public void setup(
-        String          identifier,
-        ArtifactFactory factory,
-        Object          context,
-        CallMeta        callMeta,
-        Document        data)
-    {
-        logger.debug("WMSHWSPointsArtifact.setup");
-
-        super.setup(identifier, factory, context, callMeta, data);
-    }
-
-
-    @Override
-    public String getName() {
-        return NAME;
-    }
-
-
-    @Override
-    public State getCurrentState(Object cc) {
-        State s = new HWSPointsState(this);
-
-        List<Facet> fs = getFacets(getCurrentStateId());
-
-        DefaultOutput o = new DefaultOutput(
-            "floodmap",
-            "floodmap",
-            "image/png",
-            fs,
-            "map");
-
-        s.getOutputs().add(o);
-
-        return s;
-    }
-
-
-    public static class HWSPointsState extends WMSDBState implements FacetTypes
-    {
-        private static final Logger logger =
-            Logger.getLogger(HWSPointsState.class);
-
-        protected int riverId;
-
-        public HWSPointsState(WMSDBArtifact artifact) {
-            super(artifact);
-            riverId = 0;
-        }
-
-        public int getRiverId() {
-            if (riverId == 0) {
-                String   ids   = artifact.getDataAsString("ids");
-                String[] parts = ids.split(";");
-
-                try {
-                    riverId = Integer.parseInt(parts[0]);
-                }
-                catch (NumberFormatException nfe) {
-                    logger.error("Cannot parse river id from '" + parts[0] + "'");
-                }
-            }
-
-            return riverId;
-        }
-
-        @Override
-        protected String getFacetType() {
-            return FLOODMAP_HWS_POINTS;
-        }
-
-        @Override
-        protected String getUrl() {
-            return FLYSUtils.getUserWMSUrl(artifact.identifier());
-        }
-
-        @Override
-        protected String getSrid() {
-            River river = RiverFactory.getRiver(getRiverId());
-            return FLYSUtils.getRiverSrid(river.getName());
-        }
-
-        @Override
-        protected Envelope getExtent(boolean reproject) {
-            List<HWSPoint> hws = HWSPoint.getPoints(getRiverId(), getName());
-
-            Envelope max = null;
-
-            for (HWSPoint h: hws) {
-                Envelope env = h.getGeom().getEnvelopeInternal();
-
-                if (max == null) {
-                    max = env;
-                    continue;
-                }
-
-                max.expandToInclude(env);
-            }
-
-            return max != null && reproject
-                ? GeometryUtils.transform(max, getSrid())
-                : max;
-        }
-
-        @Override
-        protected String getFilter() {
-            return "river_id=" + String.valueOf(getRiverId())
-                + " AND name='" + getName() + "'";
-        }
-
-        @Override
-        protected String getDataString() {
-            String srid = getSrid();
-
-            if (FLYSUtils.isUsingOracle()) {
-                return "geom FROM hws_points USING SRID " + srid;
-            }
-            else {
-                return "geom FROM hws_points USING UNIQUE id USING SRID " + srid;
-            }
-        }
-
-        @Override
-        protected String getGeometryType() {
-            return "POINT";
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/WMSHydrBoundaryArtifact.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,162 +0,0 @@
-package de.intevation.flys.artifacts;
-
-import java.util.List;
-
-import org.w3c.dom.Document;
-
-import org.apache.log4j.Logger;
-
-import com.vividsolutions.jts.geom.Envelope;
-
-import de.intevation.artifacts.ArtifactFactory;
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.artifactdatabase.state.DefaultOutput;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.State;
-
-import de.intevation.flys.model.River;
-import de.intevation.flys.model.HydrBoundary;
-
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.RiverFactory;
-import de.intevation.flys.utils.FLYSUtils;
-import de.intevation.flys.utils.GeometryUtils;
-
-
-public class WMSHydrBoundaryArtifact extends WMSDBArtifact {
-
-    public static final String NAME = "hydr_boundary";
-
-
-    private static final Logger logger =
-        Logger.getLogger(WMSHydrBoundaryArtifact.class);
-
-
-    @Override
-    public void setup(
-        String          identifier,
-        ArtifactFactory factory,
-        Object          context,
-        CallMeta        callMeta,
-        Document        data)
-    {
-        logger.debug("WMSHydrBoundaryArtifact.setup");
-
-        super.setup(identifier, factory, context, callMeta, data);
-    }
-
-
-    @Override
-    public String getName() {
-        return NAME;
-    }
-
-
-    @Override
-    public State getCurrentState(Object cc) {
-        State s = new HydrBoundaryState(this);
-
-        List<Facet> fs = getFacets(getCurrentStateId());
-
-        DefaultOutput o = new DefaultOutput(
-            "floodmap",
-            "floodmap",
-            "image/png",
-            fs,
-            "map");
-
-        s.getOutputs().add(o);
-
-        return s;
-    }
-
-
-    public static class HydrBoundaryState extends WMSDBState implements FacetTypes
-    {
-        private static final Logger logger =
-            Logger.getLogger(HydrBoundaryState.class);
-
-        protected int riverId;
-
-        public HydrBoundaryState(WMSDBArtifact artifact) {
-            super(artifact);
-            riverId = 0;
-        }
-
-        @Override
-        protected String getFacetType() {
-            return FLOODMAP_HYDR_BOUNDARY;
-        }
-
-        @Override
-        protected String getUrl() {
-            return FLYSUtils.getUserWMSUrl(artifact.identifier());
-        }
-
-        @Override
-        protected String getSrid() {
-            River river = RiverFactory.getRiver(getRiverId());
-            return FLYSUtils.getRiverSrid(river.getName());
-        }
-
-        @Override
-        protected Envelope getExtent(boolean reproject) {
-            List<HydrBoundary> boundaries;
-            String kind = getIdPart(2);
-            if (kind != null) {
-                boundaries = HydrBoundary.getHydrBoundaries(getRiverId(),
-                        getName(), Integer.parseInt(kind));
-            } else {
-                boundaries = HydrBoundary.getHydrBoundaries(getRiverId(), getName());
-            }
-
-            Envelope max = null;
-
-            for (HydrBoundary b: boundaries) {
-                Envelope env = b.getGeom().getEnvelopeInternal();
-
-                if (max == null) {
-                    max = env;
-                    continue;
-                }
-
-                max.expandToInclude(env);
-            }
-
-            return max != null && reproject
-                ? GeometryUtils.transform(max, getSrid())
-                : max;
-        }
-
-        @Override
-        protected String getFilter() {
-            String kind = getIdPart(2);
-            if (kind != null) {
-                return "river_id=" + String.valueOf(getRiverId())
-                    + " AND name='" + getName() + "'"
-                    + " AND kind = " + kind;
-            }
-            return "river_id=" + String.valueOf(getRiverId())
-                + " AND name='" + getName() + "'";
-        }
-
-        @Override
-        protected String getDataString() {
-            String srid = getSrid();
-
-            if (FLYSUtils.isUsingOracle()) {
-                return "geom FROM hydr_boundaries USING SRID " + srid;
-            }
-            else {
-                return "geom FROM hydr_boundaries USING UNIQUE id USING SRID " + srid;
-            }
-        }
-
-        @Override
-        protected String getGeometryType() {
-            return "LINE";
-        }
-    } // end of HydrBoundaryState
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/WMSHydrBoundaryPolyArtifact.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,194 +0,0 @@
-package de.intevation.flys.artifacts;
-
-import java.util.List;
-
-import org.w3c.dom.Document;
-
-import org.apache.log4j.Logger;
-
-import com.vividsolutions.jts.geom.Envelope;
-
-import de.intevation.artifacts.ArtifactFactory;
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.artifactdatabase.state.DefaultOutput;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.State;
-
-import de.intevation.flys.model.River;
-import de.intevation.flys.model.HydrBoundaryPoly;
-
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.RiverFactory;
-import de.intevation.flys.utils.FLYSUtils;
-import de.intevation.flys.utils.GeometryUtils;
-
-
-public class WMSHydrBoundaryPolyArtifact extends WMSDBArtifact {
-
-    public static final String NAME = "hydr_boundary_poly";
-
-
-    private static final Logger logger =
-        Logger.getLogger(WMSHydrBoundaryPolyArtifact.class);
-
-
-    @Override
-    public void setup(
-        String          identifier,
-        ArtifactFactory factory,
-        Object          context,
-        CallMeta        callMeta,
-        Document        data)
-    {
-        logger.debug("WMSHydrBoundaryArtifact.setup");
-
-        super.setup(identifier, factory, context, callMeta, data);
-    }
-
-
-    @Override
-    public String getName() {
-        return NAME;
-    }
-
-
-    @Override
-    public State getCurrentState(Object cc) {
-        State s = new HydrBoundaryPolyState(this);
-
-        List<Facet> fs = getFacets(getCurrentStateId());
-
-        DefaultOutput o = new DefaultOutput(
-            "floodmap",
-            "floodmap",
-            "image/png",
-            fs,
-            "map");
-
-        s.getOutputs().add(o);
-
-        return s;
-    }
-
-
-    public static class HydrBoundaryPolyState extends WMSDBState implements FacetTypes
-    {
-        private static final Logger logger =
-            Logger.getLogger(HydrBoundaryPolyState.class);
-
-        protected int riverId;
-
-        public HydrBoundaryPolyState(WMSDBArtifact artifact) {
-            super(artifact);
-            riverId = 0;
-        }
-
-        @Override
-        protected String getFacetType() {
-            return FLOODMAP_HYDR_BOUNDARY_POLY;
-        }
-
-        @Override
-        protected String getUrl() {
-            return FLYSUtils.getUserWMSUrl(artifact.identifier());
-        }
-
-        @Override
-        protected String getSrid() {
-            River river = RiverFactory.getRiver(getRiverId());
-            return FLYSUtils.getRiverSrid(river.getName());
-        }
-
-        @Override
-        protected Envelope getExtent(boolean reproject) {
-            String kind = getIdPart(2);
-            String sectie = getIdPart(3);
-            String sobek = getIdPart(4);
-            int kindId = -1;
-            int sectieId = -1;
-            int sobekId = -1;
-
-            if (kind != null) {
-                kindId = Integer.parseInt(kind);
-            }
-            if (sectie != null && !sectie.equals("-1")) {
-                sectieId = Integer.parseInt(sectie);
-            }
-            if (sobek != null && !sobek.equals("-1")) {
-                sobekId = Integer.parseInt(sobek);
-            }
-
-            List<HydrBoundaryPoly> boundaries;
-            if (kindId == -1 && sobekId == -1 && sectieId == -1) {
-                boundaries = HydrBoundaryPoly.getHydrBoundaries(
-                        getRiverId(), getName());
-            } else {
-                boundaries = HydrBoundaryPoly.getHydrBoundaries(
-                        getRiverId(), kindId, sectieId, sobekId);
-            }
-
-            Envelope max = null;
-
-            for (HydrBoundaryPoly b: boundaries) {
-                Envelope env = b.getGeom().getEnvelopeInternal();
-
-                if (max == null) {
-                    max = env;
-                    continue;
-                }
-
-                max.expandToInclude(env);
-            }
-
-            return max != null && reproject
-                ? GeometryUtils.transform(max, getSrid())
-                : max;
-        }
-
-        @Override
-        protected String getFilter() {
-            // Expected id string:
-            // river_id;layer_name;kind;sectie;sobek
-            String kind = getIdPart(2);
-            String sectie = getIdPart(3);
-            String sobek = getIdPart(4);
-
-            String filter = "";
-            if (kind != null && !kind.equals("-1")) {
-                filter += " AND kind = " + kind;
-            }
-            if (sectie != null && !sectie.equals("-1")) {
-                filter += " AND sectie = " + sectie;
-            }
-            if (sobek != null && !sobek.equals("-1")) {
-                filter += " AND sobek = " + sobek;
-            }
-
-            if (filter.isEmpty()) {
-                filter = " AND name='" + getName() + "'";
-            }
-
-            return "river_id=" + String.valueOf(getRiverId())
-                + filter;
-        }
-
-        @Override
-        protected String getDataString() {
-            String srid = getSrid();
-
-            if (FLYSUtils.isUsingOracle()) {
-                return "geom FROM hydr_boundaries_poly USING SRID " + srid;
-            }
-            else {
-                return "geom FROM hydr_boundaries_poly USING UNIQUE id USING SRID " + srid;
-            }
-        }
-
-        @Override
-        protected String getGeometryType() {
-            return "POLYGON";
-        }
-    } // end of HydrBoundaryState
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/WMSJettiesArtifact.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,156 +0,0 @@
-package de.intevation.flys.artifacts;
-
-import java.util.List;
-
-import org.w3c.dom.Document;
-
-import org.apache.log4j.Logger;
-
-import com.vividsolutions.jts.geom.Envelope;
-
-import de.intevation.artifacts.ArtifactFactory;
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.artifactdatabase.state.DefaultOutput;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.State;
-
-import de.intevation.flys.model.River;
-import de.intevation.flys.model.Jetty;
-
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.RiverFactory;
-import de.intevation.flys.utils.FLYSUtils;
-import de.intevation.flys.utils.GeometryUtils;
-
-
-public class WMSJettiesArtifact extends WMSDBArtifact {
-
-    public static final String NAME = "jetties";
-
-
-    private static final Logger logger =
-        Logger.getLogger(WMSJettiesArtifact.class);
-
-
-    @Override
-    public void setup(
-        String          identifier,
-        ArtifactFactory factory,
-        Object          context,
-        CallMeta        callMeta,
-        Document        data)
-    {
-        logger.debug("WMSJettiesArtifact.setup");
-
-        super.setup(identifier, factory, context, callMeta, data);
-    }
-
-
-    @Override
-    public String getName() {
-        return NAME;
-    }
-
-
-    @Override
-    public State getCurrentState(Object cc) {
-        State s = new JettiesState(this);
-
-        List<Facet> fs = getFacets(getCurrentStateId());
-
-        DefaultOutput o = new DefaultOutput(
-            "floodmap",
-            "floodmap",
-            "image/png",
-            fs,
-            "map");
-
-        s.getOutputs().add(o);
-
-        return s;
-    }
-
-
-    public static class JettiesState extends WMSDBState implements FacetTypes
-    {
-        private static final Logger logger =
-            Logger.getLogger(JettiesState.class);
-
-        protected int riverId;
-
-        public JettiesState(WMSDBArtifact artifact) {
-            super(artifact);
-            riverId = 0;
-        }
-
-        @Override
-        protected String getFacetType() {
-            return FLOODMAP_JETTIES;
-        }
-
-        @Override
-        protected String getUrl() {
-            return FLYSUtils.getUserWMSUrl(artifact.identifier());
-        }
-
-        @Override
-        protected String getSrid() {
-            River river = RiverFactory.getRiver(getRiverId());
-            return FLYSUtils.getRiverSrid(river.getName());
-        }
-
-        @Override
-        protected Envelope getExtent(boolean reproject) {
-            List<Jetty> jetties;
-            String kind = getIdPart(2);
-
-            jetties = Jetty.getJetties(getRiverId(), Integer.parseInt(kind));
-
-            Envelope max = null;
-
-            for (Jetty b: jetties) {
-                Envelope env = b.getGeom().getEnvelopeInternal();
-
-                if (max == null) {
-                    max = env;
-                    continue;
-                }
-
-                max.expandToInclude(env);
-            }
-
-            return max != null && reproject
-                ? GeometryUtils.transform(max, getSrid())
-                : max;
-        }
-
-        @Override
-        protected String getFilter() {
-            // The expected id string is:
-            // river_id;layer-name;kind
-
-            String kind = getIdPart(2);
-            return "river_id=" + String.valueOf(getRiverId()) +
-                " AND kind_id=" + kind;
-        }
-
-        @Override
-        protected String getDataString() {
-            String srid = getSrid();
-
-            if (FLYSUtils.isUsingOracle()) {
-                return "geom FROM jetties USING SRID " + srid;
-            }
-            else {
-                return "geom FROM jetties USING UNIQUE id USING SRID " + srid;
-            }
-        }
-
-        @Override
-        protected String getGeometryType() {
-            return "POINT";
-        }
-    } // end of WMSKmState
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/WMSKmArtifact.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,171 +0,0 @@
-package de.intevation.flys.artifacts;
-
-import java.util.List;
-
-import org.w3c.dom.Document;
-
-import org.apache.log4j.Logger;
-
-import com.vividsolutions.jts.geom.Envelope;
-import com.vividsolutions.jts.geom.Geometry;
-
-import de.intevation.artifacts.ArtifactFactory;
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.DefaultOutput;
-import de.intevation.artifactdatabase.state.State;
-
-import de.intevation.flys.model.River;
-import de.intevation.flys.model.RiverAxisKm;
-
-import de.intevation.flys.artifacts.WMSDBArtifact.WMSDBState;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.RiverFactory;
-import de.intevation.flys.utils.FLYSUtils;
-import de.intevation.flys.utils.GeometryUtils;
-
-
-public class WMSKmArtifact extends WMSDBArtifact {
-
-    public static final String NAME = "wmskm";
-
-
-    private static final Logger logger = Logger.getLogger(WMSKmArtifact.class);
-
-
-    @Override
-    public void setup(
-        String          identifier,
-        ArtifactFactory factory,
-        Object          context,
-        CallMeta        callMeta,
-        Document        data)
-    {
-        logger.debug("WMSKmArtifact.setup");
-
-        super.setup(identifier, factory, context, callMeta, data);
-    }
-
-
-    @Override
-    public String getName() {
-        return NAME;
-    }
-
-
-    @Override
-    public State getCurrentState(Object cc) {
-        State s = new WMSKmState(this);
-
-        List<Facet> fs = getFacets(getCurrentStateId());
-
-        DefaultOutput o = new DefaultOutput(
-            "floodmap",
-            "floodmap",
-            "image/png",
-            fs,
-            "map");
-
-        s.getOutputs().add(o);
-
-        return s;
-    }
-
-
-
-    public static class WMSKmState extends WMSDBState implements FacetTypes {
-
-        private static final Logger logger = Logger.getLogger(WMSKmState.class);
-
-        protected Geometry geom;
-        protected int      riverId;
-
-        public WMSKmState(WMSDBArtifact artifact) {
-            super(artifact);
-            riverId = 0;
-        }
-
-        public int getRiverId() {
-            if (riverId == 0) {
-                String ids = artifact.getDataAsString("ids");
-
-                try {
-                    riverId = Integer.parseInt(ids);
-                }
-                catch (NumberFormatException nfe) {
-                    logger.error("Cannot parse river id from '" + ids + "'");
-                }
-            }
-
-            return riverId;
-        }
-
-        @Override
-        protected String getFacetType() {
-            return FLOODMAP_KMS;
-        }
-
-        @Override
-        protected String getUrl() {
-            return FLYSUtils.getUserWMSUrl(artifact.identifier());
-        }
-
-        @Override
-        protected String getSrid() {
-            River river = RiverFactory.getRiver(getRiverId());
-            return FLYSUtils.getRiverSrid(river.getName());
-        }
-
-        @Override
-        protected Envelope getExtent(boolean reproject) {
-            List<RiverAxisKm> kms = RiverAxisKm.getRiverAxisKms(getRiverId());
-
-            Envelope max = null;
-
-            for (RiverAxisKm km: kms) {
-                Envelope env = km.getGeom().getEnvelopeInternal();
-
-                if (max == null) {
-                    max = env;
-                    continue;
-                }
-
-                max.expandToInclude(env);
-            }
-
-            return max != null && reproject
-                ? GeometryUtils.transform(max, getSrid())
-                : max;
-        }
-
-        @Override
-        protected String getFilter() {
-            return "river_id=" + String.valueOf(getRiverId());
-        }
-
-        @Override
-        protected String getDataString() {
-            String srid = getSrid();
-
-            if (FLYSUtils.isUsingOracle()) {
-                return "geom FROM river_axes_km USING SRID " + srid;
-            }
-            else {
-                return "geom FROM river_axes_km " +
-                       "USING UNIQUE id USING SRID " + srid;
-            }
-        }
-
-        @Override
-        protected String getLabelItem() {
-            return "km";
-        }
-
-        @Override
-        protected String getGeometryType() {
-            return "POINT";
-        }
-    } // end of WMSKmState
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/WMSLineArtifact.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,165 +0,0 @@
-package de.intevation.flys.artifacts;
-
-import java.util.List;
-
-import org.w3c.dom.Document;
-
-import org.apache.log4j.Logger;
-
-import com.vividsolutions.jts.geom.Envelope;
-
-import de.intevation.artifacts.ArtifactFactory;
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.artifactdatabase.state.DefaultOutput;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.State;
-
-import de.intevation.flys.model.River;
-import de.intevation.flys.model.HWSLine;
-
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.RiverFactory;
-import de.intevation.flys.utils.FLYSUtils;
-import de.intevation.flys.utils.GeometryUtils;
-
-
-public class WMSLineArtifact extends WMSDBArtifact {
-
-    public static final String NAME = "lines";
-
-
-    private static final Logger logger =
-        Logger.getLogger(WMSLineArtifact.class);
-
-
-    @Override
-    public void setup(
-        String          identifier,
-        ArtifactFactory factory,
-        Object          context,
-        CallMeta        callMeta,
-        Document        data)
-    {
-        logger.debug("WMSLineArtifact.setup");
-
-        super.setup(identifier, factory, context, callMeta, data);
-    }
-
-
-    @Override
-    public String getName() {
-        return NAME;
-    }
-
-
-    @Override
-    public State getCurrentState(Object cc) {
-        State s = new LineState(this);
-
-        List<Facet> fs = getFacets(getCurrentStateId());
-
-        DefaultOutput o = new DefaultOutput(
-            "floodmap",
-            "floodmap",
-            "image/png",
-            fs,
-            "map");
-
-        s.getOutputs().add(o);
-
-        return s;
-    }
-
-
-    public static class LineState extends WMSDBState implements FacetTypes
-    {
-        private static final Logger logger =
-            Logger.getLogger(LineState.class);
-
-        protected int riverId;
-
-        public LineState(WMSDBArtifact artifact) {
-            super(artifact);
-            riverId = 0;
-        }
-
-        public int getRiverId() {
-            if (riverId == 0) {
-                String   ids   = artifact.getDataAsString("ids");
-                String[] parts = ids.split(";");
-
-                try {
-                    riverId = Integer.parseInt(parts[0]);
-                }
-                catch (NumberFormatException nfe) {
-                    logger.error("Cannot parse river id from '" + parts[0] + "'");
-                }
-            }
-
-            return riverId;
-        }
-
-        @Override
-        protected String getFacetType() {
-            return FLOODMAP_LINES;
-        }
-
-        @Override
-        protected String getUrl() {
-            return FLYSUtils.getUserWMSUrl(artifact.identifier());
-        }
-
-        @Override
-        protected String getSrid() {
-            River river = RiverFactory.getRiver(getRiverId());
-            return FLYSUtils.getRiverSrid(river.getName());
-        }
-
-        @Override
-        protected Envelope getExtent(boolean reproject) {
-            List<HWSLine> lines = HWSLine.getLines(getRiverId(), getName());
-
-            Envelope max = null;
-
-            for (HWSLine l: lines) {
-                Envelope env = l.getGeom().getEnvelopeInternal();
-
-                if (max == null) {
-                    max = env;
-                    continue;
-                }
-
-                max.expandToInclude(env);
-            }
-
-            return max != null && reproject
-                ? GeometryUtils.transform(max, getSrid())
-                : max;
-        }
-
-        @Override
-        protected String getFilter() {
-            return "river_id=" + String.valueOf(getRiverId()) +
-                " AND name='" + getName() + "'";
-        }
-
-        @Override
-        protected String getDataString() {
-            String srid = getSrid();
-
-            if (FLYSUtils.isUsingOracle()) {
-                return "geom FROM lines USING SRID " + srid;
-            }
-            else {
-                return "geom FROM lines USING UNIQUE id USING SRID " + srid;
-            }
-        }
-
-        @Override
-        protected String getGeometryType() {
-            return "LINE";
-        }
-    } // end of WMSKmState
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/WMSQPSArtifact.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,171 +0,0 @@
-package de.intevation.flys.artifacts;
-
-import java.util.List;
-
-import org.w3c.dom.Document;
-
-import org.apache.log4j.Logger;
-
-import com.vividsolutions.jts.geom.Envelope;
-
-import de.intevation.artifacts.ArtifactFactory;
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.artifactdatabase.state.DefaultOutput;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.State;
-
-import de.intevation.flys.model.River;
-import de.intevation.flys.model.CrossSectionTrack;
-
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.RiverFactory;
-import de.intevation.flys.utils.FLYSUtils;
-import de.intevation.flys.utils.GeometryUtils;
-
-
-public class WMSQPSArtifact extends WMSDBArtifact {
-
-    public static final String NAME = "qps";
-
-
-    private static final Logger logger =
-        Logger.getLogger(WMSQPSArtifact.class);
-
-
-    @Override
-    public void setup(
-        String          identifier,
-        ArtifactFactory factory,
-        Object          context,
-        CallMeta        callMeta,
-        Document        data)
-    {
-        logger.debug("WMSQPSArtifact.setup");
-
-        super.setup(identifier, factory, context, callMeta, data);
-    }
-
-
-    @Override
-    public String getName() {
-        return NAME;
-    }
-
-
-    @Override
-    public State getCurrentState(Object cc) {
-        State s = new WMSQPSState(this);
-
-        List<Facet> fs = getFacets(getCurrentStateId());
-
-        DefaultOutput o = new DefaultOutput(
-            "floodmap",
-            "floodmap",
-            "image/png",
-            fs,
-            "map");
-
-        s.getOutputs().add(o);
-
-        return s;
-    }
-
-
-    public static class WMSQPSState extends WMSDBState implements FacetTypes {
-
-        private static final Logger logger =
-            Logger.getLogger(WMSQPSState.class);
-
-        public WMSQPSState(WMSDBArtifact artifact) {
-            super(artifact);
-        }
-
-        @Override
-        protected String getFacetType() {
-            return FLOODMAP_QPS;
-        }
-
-        @Override
-        protected String getUrl() {
-            return FLYSUtils.getUserWMSUrl(artifact.identifier());
-        }
-
-        @Override
-        protected String getSrid() {
-            River river = RiverFactory.getRiver(getRiverId());
-            return FLYSUtils.getRiverSrid(river.getName());
-        }
-
-        @Override
-        protected Envelope getExtent(boolean reproject) {
-            River river = RiverFactory.getRiver(getRiverId());
-            List<CrossSectionTrack> qps;
-
-            String kind = getIdPart(2);
-
-            if (kind != null && kind.equals("1")) {
-                qps = CrossSectionTrack.getCrossSectionTrack(river.getName(),
-                        Integer.parseInt(kind));
-            } else if (kind != null) {
-                qps = CrossSectionTrack.getCrossSectionTrack(river.getName(),
-                        getName(), Integer.parseInt(kind));
-            } else {
-                qps = CrossSectionTrack.getCrossSectionTrack(river.getName(),
-                        getName());
-            }
-
-            Envelope max = null;
-
-            for (CrossSectionTrack qp: qps) {
-                Envelope env = qp.getGeom().getEnvelopeInternal();
-
-                if (max == null) {
-                    max = env;
-                    continue;
-                }
-
-                max.expandToInclude(env);
-            }
-
-            return max != null && reproject
-                ? GeometryUtils.transform(max, getSrid())
-                : max;
-        }
-
-        @Override
-        protected String getFilter() {
-            String kind = getIdPart(2);
-            if (kind != null && kind.equals("1")) {
-                // There can be several layers named qps that differ in kind
-                return "river_id=" + String.valueOf(getRiverId()) +
-                    " AND kind_id=" + kind;
-            } else if (kind != null) {
-                return "river_id=" + String.valueOf(getRiverId()) +
-                    " AND kind_id=" + kind +
-                    " AND name='" + getName() + "'";
-            }
-            return "river_id=" + String.valueOf(getRiverId()) +
-                " AND name='" + getName() + "'";
-        }
-
-        @Override
-        protected String getDataString() {
-            String srid = getSrid();
-
-            if (FLYSUtils.isUsingOracle()) {
-                return "geom FROM cross_section_tracks USING SRID " + srid;
-            }
-            else {
-                return "geom FROM cross_section_tracks " +
-                       "USING UNIQUE id USING SRID " + srid;
-            }
-        }
-
-        @Override
-        protected String getGeometryType() {
-            return "LINE";
-        }
-    } // end of WMSQPSState
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/WQKmsInterpolArtifact.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,295 +0,0 @@
-package de.intevation.flys.artifacts;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Document;
-
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.FacetActivity;
-import de.intevation.artifactdatabase.state.DefaultOutput;
-import de.intevation.artifactdatabase.state.State;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.ArtifactFactory;
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.WQKms;
-import de.intevation.flys.artifacts.model.WQFacet;
-import de.intevation.flys.artifacts.model.WKmsFactory;
-import de.intevation.flys.artifacts.model.WQKmsFactory;
-import de.intevation.flys.artifacts.model.WstValueTable;
-import de.intevation.flys.artifacts.model.WstValueTableFactory;
-
-import de.intevation.flys.artifacts.states.StaticState;
-import de.intevation.flys.artifacts.resources.Resources;
-
-
-/**
- * Artifact to access additional "waterlevel/discharge"-type of data, like
- * fixation measurements, but doing so with costy interpolation.
- *
- * This artifact neglects (Static)FLYSArtifacts capabilities of interaction
- * with the StateEngine by overriding the getState*-methods.
- */
-public class WQKmsInterpolArtifact
-extends      StaticFLYSArtifact
-implements   FacetTypes
-{
-    /** The logger for this class. */
-    private static Logger logger =
-        Logger.getLogger(WQKmsInterpolArtifact.class);
-
-    /** State name. */
-    public static final String STATIC_STATE_NAME =
-        "state.additional_wqkms.interpol.static";
-
-    /** Artifact name. */
-    private static final String NAME = "staticwqkmsinterpol";
-
-    static {
-        // TODO: Move to configuration.
-        FacetActivity.Registry.getInstance()
-            .register(NAME, FacetActivity.INACTIVE);
-    }
-
-    /** One and only state to be in. */
-    protected transient State state = null;
-
-
-    /**
-     * Trivial Constructor.
-     */
-    public WQKmsInterpolArtifact() {
-        logger.debug("WQKmsInterpolArtifact.WQKmsInterpolArtifact");
-    }
-
-
-    /** Return fixed artifact (types) name. */
-    @Override
-    public String getName() {
-        return NAME;
-    }
-
-
-    /**
-     * Gets called from factory, to set things up.
-     */
-    @Override
-    public void setup(
-        String          identifier,
-        ArtifactFactory factory,
-        Object          context,
-        CallMeta        callMeta,
-        Document        data)
-    {
-        logger.debug("WQKmsInterpolArtifact.setup");
-
-        state = new StaticState(STATIC_STATE_NAME);
-
-        List<Facet> fs = new ArrayList<Facet>();
-        String code = getDatacageIDValue(data);
-
-        // TODO Go for JSON, one day.
-        //ex.: flood_protection-wstv-114-12
-        if (code != null) {
-            String [] parts = code.split("-");
-
-            logger.debug("WQKmsInterpolArtifact.setup: code " + code);
-
-            if (parts.length >= 4) {
-                int wst = Integer.parseInt(parts[3]);
-                int col = -1;
-                String colpos = parts[2];
-                // Are we interested in a single column or in all columns?
-                if (colpos.equals("A")) {
-                    ; // Take all.
-                }
-                else {
-                    col = Integer.parseInt(colpos);
-                    addStringData("col_pos", parts[2]);
-                }
-                addStringData("wst_id",  parts[3]);
-                String wkmsName = (col >= 0)
-                                ? WKmsFactory.getWKmsName(col, wst)
-                                : WKmsFactory.getWKmsName(wst);
-                String name;
-                if (parts[0].startsWith("height")){
-                    name = STATIC_WQ_ANNOTATIONS;
-                }
-                else if (parts[0].startsWith("flood")) {
-                    name = STATIC_WKMS_INTERPOL;
-                }
-                else {
-                    name = STATIC_WQ;
-                }
-
-                Facet wQFacet = new WQFacet(name,
-                    Resources.getMsg(
-                        callMeta,
-                        wkmsName,
-                        wkmsName));
-                fs.add(wQFacet);
-                addFacets(state.getID(), fs);
-            }
-        }
-        else {
-            logger.warn("WQKmsInterpolArtifact: no code");
-        }
-
-        spawnState();
-        super.setup(identifier, factory, context, callMeta, data);
-    }
-
-
-    /**
-     * Initialize the static state with output.
-     * @return static state
-     */
-    protected State spawnState() {
-        state = new StaticState(STATIC_STATE_NAME);
-        List<Facet> fs = getFacets(STATIC_STATE_NAME);
-        DefaultOutput output = new DefaultOutput(
-            "general",
-            "general",
-            "image/png",
-            fs,
-            "chart");
-
-        state.getOutputs().add(output);
-
-        return state;
-    }
-
-
-    /**
-     * Called via setup.
-     *
-     * @param artifact The master-artifact.
-     */
-    @Override
-    protected void initialize(
-        Artifact artifact,
-        Object context,
-        CallMeta meta)
-    {
-        logger.debug("WQKmsInterpolArtifact.initialize");
-        FLYSArtifact winfo = (FLYSArtifact) artifact;
-        importData(winfo, "river");
-        importData(winfo, "ld_locations");
-    }
-
-
-    /**
-     * 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 WQ at a given km.
-     * @param currentKm the requested km. If NULL, ld_location data
-     *        will be used.
-     */
-    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"));
-        }
-
-        Double tmp = (currentKm != null)
-                     ? currentKm
-                     : getDataAsDouble("ld_locations");
-
-        double [][] vs = interpolator.interpolateWQColumnwise(
-            tmp != null ? tmp : 0);
-
-        for (int x = 0; x < vs[1].length; x++) {
-            logger.debug("getWQAtKm: Q/W " + vs[0][x] + " / " + vs[1][x]);
-        }
-
-        return vs;
-    }
-
-
-    /**
-     * Get a DataItem casted to int (0 if fails).
-     */
-    public int getDataAsInt(String dataName) {
-        String val = getDataAsString(dataName);
-        try {
-            return Integer.parseInt(val);
-        }
-        catch (NumberFormatException e) {
-            logger.warn("Could not get data " + dataName + " as int", e);
-            return 0;
-        }
-    }
-
-
-    /**
-     * 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();
-    }
-
-
-    /**
-     * Get WQKms from factory.
-     * @param idx param is not needed (TODO)
-     * @return WQKms according to parameterization (can be null);
-     */
-    public WQKms getWQKms(int idx) {
-        logger.debug("WQKmsInterpolArtifact.getWQKms");
-        logger.warn("Stub, getWQKms not yet implemented.");
-
-        return WQKmsFactory.getWQKms(
-            Integer.parseInt(getDataAsString("col_pos")),
-            Integer.parseInt(getDataAsString("wst_id")));
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/WaterLineArtifact.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,23 +0,0 @@
-package de.intevation.flys.artifacts;
-
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.geom.Lines;
-import de.intevation.flys.model.FastCrossSectionLine;
-
-
-/**
- * Interface, Artifact can create WaterLines (Water against Cross-Profile).
- */
-public interface WaterLineArtifact {
-
-    /** Get points that define a line of a (water)facet against a cross-
-     * section. */
-    public Lines.LineData getWaterLines(
-        int                  facetIdx,
-        FastCrossSectionLine      csl,
-        double                      d,
-        double                      w,
-        CallContext           context);
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/WaterlevelArtifact.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,122 +0,0 @@
-package de.intevation.flys.artifacts;
-
-import de.intevation.artifactdatabase.state.DefaultFacet;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.FacetActivity;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.ArtifactFactory;
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.flys.artifacts.states.DefaultState;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Document;
-
-
-/**
- * Clone of an WINFOArtifact to expose exactly one waterlevel only.
- * All Facets of the "longitudinal_section" output will be added to the
- * "w_differences" output and filterFacets adjusted accordingly.
- *
- * @TODO Straighten inheritance-line (waterlevel-WINFO or vice versa).
- */
-public class WaterlevelArtifact extends WINFOArtifact {
-
-    /** The logger for this class. */
-    private static Logger logger = Logger.getLogger(WaterlevelArtifact.class);
-
-    /** The name of the artifact. */
-    public static final String ARTIFACT_NAME = "waterlevel";
-
-    static {
-        // TODO: Move to configuration.
-        FacetActivity.Registry.getInstance()
-            .register(ARTIFACT_NAME, FacetActivity.INACTIVE);
-    }
-
-    /**
-     * The default constructor.
-     */
-    public WaterlevelArtifact() {
-    }
-
-
-    /**
-     * Setup and restate longitudinal_section filterfacets to apply to the
-     * w_differences output, too. Also, for w_differences, add respective q-
-     * filter facets.
-     */
-    public void setup(
-        String          identifier,
-        ArtifactFactory factory,
-        Object          context,
-        CallMeta        callMeta,
-        Document        data)
-    {
-        super.setup(identifier, factory, context, callMeta, data);
-        // For w_differences, also allow q-facets.
-        if(filterFacets != null) {
-            List<Facet> list = new ArrayList<Facet>();
-            List<Facet> wlist = filterFacets.get(ChartType.LS);
-            if (wlist == null) {
-                logger.warn("No matching filterfacets found");
-                dumpFilterFacets();
-            } else {
-                for (Facet f: wlist) {
-                    if (!f.getName().equals(LONGITUDINAL_Q)) {
-                        DefaultFacet df = new DefaultFacet(f.getIndex(),
-                            "longitudinal_section.q", "");
-                        list.add(df);
-                    }
-                }
-
-                list.addAll(wlist);
-
-                filterFacets.put("w_differences", list);
-            }
-        }
-    }
-
-
-    /**
-     * Clone important stuff of an WINFOArtifact.
-     * @param artifact the WINFOArtifact to clone stuff from.
-     */
-    protected void initialize(
-        Artifact artifact,
-        Object context,
-        CallMeta meta)
-    {
-        WINFOArtifact winfo = (WINFOArtifact) artifact;
-        setData(winfo.cloneData());
-        logger.debug("Cloned data of winfo artifact.");
-        // Statically add Facets.
-        List<Facet> fs = new ArrayList<Facet>();
-        DefaultState state = (DefaultState) getCurrentState(context);
-        state.computeInit(this, hash(), context, meta, fs);
-        if (!fs.isEmpty()) {
-            logger.debug("Facets to add in WaterlevelArtifact.initialize .");
-            addFacets(getCurrentStateId(), fs);
-        }
-        else {
-            logger.debug("No facets to add in WaterlevelArtifact.initialize ("
-                + state.getID() + ").");
-        }
-    }
-
-
-    /**
-     * Returns the name of the concrete artifact.
-     *
-     * @return the name of the concrete artifact.
-     */
-    public String getName() {
-        return ARTIFACT_NAME;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/access/Access.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,197 +0,0 @@
-package de.intevation.flys.artifacts.access;
-
-import de.intevation.artifactdatabase.data.StateData;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.flys.artifacts.model.DateRange;
-
-import de.intevation.flys.utils.FLYSUtils;
-
-import gnu.trove.TDoubleArrayList;
-import gnu.trove.TLongArrayList;
-
-import java.util.ArrayList;
-import java.util.Date;
-
-import org.apache.log4j.Logger;
-
-public class Access
-{
-    private static Logger log = Logger.getLogger(Access.class);
-
-    protected FLYSArtifact artifact;
-
-    public Access() {
-    }
-
-    public Access(FLYSArtifact artifact) {
-        this.artifact = artifact;
-    }
-
-    public FLYSArtifact getArtifact() {
-        return artifact;
-    }
-
-    public void setArtifact(FLYSArtifact artifact) {
-        this.artifact = artifact;
-    }
-
-
-    /** Get a data entry as string. */
-    protected String getString(String key) {
-        StateData sd = artifact.getData(key);
-        if (sd == null) {
-            log.warn("missing '" + key + "' value");
-            return null;
-        }
-        return (String)sd.getValue();
-    }
-
-    /** Get a data entry as double, returns null if string not double valueoffable. */
-    protected Double getDouble(String key) {
-        StateData sd = artifact.getData(key);
-        if (sd == null) {
-            log.warn("missing '" + key + "' value");
-            return null;
-        }
-        try {
-            return Double.valueOf((String)sd.getValue());
-        }
-        catch (NumberFormatException nfe) {
-            log.warn(key + " '" + sd.getValue() + "' is not numeric.");
-        }
-        return null;
-    }
-
-    protected Long getLong(String key) {
-        StateData sd = artifact.getData(key);
-        if (sd == null) {
-            log.warn("missing '" + key + "' value");
-            return null;
-        }
-        try {
-            return Long.valueOf((String)sd.getValue());
-        }
-        catch (NumberFormatException nfe) {
-            log.warn(key + " '" + sd.getValue() + "' is not a long integer.");
-        }
-        return null;
-    }
-
-    protected Integer getInteger(String key) {
-        StateData sd = artifact.getData(key);
-        if (sd == null) {
-            log.warn("missing '" + key + "' value");
-            return null;
-        }
-        try {
-            return Integer.valueOf((String)sd.getValue());
-        }
-        catch (NumberFormatException nfe) {
-            log.warn(key + " '" + sd.getValue() + "' is not a integer.");
-        }
-        return null;
-    }
-
-    protected int [] getIntArray(String key) {
-        StateData sd = artifact.getData(key);
-        if (sd == null) {
-            log.warn("missing '" + key +"' value");
-            return null;
-        }
-        return FLYSUtils.intArrayFromString((String)sd.getValue());
-    }
-
-    protected DateRange [] getDateRange(String key) {
-
-        StateData sd = artifact.getData(key);
-
-        if (sd == null) {
-            log.warn("missing '" + key + "'");
-            return null;
-        }
-
-        String data = (String)sd.getValue();
-        String[] pairs = data.split("\\s*;\\s*");
-
-        ArrayList<DateRange> aPs = new ArrayList<DateRange>(pairs.length);
-
-        for (int i = 0; i < pairs.length; i++) {
-            String[] fromTo = pairs[i].split("\\s*,\\s*");
-            if (fromTo.length >= 2) {
-                try {
-                    Date from = new Date(Long.parseLong(fromTo[0]));
-                    Date to   = new Date(Long.parseLong(fromTo[1]));
-                    DateRange aP = new DateRange(from, to);
-                    if (!aPs.contains(aP)) {
-                        aPs.add(aP);
-                    }
-                }
-                catch (NumberFormatException nfe) {
-                    log.warn(key + " contains no long values.", nfe);
-                }
-            }
-        }
-
-        DateRange [] result = aPs.toArray(new DateRange[aPs.size()]);
-
-        if (log.isDebugEnabled()) {
-            for (int i = 0; i < result.length; ++i) {
-                DateRange ap = result[i];
-                log.debug("period " +
-                    ap.getFrom() + " - " + ap.getTo());
-            }
-        }
-
-        return result;
-    }
-
-    protected Boolean getBoolean(String key) {
-        StateData sd = artifact.getData(key);
-        if (sd == null) {
-            log.warn("missing '" + key + "' value");
-            return null;
-        }
-        return Boolean.valueOf((String)sd.getValue());
-    }
-
-    protected double [] getDoubleArray(String key) {
-        StateData sd = artifact.getData(key);
-        if (sd == null) {
-            log.warn("missing '" + key + "'");
-            return null;
-        }
-        String [] parts = ((String)sd.getValue()).split("[\\s;]+");
-        TDoubleArrayList list = new TDoubleArrayList(parts.length);
-        for (String part: parts) {
-            try {
-                list.add(Double.parseDouble(part));
-            }
-            catch (NumberFormatException nfe) {
-                log.warn("'" + part + "' is not numeric.");
-            }
-        }
-        return list.toNativeArray();
-    }
-
-    protected long [] getLongArray(String key) {
-        StateData sd = artifact.getData(key);
-        if (sd == null) {
-            log.warn("missing '" + key + "'");
-            return null;
-        }
-        String [] parts = ((String)sd.getValue()).split("[\\s;]+");
-        TLongArrayList list = new TLongArrayList(parts.length);
-        for (String part: parts) {
-            try {
-                list.add(Long.parseLong(part));
-            }
-            catch (NumberFormatException nfe) {
-                log.warn("'" + part + "' is not numeric.");
-            }
-        }
-        return list.toNativeArray();
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/access/BedDifferencesAccess.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-package de.intevation.flys.artifacts.access;
-
-import java.util.Arrays;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.CallContext;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.utils.FLYSUtils;
-import de.intevation.flys.utils.StringUtil;
-
-
-public class BedDifferencesAccess
-extends RiverAccess
-{
-    private static Logger logger = Logger.getLogger(BedDifferencesAccess.class);
-    private String yearEpoch;
-    private String[] diffs;
-
-    private CallContext context;
-
-    public BedDifferencesAccess(FLYSArtifact artifact, CallContext context) {
-        super(artifact);
-        this.context = context;
-    }
-
-    public String getYearEpoch() {
-        yearEpoch = getString("ye_select");
-        return yearEpoch;
-    }
-
-    public FLYSArtifact[][] getDifferenceArtifacts() {
-        diffs = getString("diffids").split("#");
-        logger.debug("diffs: " + Arrays.toString(diffs));
-        FLYSArtifact[][] artifacts = new FLYSArtifact[diffs.length/2][2];
-        for (int i = 0; i < diffs.length; i += 2) {
-            String diff1 = StringUtil.unbracket(diffs[0 + 2*i]);
-            String diff2 = StringUtil.unbracket(diffs[1 + 2*i]);
-            String[] diff1parts = diff1.split(";");
-            String[] diff2parts = diff2.split(";");
-            logger.debug("creating 2 artifacts." + diff1parts[0] + "; " + diff2parts[0]);
-            artifacts[i][0] = FLYSUtils.getArtifact(diff1parts[0], context);
-            artifacts[i][1] = FLYSUtils.getArtifact(diff2parts[0], context);
-        }
-        return artifacts;
-    }
-
-    public static int getHeightId(FLYSArtifact artifact) {
-        Access a = new Access(artifact);
-        return a.getInteger("height_id");
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/access/BedHeightAccess.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,152 +0,0 @@
-package de.intevation.flys.artifacts.access;
-
-import java.util.List;
-
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.model.minfo.BedHeight;
-import de.intevation.flys.artifacts.model.minfo.BedHeightFactory;
-import de.intevation.flys.artifacts.states.SoundingsSelect;
-import de.intevation.flys.model.BedHeightSingle;
-import de.intevation.flys.model.BedHeightSingleValue;
-
-import gnu.trove.TIntArrayList;
-
-import org.apache.log4j.Logger;
-
-
-public class BedHeightAccess
-extends      RangeAccess
-{
-
-    private static final Logger logger = Logger.getLogger(BedHeightAccess.class);
-
-    private int[] singleIDs;
-    private int[] epochIDs;
-
-    private String time;
-
-    public BedHeightAccess(FLYSArtifact artifact, CallContext context) {
-        super(artifact, context);
-    }
-
-
-    public Double getLowerKM() {
-        // TODO update callers
-        return getFrom();
-    }
-
-
-    public Double getUpperKM() {
-        // TODO update callers
-        return getTo();
-    }
-
-
-    public int[] getBedHeightSingleIDs() {
-        if (singleIDs == null) {
-            String data = getString("soundings");
-
-            if (data == null) {
-                logger.warn("No 'soundings' parameter specified!");
-                return null;
-            }
-            else {
-                logger.debug("getBedHeightSingleIDs(): data=" + data);
-            }
-
-            String[] parts = data.split(";");
-
-            TIntArrayList ids = new TIntArrayList();
-
-            for (String part: parts) {
-                if (part.indexOf(SoundingsSelect.PREFIX_SINGLE) >= 0) {
-                    String tmp = part.replace(SoundingsSelect.PREFIX_SINGLE, "");
-
-                    try {
-                        int i = Integer.parseInt(tmp);
-                        if (!ids.contains(i)) {
-                            ids.add(i);
-                        }
-                    }
-                    catch (NumberFormatException nfe) {
-                        logger.warn("Cannot parse int from string: '" + tmp + "'");
-                    }
-                }
-            }
-
-            singleIDs = ids.toNativeArray();
-        }
-
-        return singleIDs;
-    }
-
-
-    public String getYearEpoch() {
-        if (time == null) {
-            time =  getString("ye_select");
-        }
-        return time;
-    }
-
-
-    public int[] getBedHeightEpochIDs() {
-        if (epochIDs == null) {
-            String data = getString("soundings");
-
-            if (data == null) {
-                logger.warn("No 'soundings' parameter specified!");
-                return null;
-            }
-
-            String[] parts = data.split(";");
-
-            TIntArrayList ids = new TIntArrayList();
-
-            for (String part: parts) {
-                if (part.indexOf(SoundingsSelect.PREFIX_EPOCH) >= 0) {
-                    String tmp = part.replace(SoundingsSelect.PREFIX_EPOCH, "");
-
-                    try {
-                        ids.add(Integer.parseInt(tmp));
-                    }
-                    catch (NumberFormatException nfe) {
-                        logger.warn("Cannot parse int from string: '" + tmp + "'");
-                    }
-                }
-            }
-
-            epochIDs = ids.toNativeArray();
-        }
-
-        return epochIDs;
-    }
-
-    /**
-     * Return the {@link BedHeight} at the height_id and time of the artifact
-     * @return {@link BedHeight}
-     */
-    public BedHeight getHeight() {
-        logger.debug("getHeight");
-        return BedHeightFactory.getHeight(
-            artifact.getDataAsString("type"),
-            Integer.parseInt(artifact.getDataAsString("height_id")),
-            Integer.parseInt(artifact.getDataAsString("time")));
-    }
-
-    /**
-     * Return a {@link List} of {@link BedHeightSingleValue}s
-     * at the range of the artifact
-     * @return List of {@link BedHeightSingleValue}s
-     */
-    public List<BedHeightSingleValue> getSingleValues() {
-        logger.debug("getSingleValues");
-        BedHeightSingle single = BedHeightSingle.getBedHeightSingleById(
-                artifact.getDataAsInteger("height_id"));
-        return BedHeightSingleValue.getBedHeightSingleValues(single,
-                getFrom(),
-                getTo());
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/access/BedQualityAccess.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,86 +0,0 @@
-package de.intevation.flys.artifacts.access;
-
-import java.util.Date;
-import java.util.LinkedList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.model.DateRange;
-
-
-/** Access data of artifact used in BedQuality calculations. */
-public class BedQualityAccess
-extends      RangeAccess {
-
-    private static final Logger logger = Logger
-        .getLogger(BedQualityAccess.class);
-
-    private List<String> bedDiameter;
-    private List<String> bedloadDiameter;
-    private List<DateRange> ranges;
-
-
-    public BedQualityAccess(FLYSArtifact artifact, CallContext context) {
-        super(artifact, context);
-    }
-
-    public List<DateRange> getDateRanges() {
-        if (ranges == null) {
-            ranges = extractRanges(getString("periods"));
-        }
-        return ranges;
-    }
-
-    public List<String> getBedDiameter() {
-        String value = getString("bed_diameter");
-        if (bedDiameter == null && value != null) {
-            bedDiameter = extractDiameter(value);
-        }
-        return bedDiameter;
-    }
-
-    public List<String> getBedloadDiameter() {
-        String value = getString("load_diameter");
-        if (bedloadDiameter == null && value != null) {
-            bedloadDiameter = extractDiameter(value);
-        }
-        return bedloadDiameter;
-    }
-
-    private List<DateRange> extractRanges(String dateString) {
-        List<DateRange> list = new LinkedList<DateRange>();
-        String[] dates = dateString.split(";");
-        for (String s : dates) {
-            String[] pair = s.split(",");
-            try {
-                long l1      = Long.parseLong(pair[0]);
-                long l2      = Long.parseLong(pair[1]);
-                Date first   = new Date(l1);
-                Date second  = new Date(l2);
-                DateRange dr = new DateRange(first, second);
-                list.add(dr);
-            }
-            catch (NumberFormatException nfe) {
-                continue;
-            }
-        }
-        return list;
-    }
-
-    private List<String> extractDiameter(String value) {
-        List<String> result = new LinkedList<String>();
-        String[] diameter = value.split(";");
-        for (String v : diameter) {
-            logger.debug("diameter: " + v);
-            String[] parts = v.split("\\.");
-            result.add(parts[parts.length - 1]);
-            logger.debug(parts[parts.length-1]);
-        }
-        return result;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/access/Calculation4Access.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,91 +0,0 @@
-package de.intevation.flys.artifacts.access;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.flys.artifacts.model.Segment;
-
-import java.util.Collections;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.flys.utils.DoubleUtil;
-
-public class Calculation4Access
-extends      RangeAccess
-{
-    private static Logger log = Logger.getLogger(Calculation4Access.class);
-
-    protected List<Segment> segments;
-
-    protected double [] fromToStep;
-
-    protected Boolean isQ;
-
-    protected Boolean isRange;
-
-
-    public Calculation4Access(FLYSArtifact artifact) {
-        super(artifact, null);
-    }
-
-    public List<Segment> getSegments() {
-        if (segments == null) {
-            String input = getString("wq_values");
-            if (input == null || (input = input.trim()).length() == 0) {
-                log.warn("no wq_values given");
-                segments = Collections.<Segment>emptyList();
-            }
-            else {
-                segments = Segment.parseSegments(input);
-            }
-        }
-        return segments;
-    }
-
-    public boolean isQ() {
-        if (isQ == null) {
-            Boolean value = getBoolean("wq_isq");
-            isQ = value != null && value;
-        }
-        return isQ;
-    }
-
-    public boolean isRange() {
-        if (isRange == null) {
-            String mode = getString("ld_mode");
-            isRange = mode == null || mode.equals("distance");
-        }
-        return isRange;
-    }
-
-    public double [] getFromToStep() {
-        if (fromToStep == null) {
-            // XXX: Is this really needed in this calculation?
-            if (!isRange()) {
-                return null;
-            }
-
-            // XXX: FLYSArtifact sucks!
-            // TODO further use RangeAccess functionality.
-            double [] fromTo = getKmRange();
-
-            if (fromTo == null) {
-                return null;
-            }
-
-            Double dStep = getDouble("ld_step");
-            if (dStep == null) {
-                return null;
-            }
-
-            fromToStep =  new double [] {
-                fromTo[0],
-                fromTo[1],
-                DoubleUtil.round(dStep / 1000d)
-            };
-        }
-        return fromToStep;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/access/ExtremeAccess.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,103 +0,0 @@
-package de.intevation.flys.artifacts.access;
-
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.flys.artifacts.model.RangeWithValues;
-
-import de.intevation.flys.utils.DoubleUtil;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-/** Access data used for extreme value analysis. */
-public class ExtremeAccess
-extends      RangeAccess
-{
-    /** Our private logger. */
-    private static Logger logger = Logger.getLogger(ExtremeAccess.class);
-
-    protected Long start;
-    protected Long end;
-
-    protected Double percent;
-
-    protected String function;
-
-    protected List<RangeWithValues> ranges;
-
-
-    public ExtremeAccess(FLYSArtifact artifact, CallContext context) {
-        super(artifact, context);
-    }
-
-
-    /** Returns the percent given. */
-    public Double getPercent() {
-
-        if (percent == null) {
-            percent = getDouble("percent");
-        }
-
-        if (logger.isDebugEnabled()) {
-            logger.debug("percent: '" + percent + "'");
-        }
-
-        return percent;
-    }
-
-    public String getFunction() {
-        if (function == null) {
-            function = getString("function");
-        }
-
-        if (logger.isDebugEnabled()) {
-            logger.debug("function: '" + function + "'");
-        }
-
-        return function;
-    }
-
-
-    /** Find first RangeWithValues for which the given location
-     * is within the range and return its values.
-     * @return values of first suitable rangewithvalues or null.
-     */
-    public double[] getValuesForRange(double location) {
-        logger.debug("ExtemeAcces.getValuesForRange");
-        for (RangeWithValues rangeValues: getRanges()) {
-            if (rangeValues.inside(location)) {
-                return rangeValues.getValues();
-            }
-        }
-        return null;
-    }
-
-
-    public List<RangeWithValues> getRanges() {
-
-        if (ranges == null) {
-            String rangesS = getString("ranges");
-            if (rangesS == null) {
-                return null;
-            }
-            ranges = new ArrayList<RangeWithValues>();
-            DoubleUtil.parseSegments(rangesS, new DoubleUtil.SegmentCallback() {
-                @Override
-                public void newSegment(double from, double to, double [] values) {
-                    ranges.add(new RangeWithValues(from, to, values));
-                }
-            });
-        }
-
-        if (logger.isDebugEnabled()) {
-            logger.debug("ranges: " + ranges);
-        }
-
-        return ranges;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/access/FixAccess.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,114 +0,0 @@
-package de.intevation.flys.artifacts.access;
-
-import de.intevation.artifacts.CallContext;
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import java.util.Arrays;
-
-import org.apache.log4j.Logger;
-
-/** Access for Fixation related data. */
-public class FixAccess
-extends      RangeAccess
-{
-    private static Logger log = Logger.getLogger(FixAccess.class);
-
-    protected Long start;
-    protected Long end;
-
-    protected Integer qSectorStart;
-    protected Integer qSectorEnd;
-
-    protected int [] events;
-
-    protected Boolean preprocessing;
-
-    protected String  function;
-
-    public FixAccess(FLYSArtifact artifact, CallContext context) {
-        super(artifact, context);
-    }
-
-    public Long getStart() {
-
-        if (start == null) {
-            start = getLong("start");
-        }
-
-        if (log.isDebugEnabled()) {
-            log.debug("start: '" + start + "'");
-        }
-
-        return start;
-    }
-
-    public Long getEnd() {
-
-        if (end == null) {
-            end = getLong("end");
-        }
-
-        if (log.isDebugEnabled()) {
-            log.debug("end: '" + end + "'");
-        }
-
-        return end;
-    }
-
-    public Integer getQSectorStart() {
-
-        if (qSectorStart == null) {
-            qSectorStart = getInteger("q1");
-        }
-
-        if (log.isDebugEnabled()) {
-            log.debug("q1: '" + qSectorStart + "'");
-        }
-
-        return qSectorStart;
-    }
-
-    public Integer getQSectorEnd() {
-
-        if (qSectorEnd == null) {
-            qSectorEnd = getInteger("q2");
-        }
-
-        if (log.isDebugEnabled()) {
-            log.debug("q2: '" + qSectorEnd + "'");
-        }
-
-        return qSectorEnd;
-    }
-
-    public int [] getEvents() {
-        if (events == null) {
-            events = getIntArray("events");
-        }
-        if (log.isDebugEnabled() && events != null) {
-            log.debug("events: " + Arrays.toString(events));
-        }
-        return events;
-    }
-
-    public Boolean getPreprocessing() {
-        if (preprocessing == null) {
-            preprocessing = getBoolean("preprocessing");
-        }
-        if (log.isDebugEnabled()) {
-            log.debug("preprocessing: " + preprocessing);
-        }
-        return preprocessing;
-    }
-
-    public String getFunction() {
-        if (function == null) {
-            function = getString("function");
-        }
-        if (log.isDebugEnabled()) {
-            log.debug("function: " + function);
-        }
-        return function;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/access/FixAnalysisAccess.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,103 +0,0 @@
-package de.intevation.flys.artifacts.access;
-
-import de.intevation.artifactdatabase.data.StateData;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.flys.artifacts.model.DateRange;
-
-import java.util.Arrays;
-import java.util.Date;
-
-import org.apache.log4j.Logger;
-
-public class FixAnalysisAccess
-extends      FixAccess
-{
-    private static Logger log = Logger.getLogger(FixAnalysisAccess.class);
-
-    protected DateRange    referencePeriod;
-    protected DateRange [] analysisPeriods;
-
-    protected double [] qs;
-
-    public FixAnalysisAccess(FLYSArtifact artifact, CallContext context) {
-        super(artifact, context);
-    }
-
-    public DateRange getReferencePeriod() {
-        if (referencePeriod == null) {
-            StateData refStart = artifact.getData("ref_start");
-            StateData refEnd   = artifact.getData("ref_end");
-
-            if (refStart == null || refEnd == null) {
-                log.warn("missing 'ref_start' or 'ref_start' value");
-                return null;
-            }
-
-            try {
-                long rs = Long.parseLong((String)refStart.getValue());
-                long re = Long.parseLong((String)refEnd  .getValue());
-
-                if (rs > re) { long t = rs; rs = re; re = t; }
-
-                Date from = new Date(rs);
-                Date to   = new Date(re);
-                referencePeriod = new DateRange(from, to);
-            }
-            catch (NumberFormatException nfe) {
-                log.warn("ref_start or ref_end is not an integer.");
-            }
-        }
-
-        return referencePeriod;
-    }
-
-    public DateRange [] getAnalysisPeriods() {
-        if (analysisPeriods == null) {
-            analysisPeriods = getDateRange("ana_data");
-        }
-
-        return analysisPeriods;
-    }
-
-    /**
-     * @return DateRange object ranging from eldest to youngest date
-     * of analysis and reference periods.
-     */
-    public DateRange getDateRange() {
-        DateRange refP = getReferencePeriod();
-
-        if (refP == null) {
-            return null;
-        }
-
-        Date from = refP.getFrom();
-        Date to   = refP.getTo();
-
-        DateRange[] rs = getAnalysisPeriods();
-        for (DateRange r: rs) {
-            if (r.getFrom().before(from)) {
-                from = r.getFrom();
-            }
-            if (r.getTo().after(to)) {
-                to = r.getTo();
-            }
-        }
-
-        return new DateRange(from, to);
-    }
-
-    public double [] getQs() {
-        if (qs == null) {
-            qs = getDoubleArray("qs");
-        }
-
-        if (log.isDebugEnabled() && qs != null) {
-            log.debug("qs: " + Arrays.toString(qs));
-        }
-        return qs;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/access/FixRealizingAccess.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-package de.intevation.flys.artifacts.access;
-
-import de.intevation.artifacts.CallContext;
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.flys.artifacts.model.Segment;
-
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-
-/** Fix-Realizing (Volmer/Ausgelagerte Wasserspiegellage) access. */
-public class FixRealizingAccess
-extends      FixAccess
-{
-    private static Logger log = Logger.getLogger(FixRealizingAccess.class);
-
-    protected Boolean isQ;
-
-    protected List<Segment> segments;
-
-    public FixRealizingAccess(FLYSArtifact artifact, CallContext context) {
-        super(artifact, context);
-    }
-
-    public Boolean isQ() {
-        if (isQ == null) {
-            isQ = getBoolean("wq_isq");
-        }
-
-        if (log.isDebugEnabled()) {
-            log.debug("isQ: " + isQ);
-        }
-
-        return isQ;
-    }
-
-    public List<Segment> getSegments() {
-        if (segments == null) {
-            String segmentsS = getString("wq_values");
-            if (segmentsS != null) {
-                segments = Segment.parseSegments(segmentsS);
-            }
-        }
-        if (log.isDebugEnabled()) {
-            log.debug("segments: " + segments);
-        }
-
-        return segments;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/access/FlowVelocityAccess.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-package de.intevation.flys.artifacts.access;
-
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-
-/** Access to data that deals with flow velocity stuff. */
-public class FlowVelocityAccess
-extends      RangeAccess
-{
-
-    private int[] mainChannels;
-    private int[] totalChannels;
-
-    public FlowVelocityAccess(FLYSArtifact artifact, CallContext context) {
-        super(artifact, context);
-    }
-
-
-    public int[] getMainChannels() {
-        if (mainChannels == null) {
-            mainChannels = getIntArray("main_channel");
-        }
-
-        return mainChannels;
-    }
-
-
-    public int[] getTotalChannels() {
-        if (totalChannels == null) {
-            totalChannels = getIntArray("total_channel");
-        }
-
-        return totalChannels;
-    }
-
-
-    public Double getLowerKM() {
-        // TODO update callers to getFrom
-        return getFrom();
-    }
-
-
-    public Double getUpperKM() {
-        // TODO update callers to getTo
-        return getTo();
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/access/GaugeAccess.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-package de.intevation.flys.artifacts.access;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.CallContext;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.utils.FLYSUtils;
-
-/** For the moment, light-weight wrapper around FLYSUtils. */
-// TODO employ 'Caching' like other Accesses, remove usage of FLYSUtils.
-public class GaugeAccess
-extends RangeAccess
-{
-    private static Logger logger = Logger.getLogger(GaugeAccess.class);
-
-    public GaugeAccess(FLYSArtifact artifact, CallContext context) {
-        super(artifact, context);
-    }
-
-
-    public String getGaugeName() {
-        return FLYSUtils.getGaugename(this.getArtifact());
-    }
-
-    // TODO there is also getGauges in FLYSUtils...
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/access/HistoricalDischargeAccess.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,109 +0,0 @@
-package de.intevation.flys.artifacts.access;
-
-import java.util.Date;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.model.Timerange;
-
-
-public class HistoricalDischargeAccess extends RiverAccess {
-
-    public static enum EvaluationMode {
-        W, Q
-    }
-
-    public static final String DATA_EVALUATION_TIME = "year_range";
-    public static final String DATA_EVALUATION_MODE = "historical_mode";
-    public static final String DATA_INPUT_VALUES = "historical_values";
-
-    private Timerange evaluationTimerange;
-    private EvaluationMode evaluationMode;
-
-    private double[] qs;
-    private double[] ws;
-
-    public HistoricalDischargeAccess(FLYSArtifact artifact) {
-        super(artifact);
-    }
-
-    /**
-     * This method returns the evaluation mode. The evaluation mode W is set, if
-     * the <b>DATA_EVALUATION_MODE</b> is 0. Otherwise, the evaluation mode Q is
-     * set.
-     *
-     * @return EvaluationMode.W if the parameter <i>historical_mode</i> is set
-     *         to 0, otherwise EvaluationMode.Q.
-     */
-    public EvaluationMode getEvaluationMode() {
-        if (evaluationMode == null) {
-            int mode = getInteger(DATA_EVALUATION_MODE);
-            evaluationMode = mode == 0 ? EvaluationMode.W : EvaluationMode.Q;
-        }
-
-        return evaluationMode;
-    }
-
-    /**
-     * This method returns the time range specified by <i>year_range</i>
-     * parameter. This parameter has to be a string that consists of two long
-     * values (time millis since 1970) separated by a ';'.
-     *
-     * @return the evaluation time range specified by <i>year_range</i>.
-     */
-    public Timerange getEvaluationTimerange() {
-        if (evaluationTimerange == null) {
-            long[] startend = getLongArray(DATA_EVALUATION_TIME);
-
-            if (startend != null && startend.length > 1) {
-                Date start = new Date(startend[0]);
-                Date end = new Date(startend[1]);
-
-                evaluationTimerange = new Timerange(start, end);
-            }
-        }
-
-        return evaluationTimerange;
-    }
-
-    /**
-     * This method returns the input Q values if the evaluation mode Q is set.
-     * Otherwise, this method will return a double array of length 0. The values
-     * returned by this method are extracted from string parameter
-     * <i>historical_values</i>.
-     *
-     * @return the input Q values or a double array of length 0.
-     */
-    public double[] getQs() {
-        if (qs == null) {
-            if (getEvaluationMode() == EvaluationMode.Q) {
-                qs = getDoubleArray(DATA_INPUT_VALUES);
-            }
-            else {
-                qs = new double[0];
-            }
-        }
-
-        return qs;
-    }
-
-    /**
-     * This method returns the input W values if the evaluation mode W is set.
-     * Otherwise, this method will return a double array of length 0. The values
-     * returned by this method are extracted from string parameter
-     * <i>historical_values</i>.
-     *
-     * @return the input W values or a double array of length 0.
-     */
-    public double[] getWs() {
-        if (ws == null) {
-            if (getEvaluationMode() == EvaluationMode.W) {
-                ws = getDoubleArray(DATA_INPUT_VALUES);
-            }
-            else {
-                ws = new double[0];
-            }
-        }
-
-        return ws;
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/access/MapAccess.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-package de.intevation.flys.artifacts.access;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import de.intevation.artifacts.CallContext;
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-
-public class MapAccess
-extends RangeAccess
-{
-
-    public MapAccess(FLYSArtifact artifact, CallContext context) {
-        super(artifact, context);
-    }
-
-    public List<String> getHWS() {
-        String param = getString("uesk.hws");
-        if (param != null) {
-            String[] split = param.split(";");
-            return new ArrayList<String>(Arrays.asList(split));
-        }
-        return new ArrayList<String>();
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/access/RangeAccess.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,193 +0,0 @@
-package de.intevation.flys.artifacts.access;
-
-import org.apache.log4j.Logger;
-
-import gnu.trove.TDoubleArrayList;
-import de.intevation.artifacts.CallContext;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.WINFOArtifact;
-
-import de.intevation.flys.utils.FLYSUtils;
-
-
-/** For the moment, light-weight wrapper around FLYSUtils. */
-// TODO employ 'Caching' like other Accesses, remove usage of FLYSUtils.
-public class RangeAccess
-extends RiverAccess
-{
-    private static Logger logger = Logger.getLogger(RangeAccess.class);
-
-    private CallContext context;
-
-    public static enum KM_MODE { RANGE, LOCATIONS, NONE };
-
-    double[] kmRange;
-
-    Double from;
-
-    Double to;
-
-    Double step;
-
-    private KM_MODE mode;
-
-
-    public RangeAccess(FLYSArtifact artifact, CallContext context) {
-        super(artifact);
-        this.context = context;
-    }
-
-
-    /** Evaluate the ld_mode data of artifact. */
-    public KM_MODE getKmRangeMode() {
-        if (mode != null) {
-            return mode;
-        }
-        String modeData = getString("ld_mode");
-
-        if (modeData == null || modeData.length() == 0) {
-            mode = KM_MODE.NONE;
-        }
-        else if (modeData.equals("distance"))  {
-            mode = KM_MODE.RANGE;
-        }
-        else if (modeData.equals("locations")) {
-            mode = KM_MODE.LOCATIONS;
-        }
-        else {
-            mode = KM_MODE.NONE;
-        }
-
-        return mode;
-    }
-
-    /**
-     * Return sorted array of locations at which stuff was calculated
-     * (from ld_locations data), null if not parameterized this way.
-     */
-    public double[] getLocations() {
-        String locationStr = getString("ld_locations");
-
-        if (locationStr == null || locationStr.length() == 0) {
-            if (getArtifact() instanceof WINFOArtifact) {
-                WINFOArtifact winfo = (WINFOArtifact) getArtifact();
-                if (winfo.getReferenceStartKm() != null && winfo.getReferenceEndKms() != null) {
-                    return new double[]
-                        {
-                            winfo.getReferenceStartKm().doubleValue(),
-                            winfo.getReferenceEndKms()[0]
-                        };
-                }
-                else if (winfo.getReferenceStartKm() != null) {
-                    return new double[]
-                        {
-                            winfo.getReferenceStartKm().doubleValue(),
-                            winfo.getReferenceStartKm().doubleValue()
-                        };
-                }
-            }
-            return null;
-        }
-
-        String[] tmp               = locationStr.split(" ");
-        TDoubleArrayList locations = new TDoubleArrayList();
-
-        for (String l: tmp) {
-            try {
-                locations.add(Double.parseDouble(l));
-            }
-            catch (NumberFormatException nfe) {
-                logger.debug(nfe.getLocalizedMessage(), nfe);
-            }
-        }
-
-        locations.sort();
-
-        return locations.toNativeArray();
-    }
-
-
-    /** Return ld_from data (in km). */
-    public double getFrom() {
-        if (from == null) {
-            from = getDouble("ld_from");
-        }
-
-        if (logger.isDebugEnabled()) {
-            logger.debug("from: '" + from + "'");
-        }
-
-        return from.doubleValue();
-    }
-
-
-    /** Return ld_to data (in km). */
-    public double getTo() {
-        if (to == null) {
-            to = getDouble("ld_to");
-        }
-
-        if (logger.isDebugEnabled()) {
-            logger.debug("to: '" + to + "'");
-        }
-
-        return to.doubleValue();
-    }
-
-
-    /** Step width for calculation. */
-    public Double getStep() {
-
-        if (step == null) {
-            step = getDouble("ld_step");
-        }
-
-        if (logger.isDebugEnabled()) {
-            logger.debug("step: '" + step + "'");
-        }
-
-        return step;
-    }
-
-
-    /**
-     * Get min and max kilometer, independent of parametization
-     * (ld_from/to vs ld_locations).
-     */
-    public double[] getKmRange() {
-        // TODO store kmRange in field.
-        switch (getKmRangeMode()) {
-            case RANGE: {
-                return getKmFromTo();
-            }
-
-            case LOCATIONS: {
-                double[] locs = getLocations();
-                // if no locations, nPE.
-                if (locs == null) {
-                    logger.warn("no locations to get km range from.");
-                    return new double[] { Double.NaN, Double.NaN };
-                }
-                return new double[] { locs[0], locs[locs.length-1] };
-            }
-
-            case NONE: {
-                double[] locs = getLocations();
-                if (locs != null) {
-                    return new double[] { locs[0], locs[locs.length-1] };
-                }
-                else {
-                    return getKmFromTo();
-                }
-            }
-        }
-
-        return new double[] { Double.NaN, Double.NaN };
-    }
-
-
-    public double[] getKmFromTo() {
-         return FLYSUtils.getKmFromTo(this.getArtifact());
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/access/RiverAccess.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +0,0 @@
-package de.intevation.flys.artifacts.access;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import org.apache.log4j.Logger;
-
-/** Access to river data of an artifact. */
-public class RiverAccess
-extends      Access
-{
-    /** Private logger. */
-    private static Logger log = Logger.getLogger(RiverAccess.class);
-
-    /** River name. */
-    protected String river;
-
-
-    public RiverAccess() {
-    }
-
-    public RiverAccess(FLYSArtifact artifact) {
-        super(artifact);
-    }
-
-
-    /** Get River name. */
-    public String getRiver() {
-        if (river == null) {
-            river = getString("river");
-        }
-        if (log.isDebugEnabled()) {
-            log.debug("river: '" + river + "'");
-        }
-        return river;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/access/SQRelationAccess.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,76 +0,0 @@
-package de.intevation.flys.artifacts.access;
-
-import java.util.Date;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.model.DateRange;
-
-public class SQRelationAccess
-extends      RiverAccess
-{
-    private static Logger log = Logger.getLogger(SQRelationAccess.class);
-
-    protected Double    location;
-
-    protected DateRange period;
-
-    protected Double    outliers;
-
-    private String      method;
-
-    public SQRelationAccess() {
-    }
-
-    public SQRelationAccess(FLYSArtifact artifact) {
-        super(artifact);
-    }
-
-    public Double getLocation() {
-        if (location == null) {
-            // XXX: The parameter name suggests plural!?
-            location = getDouble("ld_locations");
-        }
-
-        if (log.isDebugEnabled()) {
-            log.debug("location: " + location);
-        }
-
-        return location;
-    }
-
-    public DateRange getPeriod() {
-        if (period == null) {
-            Long start = getLong("start");
-            Long end   = getLong("end");
-
-            if (start != null && end != null) {
-                period = new DateRange(new Date(start), new Date(end));
-            }
-        }
-
-        return period;
-    }
-
-    public Double getOutliers() {
-        if (outliers == null) {
-            outliers = getDouble("outliers");
-        }
-        if (log.isDebugEnabled()) {
-            log.debug("outliers: " + outliers);
-        }
-        return outliers;
-    }
-
-    public String getOutlierMethod() {
-        if (method == null) {
-            method = getString("outlier-method");
-        }
-        if (log.isDebugEnabled()) {
-            log.debug("outlier-method: " + method);
-        }
-        return method;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/access/SedimentLoadAccess.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,98 +0,0 @@
-package de.intevation.flys.artifacts.access;
-
-import gnu.trove.TIntArrayList;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-
-public class SedimentLoadAccess
-extends      RangeAccess
-{
-    private static final Logger logger = Logger.getLogger(BedHeightAccess.class);
-
-    private int[] singleIDs;
-    private int[] epochIDs;
-
-    private String time;
-    private String unit;
-
-    public SedimentLoadAccess(FLYSArtifact artifact, CallContext context) {
-        super(artifact, context);
-    }
-
-    public Double getLowerKM() {
-        // TODO update callers
-        return getFrom();
-    }
-
-    public Double getUpperKM() {
-        // TODO update callers
-        return getTo();
-    }
-
-    public String getYearEpoch() {
-        if (time == null) {
-            time =  getString("ye_select");
-        }
-        return time;
-    }
-
-    public int[] getPeriod() {
-        if (getYearEpoch().equals("year") ) {
-            Integer start = getInteger("start");
-            Integer end = getInteger("end");
-            if (start == null || end == null) {
-                logger.warn("No 'start' or 'end' parameter specified!");
-                return null;
-            }
-
-            return new int[]{start.intValue(), end.intValue()};
-        }
-        return null;
-    }
-
-    public int[][] getEpochs() {
-        if (getYearEpoch().equals("epoch") ||
-            getYearEpoch().equals("off_epoch")
-        ) {
-            String data = getString("epochs");
-
-            if (data == null) {
-                logger.warn("No 'epochs' parameter specified!");
-                return null;
-            }
-
-            String[] parts = data.split(";");
-
-            int[][] list = new int[parts.length][];
-
-            for (int i = 0; i < parts.length; i++) {
-                String[] values = parts[i].split(",");
-                TIntArrayList ints = new TIntArrayList();
-                try {
-                    ints.add(Integer.parseInt(values[0]));
-                    ints.add(Integer.parseInt(values[1]));
-                    list[i] = ints.toNativeArray();
-                }
-                catch (NumberFormatException nfe) {
-                    logger.warn("Cannot parse int from string: '" + values + "'");
-                }
-            }
-            return list;
-        }
-
-        return null;
-    }
-
-    public String getUnit () {
-        if (unit == null) {
-            unit = getString("unit");
-        }
-        return unit;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/access/StaticSQRelationAccess.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,31 +0,0 @@
-package de.intevation.flys.artifacts.access;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-
-public class StaticSQRelationAccess
-extends RiverAccess
-{
-    /** The logger that is used in this state. */
-    private static final Logger logger =
-        Logger.getLogger(StaticSQRelationAccess.class);
-
-    private String measurementStation;
-
-    public StaticSQRelationAccess(FLYSArtifact artifact) {
-        super(artifact);
-    }
-
-    /** Get measurement station */
-    public String getMeasurementStation() {
-        if (measurementStation == null) {
-            measurementStation = getString("station");
-        }
-        if (logger.isDebugEnabled()) {
-            logger.debug("measurement station: '" + measurementStation + "'");
-        }
-        return measurementStation;
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/cache/CacheFactory.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,79 +0,0 @@
-package de.intevation.flys.artifacts.cache;
-
-import de.intevation.artifacts.common.utils.Config;
-
-import net.sf.ehcache.Cache;
-import net.sf.ehcache.CacheException;
-import net.sf.ehcache.CacheManager;
-
-import org.apache.log4j.Logger;
-
-public final class CacheFactory
-{
-    private static Logger log = Logger.getLogger(CacheFactory.class);
-
-    public static final String CACHE_CONFIG_FILE_PROPERTY =
-        "flys.artifacts.cache.config.file";
-
-    public static final String XPATH_CACHE_CONFIG_FILE =
-        "/artifact-database/cache/config-file/text()";
-
-    private CacheFactory() {
-    }
-
-    private static boolean      initialized;
-
-    private static CacheManager cacheManager;
-
-    public static final Cache getCache() {
-        return getCache(Cache.DEFAULT_CACHE_NAME);
-    }
-
-    public static final String getConfigFile() {
-        String configFile = System.getProperty(CACHE_CONFIG_FILE_PROPERTY);
-
-        if (configFile != null) {
-            return configFile;
-        }
-
-        configFile = Config.getStringXPath(XPATH_CACHE_CONFIG_FILE);
-
-        if (configFile != null) {
-            configFile = Config.replaceConfigDir(configFile);
-        }
-
-        return configFile;
-    }
-
-    public static final synchronized Cache getCache(String cacheName) {
-        if (!initialized) {
-            initialized = true; // try only once
-            String configFile = getConfigFile();
-            if (configFile != null) {
-                try {
-                    cacheManager = CacheManager.create(configFile);
-                    //System.setProperty(
-                    //  "net.sf.ehcache.enableShutdownHook", "true");
-                    Runtime.getRuntime().addShutdownHook(new Thread() {
-                        public void run() {
-                            log.info("shutting down caches");
-                            for (String name: cacheManager.getCacheNames()) {
-                                log.info("\tflushing '" + name + "'");
-                                cacheManager.getCache(name).flush();
-                            }
-                            cacheManager.shutdown();
-                        }
-                    });
-                }
-                catch (CacheException ce) {
-                    log.error("cannot configure cache", ce);
-                }
-            }
-        }
-
-        return cacheManager != null
-            ? cacheManager.getCache(cacheName)
-            : null;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/charts/CrossSectionApp.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,694 +0,0 @@
-package de.intevation.flys.artifacts.charts;
-
-import de.intevation.flys.backend.SessionFactoryProvider;
-
-import de.intevation.flys.artifacts.geom.Lines;
-
-import de.intevation.flys.model.CrossSection;
-import de.intevation.flys.model.CrossSectionLine;
-import de.intevation.flys.model.CrossSectionPoint;
-
-import de.intevation.flys.utils.Pair;
-
-import de.intevation.flys.jfree.StableXYDifferenceRenderer;
-
-import java.awt.BorderLayout;
-import java.awt.Dimension;
-import java.awt.FlowLayout;
-
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.awt.event.ItemEvent;
-import java.awt.event.ItemListener;
-
-import java.awt.geom.Point2D;
-import java.awt.geom.Rectangle2D;
-
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.PrintWriter;
-
-import java.math.BigDecimal;
-import java.math.MathContext;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-import java.util.TreeMap;
-
-import java.awt.Color;
-import java.awt.Paint;
-import java.awt.TexturePaint;
-
-import java.awt.image.BufferedImage;
-
-import javax.swing.DefaultComboBoxModel;
-import javax.swing.JButton;
-import javax.swing.JComboBox;
-import javax.swing.JPanel;
-import javax.swing.JScrollPane;
-import javax.swing.JTable;
-import javax.swing.JTextField;
-
-import javax.swing.event.TableModelEvent;
-import javax.swing.event.TableModelListener;
-
-import javax.swing.table.AbstractTableModel;
-
-import org.hibernate.Query;
-import org.hibernate.Session;
-
-import org.jfree.chart.ChartFactory;
-import org.jfree.chart.ChartPanel;
-import org.jfree.chart.ChartUtilities;
-import org.jfree.chart.JFreeChart;
-
-import org.jfree.chart.axis.NumberAxis;
-
-import org.jfree.chart.plot.PlotOrientation;
-import org.jfree.chart.plot.XYPlot;
-
-import org.jfree.data.xy.DefaultXYDataset;
-import org.jfree.data.xy.XYSeries;
-import org.jfree.data.xy.XYDataset;
-import org.jfree.data.xy.XYSeriesCollection;
-import org.jfree.chart.renderer.xy.XYItemRenderer;
-
-import org.jfree.ui.ApplicationFrame;
-import org.jfree.ui.RefineryUtilities;
-
-
-/**
- * Standalone tech-demo.
- */
-public class CrossSectionApp
-extends      ApplicationFrame
-{
-    public static final String RIVER = System.getProperty("river", "Saar");
-
-    public static final String WATER_LEVEL = System.getProperty("waterlevel");
-
-    public static final String KM = System.getProperty("km");
-
-    public static final double EPSILON = 1e-4;
-
-    protected Session session;
-
-    protected JComboBox crossSectionLinesCB;
-    protected JTextField waterlevelTF;
-
-    protected ChartPanel chartPanel;
-
-    protected Double lastWaterLevel;
-
-    protected List<CrossSection> crossSections;
-    protected boolean [] drawCrossSection;
-    protected boolean [] drawWaterLevel;
-    protected boolean [] drawGround;
-    protected boolean [] drawFill;
-
-    protected Map<Double, List<Pair<CrossSection, CrossSectionLine>>> km2lines;
-
-    protected static final Paint TRANSPARENT = createTransparentPaint();
-
-    public class CrossSectionTableModel extends AbstractTableModel {
-
-        @Override
-        public String getColumnName(int col) {
-            switch (col) {
-                case 0: return "Peilungsname";
-                case 1: return "Peilung";
-                case 2: return "Wasserstand";
-                case 3: return "Boden";
-                case 4: return "Wasser";
-            }
-            return "";
-        }
-
-        @Override
-        public int getColumnCount() {
-            return 5;
-        }
-
-        @Override
-        public int getRowCount() {
-            return crossSections != null ? crossSections.size() : 0;
-        }
-
-        @Override
-        public Object getValueAt(int row, int col) {
-            if (crossSections == null) return null;
-            switch (col) {
-                case 0: return crossSections.get(row).getDescription();
-                case 1: return drawCrossSection[row];
-                case 2: return drawWaterLevel[row];
-                case 3: return drawGround[row];
-                case 4: return drawFill[row];
-            }
-            return null;
-        }
-
-        @Override
-        public void setValueAt(Object value, int row, int col) {
-            switch (col) {
-                case 1:
-                    if (change(drawCrossSection, row, (Boolean)value)) {
-                        fireTableCellUpdated(row, col);
-                    }
-                    break;
-                case 2:
-                    if (change(drawWaterLevel, row, (Boolean)value)) {
-                        fireTableCellUpdated(row, col);
-                    }
-                    break;
-                case 3:
-                    if (change(drawGround, row, (Boolean)value)) {
-                        fireTableCellUpdated(row, col);
-                    }
-                    break;
-                case 4:
-                    if (change(drawFill, row, (Boolean)value)) {
-                        fireTableCellUpdated(row, col);
-                    }
-                    break;
-            }
-        }
-
-        @Override
-        public Class<?> getColumnClass(int columnIndex) {
-            switch (columnIndex) {
-                case 0: return String.class;
-                case 1:
-                case 2:
-                case 3:
-                case 4: return Boolean.class;
-            }
-            return null;
-        }
-
-        @Override
-        public boolean isCellEditable(
-            int rowIndex,
-            int columnIndex
-        ) {
-            return columnIndex >= 1 && columnIndex <= 4;
-        }
-    } // class CrossSectionTableModel
-
-    private static boolean change(
-        boolean [] values,
-        int        index,
-        boolean    value
-    ) {
-        if (values[index] != value) {
-            values[index] = value;
-            return true;
-        }
-        return false;
-    }
-
-    public static class CrossSectionLineItem {
-
-        Double km;
-        List<Pair<CrossSection, CrossSectionLine>> lines;
-
-        public CrossSectionLineItem(
-            Double km,
-            List<Pair<CrossSection, CrossSectionLine>> lines
-        ) {
-            this.km    = km;
-            this.lines = lines;
-        }
-
-        public String toString() {
-            return String.valueOf(km);
-        }
-    } // CrossSectionLineItem
-
-    public CrossSectionApp(String title) {
-        super(title);
-
-        session = SessionFactoryProvider
-            .createSessionFactory()
-            .openSession();
-
-        JPanel content = createContent();
-        content.setPreferredSize(new Dimension(800, 480));
-        setContentPane(content);
-    }
-
-
-    /** Query and return CrossSections for named river. */
-    public List<CrossSection> crossSections(String river) {
-        Query query = session.createQuery(
-            "from CrossSection where river.name = :river");
-        query.setParameter("river", river);
-        return query.list();
-    }
-
-    protected Map<Double, List<Pair<CrossSection, CrossSectionLine>>>
-        loadAllLines(List<CrossSection> crossSections) {
-        Map<Double, List<Pair<CrossSection, CrossSectionLine>>> km2lines =
-            new TreeMap<Double, List<Pair<CrossSection, CrossSectionLine>>>();
-        for (CrossSection cs: crossSections) {
-            List<CrossSectionLine> lines = cs.getLines();
-            for (CrossSectionLine csl: lines) {
-                Double km = Math.round(csl.getKm().doubleValue() * 1000d)/1000d;
-                List<Pair<CrossSection, CrossSectionLine>> ls
-                    = km2lines.get(km);
-                if (ls == null) {
-                    ls = new ArrayList<Pair<CrossSection, CrossSectionLine>>(2);
-                    km2lines.put(km, ls);
-                }
-                ls.add(new Pair<CrossSection, CrossSectionLine>(cs, csl));
-            }
-        }
-        return km2lines;
-    }
-
-    public JPanel createContent() {
-        JPanel panel = new JPanel(new BorderLayout());
-
-        JPanel nav = new JPanel(new FlowLayout());
-
-        crossSections = crossSections(RIVER);
-        km2lines = loadAllLines(crossSections);
-
-        int CS = crossSections.size();
-        Arrays.fill(drawCrossSection = new boolean[CS], true);
-        drawWaterLevel = new boolean[CS];
-        drawGround     = new boolean[CS];
-        drawFill       = new boolean[CS];
-
-        Object [] clis = createCrossSectionLineItems(km2lines);
-
-        DefaultComboBoxModel dcbm = new DefaultComboBoxModel(clis);
-
-        crossSectionLinesCB = new JComboBox(dcbm);
-
-        if (KM != null) {
-            try {
-                double km = Double.parseDouble(KM);
-
-                CrossSectionLineItem found = null;
-
-                for (Object o: clis) {
-                    CrossSectionLineItem csli = (CrossSectionLineItem)o;
-                    if (Math.abs(csli.km - km) < EPSILON) {
-                        found = csli;
-                        break;
-                    }
-                }
-
-                if (found != null) {
-                    crossSectionLinesCB.setSelectedItem(found);
-                }
-            }
-            catch (NumberFormatException nfe) {
-                System.err.println("km is not a number: "
-                    + nfe.getMessage());
-            }
-        }
-
-        nav.add(crossSectionLinesCB);
-
-        crossSectionLinesCB.addItemListener(new ItemListener() {
-            @Override
-            public void itemStateChanged(ItemEvent ie) {
-                if (ie.getStateChange() == ItemEvent.SELECTED) {
-                    updateChart();
-                }
-            }
-        });
-
-        waterlevelTF = new JTextField(5);
-
-        if (WATER_LEVEL != null) {
-            try {
-                waterlevelTF.setText(
-                    (lastWaterLevel = Double.valueOf(WATER_LEVEL)).toString());
-            }
-            catch (NumberFormatException nfe) {
-                System.err.println("Water level not a number: " +
-                    nfe.getMessage());
-            }
-        }
-
-        waterlevelTF.addActionListener(new ActionListener() {
-            @Override
-            public void actionPerformed(ActionEvent ae) {
-                waterLevelChanged();
-            }
-        });
-
-        nav.add(waterlevelTF);
-
-        JButton dump = new JButton("dump");
-
-        dump.addActionListener(new ActionListener() {
-            @Override
-            public void actionPerformed(ActionEvent ae) {
-                dumpData();
-            }
-        });
-
-        nav.add(dump);
-
-
-        chartPanel = createChartPanel();
-
-        panel.add(chartPanel, BorderLayout.CENTER);
-
-
-        CrossSectionTableModel cstm = new CrossSectionTableModel();
-
-        cstm.addTableModelListener(new TableModelListener() {
-            @Override
-            public void tableChanged(TableModelEvent e) {
-                updateChart();
-            }
-        });
-
-        JTable crossTable = new JTable(cstm);
-
-        JPanel west = new JPanel(new BorderLayout());
-        JScrollPane scrollPane = new JScrollPane(crossTable);
-        west.add(scrollPane);
-
-        west.add(nav, BorderLayout.SOUTH);
-
-        panel.add(west, BorderLayout.WEST);
-
-        return panel;
-    }
-
-    protected void waterLevelChanged() {
-        String value = waterlevelTF.getText();
-        try {
-            lastWaterLevel = Double.valueOf(value);
-        }
-        catch (NumberFormatException nfe) {
-            waterlevelTF.setText(
-                lastWaterLevel != null ? lastWaterLevel.toString() : "");
-            return;
-        }
-        updateChart();
-    }
-
-    protected void updateChart() {
-
-        JFreeChart chart = createChart();
-
-        chartPanel.setChart(chart);
-    }
-
-    protected ChartPanel createChartPanel() {
-
-        JFreeChart chart = createChart();
-
-        return new ChartPanel(chart);
-    }
-
-    protected void dumpData() {
-
-        CrossSectionLineItem csli =
-            (CrossSectionLineItem)crossSectionLinesCB.getSelectedItem();
-
-        if (csli == null) {
-            return;
-        }
-
-        double km = Math.round(csli.km.doubleValue() * 1000d)/1000d;
-
-        String kmS = String.valueOf(km).replace(".", "-");
-
-        int i = 1;
-        File file = new File("cross-section-" + kmS + ".txt");
-        while (file.exists()) {
-            file = new File("cross-section-" + kmS + "[" + (i++) + "].txt");
-        }
-
-        System.err.println("dump points to file '" + file + "'");
-
-        PrintWriter out = null;
-
-        MathContext mc = new MathContext(3);
-
-        try {
-            out =
-                new PrintWriter(
-                new FileWriter(file));
-
-            for (Pair<CrossSection, CrossSectionLine> pair: csli.lines) {
-                out.println("# " + pair.getA().getDescription());
-                for (CrossSectionPoint point: pair.getB().getPoints()) {
-                    out.println(
-                        new BigDecimal(point.getX()).round(mc) + " " +
-                        new BigDecimal(point.getY()).round(mc));
-                }
-            }
-
-            out.flush();
-        }
-        catch (IOException ioe) {
-            ioe.printStackTrace();
-        }
-        finally {
-            if (out != null) {
-                out.close();
-            }
-        }
-    }
-
-    public void generateWaterLevels(
-        List<Point2D>                         points,
-        List<Pair<XYDataset, XYItemRenderer>> datasets
-    ) {
-        if (points == null || points.isEmpty() || lastWaterLevel == null) {
-            return;
-        }
-
-        double [][] data = Lines.createWaterLines(points, lastWaterLevel).points;
-        XYSeries series =
-            new XYSeries(String.valueOf(lastWaterLevel), false);
-
-        double [] x = data[0];
-        double [] y = data[1];
-        for (int i = 0; i < x.length; ++i) {
-            series.add(x[i], y[i], false);
-        }
-
-        datasets.add(new Pair<XYDataset, XYItemRenderer>(
-            new XYSeriesCollection(series), null));
-    }
-
-    public void generateFill(
-        List<Point2D>                         points,
-        String                                legend,
-        List<Pair<XYDataset, XYItemRenderer>> datasets
-    ) {
-        if (points == null || points.isEmpty() || lastWaterLevel == null) {
-            return;
-        }
-
-        double [][] data   = Lines.createWaterLines(points, lastWaterLevel).points;
-        double [][] values = CrossSectionLine.fetchCrossSectionProfile(points);
-
-        DefaultXYDataset dataset = new DefaultXYDataset();
-
-        dataset.addSeries(legend + "-Linie", values);
-        dataset.addSeries(legend + "-Fl\u00e4che", data);
-
-        datasets.add(new Pair<XYDataset, XYItemRenderer>(
-            dataset,
-            new StableXYDifferenceRenderer(
-                TRANSPARENT, Color.blue, false)));
-    }
-
-    public void generateProfile(
-        List<Point2D>                         points,
-        String                                legend,
-        List<Pair<XYDataset, XYItemRenderer>> datasets
-    ) {
-        if (points == null || points.isEmpty()) {
-            return;
-        }
-
-        double [][] values = CrossSectionLine.fetchCrossSectionProfile(points);
-
-        XYSeries series = new XYSeries(legend, false);
-
-        double [] x = values[0];
-        double [] y = values[1];
-        for (int i = 0; i < x.length; ++i) {
-            series.add(x[i], y[i], false);
-        }
-
-        datasets.add(new Pair<XYDataset, XYItemRenderer>(
-            new XYSeriesCollection(series), null));;
-    }
-
-
-    /**
-     * @param legend the legend entry.
-     */
-    public void generateGround(
-        List<Point2D>                         points,
-        String                                legend,
-        List<Pair<XYDataset, XYItemRenderer>> datasets
-    ) {
-        if (points == null || points.isEmpty()) {
-            return;
-        }
-
-        double [][] values = CrossSectionLine.fetchCrossSectionProfile(points);
-
-        DefaultXYDataset dataset = new DefaultXYDataset();
-
-        dataset.addSeries(legend, values);
-
-        StableXYDifferenceRenderer renderer =
-            new StableXYDifferenceRenderer();
-
-        datasets.add(new Pair<XYDataset, XYItemRenderer>(
-            dataset, renderer));
-    }
-
-    public List<Pair<XYDataset, XYItemRenderer>> generateDatasets() {
-
-        List<Pair<XYDataset, XYItemRenderer>> datasets =
-            new ArrayList<Pair<XYDataset, XYItemRenderer>>();
-
-        CrossSectionLineItem csli =
-            (CrossSectionLineItem)crossSectionLinesCB.getSelectedItem();
-
-        for (int i = 0; i < drawCrossSection.length; ++i) {
-            List<Point2D> points = null;
-            CrossSection cs = crossSections.get(i);
-
-            if (drawGround[i]) {
-                for (Pair<CrossSection, CrossSectionLine> csl: csli.lines) {
-                    if (csl.getA() == cs) {
-                        if (points == null) {
-                            points = csl.getB().fetchCrossSectionLinesPoints();
-                        }
-                        generateGround(
-                            points,
-                            cs.getDescription() + "/Boden",
-                            datasets);
-                        break;
-                    }
-                }
-            }
-
-            if (drawFill[i]) {
-                for (Pair<CrossSection, CrossSectionLine> csl: csli.lines) {
-                    if (csl.getA() == cs) {
-                        if (points == null) {
-                            points = csl.getB().fetchCrossSectionLinesPoints();
-                        }
-
-                        generateFill(
-                            points, cs.getDescription(), datasets);
-                        break;
-                    }
-                }
-            }
-
-            if (drawCrossSection[i]) {
-                for (Pair<CrossSection, CrossSectionLine> csl: csli.lines) {
-                    if (csl.getA() == cs) {
-                        if (points == null) {
-                            points = csl.getB().fetchCrossSectionLinesPoints();
-                        }
-
-                        generateProfile(
-                            points, cs.getDescription(), datasets);
-                        break;
-                    }
-                }
-            }
-
-            if (drawWaterLevel[i]) {
-                for (Pair<CrossSection, CrossSectionLine> csl: csli.lines) {
-                    if (csl.getA() == cs) {
-                        if (points == null) {
-                            points = csl.getB().fetchCrossSectionLinesPoints();
-                        }
-                        generateWaterLevels(points, datasets);
-                        break;
-                    }
-                }
-            }
-
-        }
-
-        return datasets;
-    }
-
-    protected Object [] createCrossSectionLineItems(
-        Map<Double, List<Pair<CrossSection, CrossSectionLine>>> km2lines
-    ) {
-        Object [] result = new Object[km2lines.size()];
-        int i = 0;
-        for (Map.Entry<Double, List<Pair<CrossSection, CrossSectionLine>>> entry:
-            km2lines.entrySet()) {
-            result[i++] = new CrossSectionLineItem(
-                entry.getKey(),
-                entry.getValue());
-        }
-        return result;
-    }
-
-
-    public JFreeChart createChart() {
-        JFreeChart chart = ChartFactory.createXYLineChart(
-            null,
-            "Abstand [m]",
-            "H\u00f6he [m]",
-            null,
-            PlotOrientation.VERTICAL,
-            true,
-            true,
-            false);
-
-        List<Pair<XYDataset, XYItemRenderer>> datasets =
-            generateDatasets();
-
-        XYPlot plot = chart.getXYPlot();
-
-        for (int i = 0, N = datasets.size(); i < N; ++i) {
-            Pair<XYDataset, XYItemRenderer> p = datasets.get(i);
-            plot.setDataset(i, p.getA());
-            plot.mapDatasetToRangeAxis(i, 0);
-            XYItemRenderer renderer = p.getB();
-            if (renderer != null) {
-                plot.setRenderer(i, renderer);
-            }
-        }
-
-        NumberAxis yAxis = (NumberAxis)plot.getRangeAxis();
-        yAxis.setAutoRangeIncludesZero(false);
-
-        ChartUtilities.applyCurrentTheme(chart);
-        return chart;
-    }
-
-    protected static Paint createTransparentPaint() {
-        BufferedImage texture = new BufferedImage(
-            1, 1, BufferedImage.TYPE_4BYTE_ABGR);
-
-        return new TexturePaint(
-            texture, new Rectangle2D.Double(0d, 0d, 0d, 0d));
-    }
-
-    public static void main(String [] args) {
-        CrossSectionApp csa = new CrossSectionApp("Querprofile");
-        csa.pack();
-        RefineryUtilities.centerFrameOnScreen(csa);
-        csa.setVisible(true);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/charts/TimeseriesStepChart.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,219 +0,0 @@
-package de.intevation.flys.artifacts.charts;
-
-import java.awt.Dimension;
-import java.util.Date;
-import java.util.GregorianCalendar;
-
-import org.jfree.chart.ChartFactory;
-import org.jfree.chart.ChartPanel;
-import org.jfree.chart.JFreeChart;
-import org.jfree.ui.ApplicationFrame;
-
-import org.jfree.data.time.SimpleTimePeriod;
-import org.jfree.data.time.Second;
-import org.jfree.data.time.TimeSeries;
-import org.jfree.data.time.TimeSeriesCollection;
-import org.jfree.data.time.TimePeriodValues;
-import org.jfree.data.time.TimePeriodValuesCollection;
-
-import org.apache.log4j.Logger;
-import org.apache.log4j.PropertyConfigurator;
-
-
-public class TimeseriesStepChart extends ApplicationFrame {
-
-    private static final Logger logger =
-        Logger.getLogger(TimeseriesStepChart.class);
-
-
-    public static final String LOG4J_PROPERTIES = "FLYS_CLIENT_LOG4J_PROPERIES";
-
-    public static final int USER_CHART_TYPE =
-        Integer.getInteger("chart.type", -1);
-
-    public static final int CHART_TYPE_TIMEPERIOD = 1;
-    public static final int CHART_TYPE_TIMESERIES = 2;
-
-
-    public static void main(String[] args) {
-        configureLogging();
-
-        logger.info("Start TimeseriesStepChart demo.");
-
-        if (USER_CHART_TYPE <= 0) {
-            logger.error("Unknown chart type: " + USER_CHART_TYPE);
-        }
-
-        logger.debug("Create Chart from type: " + USER_CHART_TYPE);
-
-        TimeseriesStepChart tsc = new TimeseriesStepChart();
-        tsc.build(USER_CHART_TYPE);
-
-        logger.info("End of TimeseriesStepChart demo.");
-    }
-
-
-    /** Setup the log4j logging (read LOG4J_PROPERIES envvar). */
-    public static void configureLogging() {
-        String log4jProperties = System.getenv(LOG4J_PROPERTIES);
-
-        if (log4jProperties != null && log4jProperties.length() > 0) {
-            PropertyConfigurator.configure(log4jProperties);
-            logger.info("Log4J logging initialized.");
-        }
-        else {
-            System.out.println("Error while setting up Log4J configuration.");
-        }
-
-        System.out.println("LoggingConfigurator.init finished");
-    }
-
-
-    public TimeseriesStepChart() {
-        super("TimeseriesStepChart");
-    }
-
-
-    public void build(int type) {
-        ChartPanel chartPanel = createChartPanel(type);
-        chartPanel.setPreferredSize(new Dimension(500, 300));
-
-        setContentPane(chartPanel);
-
-        pack();
-        setVisible(true);
-    }
-
-
-    protected ChartPanel createChartPanel(int type) {
-        JFreeChart chart = createJFreeChart(type);
-        ChartPanel panel = new ChartPanel(chart);
-        return panel;
-    }
-
-
-    protected JFreeChart createJFreeChart(int type) {
-        if (type == CHART_TYPE_TIMEPERIOD) {
-            return createTimePeriodValuesChart();
-        }
-        else if (type == CHART_TYPE_TIMESERIES) {
-            return createTimeSeriesChart();
-        }
-
-        logger.error("Unknown chart type: " + type);
-
-        return null;
-    }
-
-
-    public static Date createDate(int year, int month, int day) {
-        GregorianCalendar cal = new GregorianCalendar(year, month, day);
-        return cal.getTime();
-    }
-
-
-
-    //
-    // XXX FOLLOWING METHODS ARE USED FOR "TimePeriodValues" CHART TYPE
-    //
-    protected JFreeChart createTimePeriodValuesChart() {
-        return ChartFactory.createTimeSeriesChart(
-            "TimePeriodValues Chart",
-            "Time Axis",
-            "Y Axis",
-            createTimePeriodValuesDataset(),
-            true,
-            false,
-            false);
-    }
-
-
-    protected TimePeriodValuesCollection createTimePeriodValuesDataset() {
-        TimePeriodValuesCollection dataset = new TimePeriodValuesCollection();
-        TimePeriodValues series = createTimePeriodValues();
-
-        dataset.addSeries(series);
-
-        return dataset;
-    }
-
-
-    protected TimePeriodValues createTimePeriodValues() {
-        TimePeriodValues series1 = new TimePeriodValues("Series 1");
-
-        Date start1 = createDate(2000, 0, 1);
-        Date end1   = createDate(2000, 11, 31);
-
-        logger.debug("START DATE 1 = " + start1);
-        logger.debug("END   DATE 1 = " + end1);
-
-        SimpleTimePeriod period1 = new SimpleTimePeriod(start1, end1);
-
-        Date start2 = createDate(2001, 0, 1);
-        Date end2   = createDate(2001, 11, 31);
-        SimpleTimePeriod period2 = new SimpleTimePeriod(start2, end2);
-
-        Date start3 = createDate(2002, 0, 1);
-        Date end3   = createDate(2002, 11, 31);
-        SimpleTimePeriod period3 = new SimpleTimePeriod(start3, end3);
-
-        series1.add(period1, 100);
-        series1.add(period2, 200);
-        series1.add(period3, 150);
-
-        return series1;
-    }
-
-
-    //
-    // XXX FOLLOWING METHODS ARE USED FOR "TimePeriodValues" CHART TYPE
-    //
-    protected JFreeChart createTimeSeriesChart() {
-        return ChartFactory.createTimeSeriesChart(
-            "TimeSeriesCollection Chart",
-            "Time Axis",
-            "Y Axis",
-            createTimeSeriesCollectionDataset(),
-            true,
-            false,
-            false);
-    }
-
-
-    protected TimeSeriesCollection createTimeSeriesCollectionDataset() {
-        TimeSeriesCollection dataset = new TimeSeriesCollection();
-        TimeSeries[]         series  = createTimeSeries();
-
-        for (TimeSeries tmp: series) {
-            dataset.addSeries(tmp);
-        }
-
-        return dataset;
-    }
-
-
-    protected TimeSeries[] createTimeSeries() {
-        TimeSeries series1 = new TimeSeries("Series 1");
-        series1.add(new Second(0, 0, 0, 1, 1, 2000), 100);
-        series1.add(new Second(59, 59, 23, 31, 12, 2000), 100);
-
-        series1.add(new Second(0, 0, 0, 1, 1, 2001), 200);
-        series1.add(new Second(59, 59, 23, 31, 12, 2001), 200);
-
-        series1.add(new Second(0, 0, 0, 1, 1, 2002), 150);
-        series1.add(new Second(59, 59, 23, 31, 12, 2002), 150);
-
-        TimeSeries series2 = new TimeSeries("Series 2");
-        series2.add(new Second(0, 0, 0, 1, 1, 2000), 10);
-        series2.add(new Second(59, 59, 23, 31, 12, 2000), 10);
-
-        series2.add(new Second(0, 0, 0, 1, 1, 2001), 20);
-        series2.add(new Second(59, 59, 23, 31, 12, 2001), 20);
-
-        series2.add(new Second(0, 0, 0, 1, 1, 2002), 15);
-        series2.add(new Second(59, 59, 23, 31, 12, 2002), 15);
-
-        return new TimeSeries[] { series1, series2 };
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/context/FLYSContext.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,116 +0,0 @@
-package de.intevation.flys.artifacts.context;
-
-import java.util.Map;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Document;
-
-import de.intevation.artifactdatabase.DefaultArtifactContext;
-import de.intevation.artifacts.CallContext;
-import de.intevation.flys.exports.OutGenerator;
-
-
-/**
- * This class is used to store application wide information in a global context.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class FLYSContext extends DefaultArtifactContext {
-
-    /** The logger used in this class. */
-    private static Logger logger = Logger.getLogger(FLYSContext.class);
-
-    /** The key that is used to store the StateEngine in the context. */
-    public static final String ARTIFACT_KEY =
-        "artifact";
-
-    /** The key that is used to store the TransitionEngine in the context. */
-    public static final String TRANSITION_ENGINE_KEY =
-        "artifact.transition.engine";
-
-    /** The key that is used to store the StateEngine in the context. */
-    public static final String STATE_ENGINE_KEY =
-        "artifact.state.engine";
-
-    /** The key that is used to store the Map of OutGenerator classes in the
-     * context. */
-    public static final String OUTGENERATORS_KEY =
-        "flys.export.outgenerators";
-
-    /** The key that is used to store the map of themes in the context. */
-    public static final String THEMES =
-        "flys.themes.map";
-
-    /** The key that is used to store a map of theme mappings in the context. */
-    public static final String THEME_MAPPING =
-        "flys.themes.mapping.map";
-
-    /** The key that is used to store a map of WMS urls for each river. */
-    public static final String RIVER_WMS =
-        "flys.floodmap.river.wms";
-
-    /** The key that is used to store an instance of Scheduler in the context.*/
-    public static final String SCHEDULER =
-        "flys.wsplgen.scheduler";
-
-    /** Key to store the configured modules in the context. */
-    public static final String MODULES = "flys.modules";
-
-
-    /**
-     * The default constructor.
-     */
-    public FLYSContext() {
-        super();
-    }
-
-
-    /**
-     * A constructor with a config document.
-     */
-    public FLYSContext(Document config) {
-        super(config);
-    }
-
-    /**
-     * Returns the OutGenerator for a specified <i>type</i>.
-     *
-     * @param name The name of the output type.
-     * @param type Defines the type of the desired OutGenerator.
-     *
-     * @return Instance of an OutGenerator for specified <i>type</i>.
-     */
-    public static OutGenerator getOutGenerator(
-        CallContext context,
-        String      name,
-        String      type)
-    {
-
-        FLYSContext flysContext = context instanceof FLYSContext
-            ? (FLYSContext) context
-            : (FLYSContext) context.globalContext();
-
-        Map<String, Class> generators = (Map<String, Class>)
-            flysContext.get(FLYSContext.OUTGENERATORS_KEY);
-
-        if (generators == null) {
-            return null;
-        }
-
-        Class clazz = generators.get(name);
-
-        try {
-            return clazz != null ? (OutGenerator) clazz.newInstance() : null;
-        }
-        catch (InstantiationException ie) {
-            logger.error(ie, ie);
-        }
-        catch (IllegalAccessException iae) {
-            logger.error(iae, iae);
-        }
-
-        return null;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/context/FLYSContextFactory.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,521 +0,0 @@
-package de.intevation.flys.artifacts.context;
-
-import de.intevation.artifactdatabase.state.State;
-import de.intevation.artifactdatabase.state.StateEngine;
-import de.intevation.artifactdatabase.transition.Transition;
-import de.intevation.artifactdatabase.transition.TransitionEngine;
-import de.intevation.artifacts.ArtifactContextFactory;
-import de.intevation.artifacts.GlobalContext;
-import de.intevation.artifacts.common.utils.Config;
-import de.intevation.artifacts.common.utils.XMLUtils;
-import de.intevation.flys.artifacts.model.Module;
-import de.intevation.flys.artifacts.model.ZoomScale;
-import de.intevation.flys.artifacts.states.StateFactory;
-import de.intevation.flys.artifacts.transitions.TransitionFactory;
-import de.intevation.flys.themes.Theme;
-import de.intevation.flys.themes.ThemeFactory;
-import de.intevation.flys.themes.ThemeGroup;
-import de.intevation.flys.themes.ThemeMapping;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.xml.xpath.XPathConstants;
-
-import org.apache.log4j.Logger;
-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.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class FLYSContextFactory implements ArtifactContextFactory {
-
-    /** The logger that is used in this class. */
-    private static Logger logger = Logger.getLogger(FLYSContextFactory.class);
-
-    /** The XPath to the artifacts configured in the configuration. */
-    public static final String XPATH_ARTIFACTS =
-        "/artifact-database/artifacts/artifact";
-
-    /** The XPath to the name of the artifact. */
-    public static final String XPATH_ARTIFACT_NAME = "/artifact/@name";
-
-    /** The XPath to the xlink ref in an artifact configuration. */
-    public static final String XPATH_XLINK = "xlink:href";
-
-    /** The XPath to the transitions configured in the artifact config. */
-    public static final String XPATH_TRANSITIONS =
-        "/artifact/states/transition";
-
-    /** The XPath to the states configured in the artifact config. */
-    public static final String XPATH_STATES =
-        "/artifact/states/state";
-
-    public static final String XPATH_OUTPUT_GENERATORS =
-        "/artifact-database/output-generators/output-generator";
-
-    public static final String XPATH_THEME_CONFIG =
-        "/artifact-database/flys/themes/configuration/text()";
-
-    public static final String XPATH_THEMES =
-        "theme";
-
-    public static final String XPATH_THEME_GROUPS =
-        "/themes/themegroup";
-
-    public static final String XPATH_THEME_MAPPINGS =
-        "/themes/mappings/mapping";
-
-    public static final String XPATH_RIVER_WMS =
-        "/artifact-database/floodmap/river";
-
-    public static final String XPATH_MODULES = "/artifact-database/modules/module";
-
-    private static final String XPATH_ZOOM_SCALES = "/artifact-database/options/zoom-scales/zoom-scale";
-
-    private static final String XPATH_DGM_PATH = "/artifact-database/options/dgm-path/text()";
-
-
-    /**
-     * Creates a new FLYSArtifactContext object and initialize all
-     * components required by the application.
-     *
-     * @param config The artifact server configuration.
-     * @return a FLYSArtifactContext.
-     */
-    @Override
-    public GlobalContext createArtifactContext(Document config) {
-        FLYSContext context = new FLYSContext(config);
-
-        configureTransitions(config, context);
-        configureStates(config, context);
-        configureOutGenerators(config, context);
-        configureThemes(config, context);
-        configureThemesMappings(config, context);
-        configureFloodmapWMS(config, context);
-        configureModules(config, context);
-        configureZoomScales(config, context);
-        configureDGMPath(config, context);
-
-        return context;
-    }
-
-
-    private void configureDGMPath(Document config, FLYSContext context) {
-        String dgmPath = (String) XMLUtils.xpath(
-            config,
-            XPATH_DGM_PATH,
-            XPathConstants.STRING);
-
-        context.put("dgm-path", dgmPath);
-    }
-
-
-    protected void configureZoomScales(Document config, FLYSContext context) {
-        NodeList list = (NodeList)XMLUtils.xpath(
-            config,
-            XPATH_ZOOM_SCALES,
-            XPathConstants.NODESET);
-        ZoomScale scale = new ZoomScale();
-        for (int i = 0; i < list.getLength(); i++) {
-            Element element = (Element)list.item(i);
-            String river = "default";
-            double range = 0d;
-            double radius = 10d;
-            if (element.hasAttribute("river")) {
-                river = element.getAttribute("river");
-            }
-            if (!element.hasAttribute("range")) {
-                continue;
-            }
-            else {
-                String r = element.getAttribute("range");
-                try {
-                    range = Double.parseDouble(r);
-                }
-                catch (NumberFormatException nfe) {
-                    continue;
-                }
-            }
-            if (!element.hasAttribute("radius")) {
-                continue;
-            }
-            else {
-                String r = element.getAttribute("radius");
-                try {
-                    radius = Double.parseDouble(r);
-                }
-                catch (NumberFormatException nfe) {
-                    continue;
-                }
-            }
-            scale.addRange(river, range, radius);
-       }
-       context.put("zoomscale", scale);
-    }
-
-
-    /**
-     * This method initializes the transition configuration.
-     *
-     * @param config the config document.
-     * @param context the FLYSContext.
-     */
-    protected void configureTransitions(Document config, FLYSContext context) {
-        TransitionEngine engine = new TransitionEngine();
-
-        Document[] artifacts = getArtifactConfigurations(config);
-        logger.info("Found " + artifacts.length + " artifacts in the config.");
-
-        for (Document doc: artifacts) {
-
-            String artName = (String) XMLUtils.xpath(
-                doc, XPATH_ARTIFACT_NAME, XPathConstants.STRING);
-
-            NodeList list = (NodeList) XMLUtils.xpath(
-                doc, XPATH_TRANSITIONS, XPathConstants.NODESET);
-
-            if (list == null) {
-                logger.warn("The artifact " + artName +
-                    " has no transitions configured.");
-                continue;
-            }
-
-            int trans = list.getLength();
-
-            logger.info(
-                "Artifact '" + artName + "' has " + trans + " transitions.");
-
-            for (int i = 0; i < trans; i++) {
-                Transition t = TransitionFactory.createTransition(list.item(i));
-                String     s = t.getFrom();
-                engine.addTransition(s, t);
-            }
-        }
-
-        context.put(FLYSContext.TRANSITION_ENGINE_KEY, engine);
-    }
-
-
-    /**
-     * This method returns all artifact documents defined in
-     * <code>config</code>. <br>NOTE: The artifact configurations need to be
-     * stored in own files referenced by an xlink.
-     *
-     * @param config The global configuration.
-     *
-     * @return an array of Artifact configurations.
-     */
-    protected Document[] getArtifactConfigurations(Document config) {
-        NodeList artifacts = (NodeList) XMLUtils.xpath(
-            config, XPATH_ARTIFACTS, XPathConstants.NODESET);
-
-        int count = artifacts.getLength();
-
-        Document[] artifactDocs = new Document[count];
-
-        for (int i = 0; i < count; i++) {
-            Element tmp = (Element) artifacts.item(i);
-
-            String xlink = tmp.getAttribute(XPATH_XLINK);
-            xlink        = Config.replaceConfigDir(xlink);
-
-            File artifactFile = new File(xlink);
-            artifactDocs[i]   = XMLUtils.parseDocument(artifactFile);
-        }
-
-        return artifactDocs;
-    }
-
-
-    /**
-     * This method initializes the transition configuration.
-     *
-     * @param config the config document.
-     * @param context the FLYSContext.
-     */
-    protected void configureStates(Document config, FLYSContext context) {
-        StateEngine engine = new StateEngine();
-
-        Document[] artifacts = getArtifactConfigurations(config);
-        logger.info("Found " + artifacts.length + " artifacts in the config.");
-
-        for (Document doc: artifacts) {
-            List<State> states = new ArrayList<State>();
-
-            String artName = (String) XMLUtils.xpath(
-                doc, XPATH_ARTIFACT_NAME, XPathConstants.STRING);
-
-            NodeList stateList = (NodeList) XMLUtils.xpath(
-                doc, XPATH_STATES, XPathConstants.NODESET);
-
-            if (stateList == null) {
-                logger.warn("The artifact " + artName +
-                    " has no states configured.");
-                continue;
-            }
-
-            int count = stateList.getLength();
-
-            logger.info(
-                "Artifact '" + artName + "' has " + count + " states.");
-
-            for (int i = 0; i < count; i++) {
-                states.add(StateFactory.createState(
-                    stateList.item(i)));
-            }
-
-            engine.addStates(artName, states);
-        }
-
-        context.put(FLYSContext.STATE_ENGINE_KEY, engine);
-    }
-
-
-    /**
-     * This method intializes the provided output generators.
-     *
-     * @param config the config document.
-     * @param context the FLYSContext.
-     */
-    protected void configureOutGenerators(Document config, FLYSContext context){
-        Map<String, Class<?>> generators = new HashMap<String, Class<?>>();
-
-        NodeList outGenerators = (NodeList) XMLUtils.xpath(
-            config,
-            XPATH_OUTPUT_GENERATORS,
-            XPathConstants.NODESET);
-
-        int num = outGenerators == null ? 0 : outGenerators.getLength();
-
-        if (num == 0) {
-            logger.warn("No output generators configured in this application.");
-            return;
-        }
-
-        logger.info("Found " + num + " configured output generators.");
-
-        int idx = 0;
-
-        for (int i = 0; i < num; i++) {
-            Node item = outGenerators.item(i);
-
-            String name = (String) XMLUtils.xpath(
-                item, "@name", XPathConstants.STRING);
-
-            String clazz = (String) XMLUtils.xpath(
-                item, "text()", XPathConstants.STRING);
-
-            if (name == null || clazz == null) {
-                continue;
-            }
-
-            try {
-                generators.put(name, Class.forName(clazz));
-
-                idx++;
-            }
-            catch (ClassNotFoundException cnfe) {
-                logger.warn(cnfe, cnfe);
-            }
-        }
-
-        logger.info("Successfully loaded " + idx + " output generators.");
-        context.put(FLYSContext.OUTGENERATORS_KEY, generators);
-    }
-
-
-    /**
-     * This methods reads the configured themes and puts them into the
-     * FLYSContext.
-     *
-     * @param config The global configuration.
-     * @param context The FLYSContext.
-     */
-    protected void configureThemes(Document config, FLYSContext context) {
-        logger.debug("FLYSContextFactory.configureThemes");
-
-        Document cfg = getThemeConfig(config);
-
-        NodeList themeGroups = (NodeList) XMLUtils.xpath(
-            cfg, XPATH_THEME_GROUPS, XPathConstants.NODESET);
-
-        int groupNum = themeGroups != null ? themeGroups.getLength() : 0;
-
-        if (groupNum == 0) {
-            logger.warn("There are no theme groups configured!");
-        }
-
-        logger.info("Found " + groupNum + " theme groups in configuration");
-
-        List<ThemeGroup> groups = new ArrayList<ThemeGroup>();
-
-        for (int g = 0; g < groupNum; g++) {
-            Element themeGroup = (Element) themeGroups.item(g);
-            NodeList themes = (NodeList) XMLUtils.xpath(
-                themeGroup, XPATH_THEMES, XPathConstants.NODESET);
-
-            int num = themes != null ? themes.getLength() : 0;
-
-            if (num == 0) {
-                logger.warn("There are no themes configured!");
-                return;
-            }
-
-            logger.info("Theme group has " + num + " themes.");
-
-            Map<String, Theme> theThemes = new HashMap<String, Theme>();
-
-            for (int i = 0; i < num; i++) {
-                Node theme = themes.item(i);
-
-                Theme theTheme = ThemeFactory.createTheme(cfg, theme);
-
-                if (theme != null) {
-                    theThemes.put(theTheme.getName(), theTheme);
-                }
-            }
-            String gName = themeGroup.getAttribute("name");
-            groups.add(new ThemeGroup(gName, theThemes));
-
-            logger.info(
-                "Initialized " + theThemes.size() + "/" + num + " themes " +
-                "of theme-group '" + gName + "'");
-        }
-        context.put(FLYSContext.THEMES, groups);
-    }
-
-    /**
-     * This method is used to retrieve the theme configuration document.
-     *
-     * @param config The global configuration.
-     *
-     * @return the theme configuration.
-     */
-    protected Document getThemeConfig(Document config) {
-        String themeConfig = (String) XMLUtils.xpath(
-            config,
-            XPATH_THEME_CONFIG,
-            XPathConstants.STRING);
-
-        themeConfig = Config.replaceConfigDir(themeConfig);
-
-        logger.debug("Parse theme cfg: " + themeConfig);
-
-        return XMLUtils.parseDocument(new File(themeConfig));
-    }
-
-
-    protected void configureThemesMappings(Document cfg, FLYSContext context) {
-        logger.debug("FLYSContextFactory.configureThemesMappings");
-
-        Document config = getThemeConfig(cfg);
-
-        NodeList mappings = (NodeList) XMLUtils.xpath(
-            config, XPATH_THEME_MAPPINGS, XPathConstants.NODESET);
-
-        int num = mappings != null ? mappings.getLength() : 0;
-
-        if (num == 0) {
-            logger.warn("No theme <--> facet mappins found!");
-            return;
-        }
-
-        Map<String, List<ThemeMapping>> mapping =
-            new HashMap<String, List<ThemeMapping>>();
-
-        for (int i = 0; i < num; i++) {
-            Element node = (Element)mappings.item(i);
-
-            String from              = node.getAttribute("from");
-            String to                = node.getAttribute("to");
-            String pattern           = node.getAttribute("pattern");
-            String masterAttrPattern = node.getAttribute("masterAttr");
-            String outputPattern     = node.getAttribute("output");
-
-            if (from.length() > 0 && to.length() > 0) {
-                List<ThemeMapping> tm = mapping.get(from);
-
-                if (tm == null) {
-                    tm = new ArrayList<ThemeMapping>();
-                    mapping.put(from, tm);
-                }
-
-                tm.add(new ThemeMapping(
-                    from, to, pattern, masterAttrPattern, outputPattern));
-            }
-        }
-
-        logger.debug("Found " + mapping.size() + " theme mappings.");
-
-        context.put(FLYSContext.THEME_MAPPING, mapping);
-    }
-
-
-    /**
-     * Reads configured floodmap river WMSs from floodmap.xml and
-     * loads them into the given FLYSContext.
-     * @param cfg
-     * @param context
-     */
-    protected void configureFloodmapWMS(Document cfg, FLYSContext context) {
-        Map<String, String> riverWMS = new HashMap<String, String>();
-
-        NodeList rivers = (NodeList) XMLUtils.xpath(
-            cfg, XPATH_RIVER_WMS, XPathConstants.NODESET);
-
-        int num = rivers != null ? rivers.getLength() : 0;
-
-        for (int i = 0; i < num; i++) {
-            Element e = (Element) rivers.item(i);
-
-            String river = e.getAttribute("name");
-            String url   = XMLUtils.xpathString(e, "river-wms/@url", null);
-
-            if (river != null && url != null) {
-                riverWMS.put(river, url);
-            }
-        }
-
-        logger.debug("Found " + riverWMS.size() + " river WMS.");
-
-        context.put(FLYSContext.RIVER_WMS, riverWMS);
-    }
-
-
-    /**
-     * This method initializes the modules configuration.
-     *
-     * @param config the config document.
-     * @param context the FLYSContext.
-     */
-    protected void configureModules(Document cfg, FLYSContext context) {
-        NodeList modulenodes = (NodeList) XMLUtils.xpath(
-            cfg, XPATH_MODULES, XPathConstants.NODESET);
-
-        int num = modulenodes != null ? modulenodes.getLength() : 0;
-        ArrayList<Module> modules = new ArrayList<Module>(num);
-
-        for (int i = 0; i < num; i++) {
-            Element e = (Element) modulenodes.item(i);
-            String modulename = e.getAttribute("name");
-            String attrselected = e.getAttribute("selected");
-            boolean selected = attrselected == null ? false :
-                attrselected.equalsIgnoreCase("true");
-            logger.debug("Loaded module " + modulename);
-            modules.add(new Module(modulename, selected));
-        }
-        context.put(FLYSContext.MODULES, modules);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/context/SessionCallContextListener.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +0,0 @@
-package de.intevation.flys.artifacts.context;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Node;
-
-import org.hibernate.Session;
-
-import de.intevation.flys.backend.SessionHolder;
-
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.CallContext.Listener;
-
-
-/**
- * This CallContextListener is used to initialize a ThreadLocal variable in
- * each CallContext (for each request) that holds Sessions.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class SessionCallContextListener implements Listener {
-
-    public static final String SESSION_KEY = "context.session";
-
-    /** The logger that is used in this class.*/
-    private static Logger logger =
-        Logger.getLogger(SessionCallContextListener.class);
-
-
-    public SessionCallContextListener() {
-    }
-
-
-    public void setup(Document config, Node listenerConfig) {
-        // nothing to do here
-    }
-
-
-    /**
-     * Initializes a ThreadLocal variable that is used to hold sessions.
-     *
-     * @param context The CallContext.
-     */
-    public void init(CallContext context) {
-        logger.debug("SessionCallContextListener.init");
-
-        Session session = SessionHolder.acquire();
-
-        context.putContextValue(SESSION_KEY, session);
-    }
-
-
-    /**
-     * Closes open sessions of the ThreadLocal variable opened in init().
-     *
-     * @param context The CallContext.
-     */
-    public void close(CallContext context) {
-        logger.debug("SessionCallContextListener.close");
-
-        Session session = (Session)context.getContextValue(SESSION_KEY);
-        session.close();
-
-        SessionHolder.release();
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/DBConfig.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,108 +0,0 @@
-package de.intevation.flys.artifacts.datacage;
-
-import de.intevation.artifacts.common.utils.Config;
-
-import de.intevation.artifactdatabase.db.SQL;
-import de.intevation.artifactdatabase.db.DBConnection;
-
-import org.apache.log4j.Logger;
-
-public class DBConfig
-{
-    private static Logger logger = Logger.getLogger(DBConfig.class);
-
-     /**
-     * XPath to access the database driver within the global configuration.
-     */
-    public static final String DB_DRIVER =
-        "/artifact-database/datacage/driver/text()";
-    /**
-     * XPath to access the database URL within the global configuration.
-     */
-    public static final String DB_URL =
-        "/artifact-database/datacage/url/text()";
-    /**
-     * XPath to access the database use within the global configuration.
-     */
-    public static final String DB_USER =
-        "/artifact-database/datacage/user/text()";
-    /**
-     * XPath to access the database password within the global configuration.
-     */
-    public static final String DB_PASSWORD =
-        "/artifact-database/datacage/password/text()";
-
-    /**
-     * The default database driver: H2
-     */
-    public static final String DEFAULT_DRIVER =
-        "org.h2.Driver";
-
-    /**
-     * The default database user: ""
-     */
-    public static final String DEFAULT_USER = "";
-
-    /**
-     * The default database password: ""
-     */
-    public static final String DEFAULT_PASSWORD = "";
-
-
-    public static final String DEFAULT_URL =
-        "jdbc:h2:mem:datacage;INIT=RUNSCRIPT FROM '${artifacts.config.dir}/datacage.sql'";
-
-    public static final String RESOURCE_PATH = "/datacage-sql";
-
-    private static DBConfig instance;
-
-    protected DBConnection dbConnection;
-    protected SQL          sql;
-
-    public DBConfig() {
-    }
-
-    public DBConfig(DBConnection dbConnection, SQL sql) {
-        this.dbConnection = dbConnection;
-        this.sql          = sql;
-    }
-
-    public static synchronized DBConfig getInstance() {
-        if (instance == null) {
-            instance = createInstance();
-        }
-        return instance;
-    }
-
-    protected static DBConfig createInstance() {
-        String driver = Config.getStringXPath(
-            DB_DRIVER, DEFAULT_DRIVER);
-
-        String url = Config.getStringXPath(
-            DB_URL, DEFAULT_URL);
-
-        url = Config.replaceConfigDir(url);
-
-        String user = Config.getStringXPath(
-            DB_USER, DEFAULT_USER);
-
-        String password = Config.getStringXPath(
-            DB_PASSWORD, DEFAULT_PASSWORD);
-
-        DBConnection dbConnection = new DBConnection(
-            driver, url, user, password);
-
-        SQL sql = new SQL(DBConfig.class, RESOURCE_PATH, driver);
-
-        return new DBConfig(dbConnection, sql);
-    }
-
-    public DBConnection getDBConnection() {
-        return dbConnection;
-    }
-
-    public SQL getSQL() {
-        return sql;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1073 +0,0 @@
-package de.intevation.flys.artifacts.datacage;
-
-import java.util.Collection;
-import java.util.List;
-import java.util.Date;
-
-import java.sql.SQLException;
-import java.sql.PreparedStatement;
-import java.sql.Types;
-import java.sql.Timestamp;
-
-import de.intevation.artifacts.GlobalContext;
-import de.intevation.artifacts.ArtifactCollection;
-import de.intevation.artifacts.User;
-
-import de.intevation.artifactdatabase.db.SQL;
-import de.intevation.artifactdatabase.db.SQLExecutor;
-
-import de.intevation.artifactdatabase.LifetimeListener;
-import de.intevation.artifactdatabase.Backend;
-
-import de.intevation.artifactdatabase.data.StateData;
-
-import de.intevation.artifactdatabase.state.Output;
-import de.intevation.artifactdatabase.state.Facet;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.ArtifactDatabase;
-import de.intevation.artifacts.ArtifactDatabaseException;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.artifacts.common.utils.LRUCache;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Document;
-
-public class Datacage
-implements   LifetimeListener
-{
-    private static Logger log = Logger.getLogger(Datacage.class);
-
-    public static final String DATACAGE_KEY =
-        "global.datacage.instance";
-
-    public static final String ARTEFACT_DATABASE_KEY =
-        "global.artifact.database";
-
-    private String SQL_DELETE_ALL_USERS      = "delete.all.users";
-    private String SQL_DELETE_ALL_ARTIFACTS  = "delete.all.artifacts";
-    private String SQL_USER_ID_NEXTVAL       = "user.id.nextval";
-    private String SQL_USER_BY_GID           = "user.by.gid";
-    private String SQL_INSERT_USER           = "insert.user";
-    private String SQL_COLLECTION_BY_GID     = "collection.by.gid";
-    private String SQL_COLLECTION_ID_NEXTVAL = "collection.id.nextval";
-    private String SQL_INSERT_COLLECTION     = "insert.collection";
-    private String SQL_ARTIFACT_BY_GID       = "artifact.by.gid";
-    private String SQL_COLLECTION_ITEM_ID_NEXTVAL =
-        "collection.item.id.nextval";
-    private String SQL_INSERT_COLLECTION_ITEM = "insert.collection.item";
-    private String SQL_ARTIFACT_ID_NEXTVAL    = "artifact.id.nextval";
-    private String SQL_INSERT_ARTIFACT        = "insert.artifact";
-    private String SQL_ARTIFACT_DATA_ID_NEXTVAL = "artifact.data.id.nextval";
-    private String SQL_INSERT_ARTIFACT_DATA   = "insert.artifact.data";
-    private String SQL_OUT_ID_NEXTVALUE       = "out.id.nextval";
-    private String SQL_INSERT_OUT             = "insert.out";
-    private String SQL_FACET_ID_NEXTVAL       = "facet.id.nextval";
-    private String SQL_INSERT_FACET           = "insert.facet";
-    private String SQL_UPDATE_COLLECTION_NAME = "update.collection.name";
-    private String SQL_DELETE_ARTIFACT_FROM_COLLECTION =
-        "delete.artifact.from.collection";
-    private String SQL_DELETE_COLLECTION_BY_GID =
-        "delete.collection.by.gid";
-    private String SQL_DELETE_USER_BY_GID = "delete.user.by.gid";
-    private String SQL_DELETE_ARTIFACT_DATA_BY_ARTIFACT_ID =
-        "delete.artifact.data.by.artifact.id";
-    private String SQL_DELETE_OUTS_BY_ARTIFACT_ID =
-        "delete.outs.by.artifact.id";
-    private String SQL_DELETE_FACETS_BY_ARTIFACT_ID =
-        "delete.facets.by.artifact.id";
-    private String SQL_DELETE_ARTIFACT_BY_GID =
-        "delete.artifact.by.gid";
-
-    protected SQLExecutor sqlExecutor;
-
-    public class InitialScan
-    implements   ArtifactDatabase.ArtifactLoadedCallback
-    {
-        protected LRUCache<String, Integer> users;
-        protected LRUCache<String, Integer> collections;
-        protected LRUCache<String, Integer> artifacts;
-
-        protected GlobalContext context;
-
-        public InitialScan() {
-            users       = new LRUCache<String, Integer>();
-            collections = new LRUCache<String, Integer>();
-            artifacts   = new LRUCache<String, Integer>();
-        }
-
-        public InitialScan(GlobalContext context) {
-            this();
-            this.context = context;
-        }
-
-        @Override
-        public void artifactLoaded(
-            String   userId,
-            String   collectionId,
-            String   collectionName,
-            Date     collectionCreated,
-            String   artifactId,
-            Date     artifactCreated,
-            Artifact artifact
-        ) {
-            if (!(artifact instanceof FLYSArtifact)) {
-                log.warn("ignoring none FLYS artifacts");
-                return;
-            }
-
-            FLYSArtifact flysArtifact = (FLYSArtifact)artifact;
-
-            Integer uId = getUserId(userId);
-            Integer cId = getCollectionId(
-                collectionId, uId, collectionName, collectionCreated);
-
-            storeArtifact(artifactId, cId, flysArtifact, artifactCreated);
-        }
-
-        protected Integer getId(
-            LRUCache<String, Integer> cache,
-            final String              idString,
-            final String              selectById
-        ) {
-            Integer id = cache.get(idString);
-            if (id != null) {
-                return id;
-            }
-
-            final Integer [] res = new Integer[1];
-
-            SQLExecutor.Instance exec = sqlExecutor.new Instance() {
-                @Override
-                public boolean doIt() throws SQLException {
-                    prepareStatement(selectById);
-                    stmnt.setString(1, idString);
-                    result = stmnt.executeQuery();
-                    if (!result.next()) {
-                        return false;
-                    }
-                    res[0] = result.getInt(1);
-                    return true;
-                }
-            };
-
-            if (exec.runRead()) {
-                cache.put(idString, res[0]);
-                return res[0];
-            }
-
-            return null;
-        }
-
-        protected void storeArtifact(
-            final String       artifactId,
-            Integer            collectionId,
-            final FLYSArtifact artifact,
-            final Date         artifactCreated
-        ) {
-            Integer aId = getId(artifacts, artifactId, SQL_ARTIFACT_BY_GID);
-
-            if (aId != null) {
-                // We've already stored it. Just create the collection item.
-                storeCollectionItem(collectionId, aId);
-                return;
-            }
-            // We need to write it to database
-
-            final Integer [] res = new Integer[1];
-
-            SQLExecutor.Instance exec = sqlExecutor.new Instance() {
-                @Override
-                public boolean doIt() throws SQLException {
-                    prepareStatement(SQL_ARTIFACT_ID_NEXTVAL);
-                    result = stmnt.executeQuery();
-                    if (!result.next()) {
-                        return false;
-                    }
-                    res[0] = result.getInt(1);
-                    reset();
-                    prepareStatement(SQL_INSERT_ARTIFACT);
-                    stmnt.setInt   (1, res[0]);
-                    stmnt.setString(2, artifactId);
-                    stmnt.setString(3, artifact.getCurrentStateId());
-                    Timestamp timestamp = new Timestamp(artifactCreated != null
-                        ? artifactCreated.getTime()
-                        : System.currentTimeMillis());
-                    stmnt.setTimestamp(4, timestamp);
-                    stmnt.execute();
-                    conn.commit();
-                    return true;
-                }
-            };
-
-            if (!exec.runWrite()) {
-                log.error("storing of artifact failed.");
-                return;
-            }
-
-            artifacts.put(artifactId, aId = res[0]);
-
-            storeCollectionItem(collectionId, aId);
-
-            storeData(aId, artifact);
-
-            storeOuts(aId, artifact, context);
-        }
-
-
-        protected void storeCollectionItem(
-            final Integer collectionId,
-            final Integer artifactId
-        ) {
-            SQLExecutor.Instance exec = sqlExecutor.new Instance() {
-                @Override
-                public boolean doIt() throws SQLException {
-                    prepareStatement(SQL_COLLECTION_ITEM_ID_NEXTVAL);
-                    result = stmnt.executeQuery();
-                    if (!result.next()) {
-                        return false;
-                    }
-                    int ciId = result.getInt(1);
-                    reset();
-                    prepareStatement(SQL_INSERT_COLLECTION_ITEM);
-                    stmnt.setInt(1, ciId);
-                    stmnt.setInt(2, collectionId);
-                    stmnt.setInt(3, artifactId);
-                    stmnt.execute();
-                    conn.commit();
-                    return true;
-                }
-            };
-
-            if (!exec.runWrite()) {
-                log.error("storing of collection item failed.");
-            }
-        }
-
-        protected Integer getCollectionId(
-            final String  collectionId,
-            final Integer ownerId,
-            final String  collectionName,
-            final Date    collectionCreated
-        ) {
-            Integer c = getId(collections, collectionId, SQL_COLLECTION_BY_GID);
-
-            if (c != null) {
-                return c;
-            }
-
-            final Integer [] res = new Integer[1];
-
-            SQLExecutor.Instance exec = sqlExecutor.new Instance() {
-                @Override
-                public boolean doIt() throws SQLException {
-                    prepareStatement(SQL_COLLECTION_ID_NEXTVAL);
-                    result = stmnt.executeQuery();
-                    if (!result.next()) {
-                        return false;
-                    }
-                    res[0] = result.getInt(1);
-                    reset();
-                    prepareStatement(SQL_INSERT_COLLECTION);
-                    stmnt.setInt   (1, res[0]);
-                    stmnt.setString(2, collectionId);
-                    stmnt.setInt   (3, ownerId);
-                    setString(stmnt, 4, collectionName);
-                    Timestamp timestamp = new Timestamp(collectionCreated != null
-                        ? collectionCreated.getTime()
-                        : System.currentTimeMillis());
-                    stmnt.setTimestamp(5, timestamp);
-                    stmnt.execute();
-                    conn.commit();
-                    return true;
-                }
-            };
-
-            if (exec.runWrite()) {
-                collections.put(collectionId, res[0]);
-                return res[0];
-            }
-
-            return null;
-        }
-
-        protected Integer getUserId(final String userId) {
-
-            Integer u = getId(users, userId, SQL_USER_BY_GID);
-
-            if (u != null) {
-                return u;
-            }
-
-            final Integer [] res = new Integer[1];
-
-            SQLExecutor.Instance exec = sqlExecutor.new Instance() {
-                @Override
-                public boolean doIt() throws SQLException {
-                    prepareStatement(SQL_USER_ID_NEXTVAL);
-                    result = stmnt.executeQuery();
-                    if (!result.next()) {
-                        return false;
-                    }
-                    res[0] = result.getInt(1);
-                    reset();
-                    prepareStatement(SQL_INSERT_USER);
-                    stmnt.setInt   (1, res[0]);
-                    stmnt.setString(2, userId);
-                    stmnt.execute();
-                    conn.commit();
-                    return true;
-                }
-            };
-
-            if (exec.runWrite()) {
-                users.put(userId, res[0]);
-                return res[0];
-            }
-
-            return null;
-        }
-
-        public boolean scan(ArtifactDatabase adb) {
-            log.debug("scan");
-            try {
-                adb.loadAllArtifacts(this);
-            }
-            catch (ArtifactDatabaseException ade) {
-                log.error(ade);
-                return false;
-            }
-            return true;
-        }
-    } // class InitialScan
-
-
-    public Datacage() {
-    }
-
-    @Override
-    public void setup(Document document) {
-        log.debug("setup");
-        DBConfig config = DBConfig.getInstance();
-        setupSQL(config.getSQL());
-        sqlExecutor = new SQLExecutor(config.getDBConnection());
-    }
-
-    protected void setupSQL(SQL sql) {
-        SQL_DELETE_ALL_USERS      = sql.get(SQL_DELETE_ALL_USERS);
-        SQL_DELETE_ALL_ARTIFACTS  = sql.get(SQL_DELETE_ALL_ARTIFACTS);
-        SQL_USER_ID_NEXTVAL       = sql.get(SQL_USER_ID_NEXTVAL);
-        SQL_USER_BY_GID           = sql.get(SQL_USER_BY_GID);
-        SQL_INSERT_USER           = sql.get(SQL_INSERT_USER);
-        SQL_COLLECTION_BY_GID     = sql.get(SQL_COLLECTION_BY_GID);
-        SQL_COLLECTION_ID_NEXTVAL = sql.get(SQL_COLLECTION_ID_NEXTVAL);
-        SQL_INSERT_COLLECTION     = sql.get(SQL_INSERT_COLLECTION);
-        SQL_ARTIFACT_BY_GID       = sql.get(SQL_ARTIFACT_BY_GID);
-        SQL_COLLECTION_ITEM_ID_NEXTVAL =
-            sql.get(SQL_COLLECTION_ITEM_ID_NEXTVAL);
-        SQL_INSERT_COLLECTION_ITEM =
-            sql.get(SQL_INSERT_COLLECTION_ITEM);
-        SQL_ARTIFACT_ID_NEXTVAL = sql.get(SQL_ARTIFACT_ID_NEXTVAL);
-        SQL_INSERT_ARTIFACT     = sql.get(SQL_INSERT_ARTIFACT);
-        SQL_ARTIFACT_DATA_ID_NEXTVAL = sql.get(SQL_ARTIFACT_DATA_ID_NEXTVAL);
-        SQL_INSERT_ARTIFACT_DATA = sql.get(SQL_INSERT_ARTIFACT_DATA);
-        SQL_OUT_ID_NEXTVALUE     = sql.get(SQL_OUT_ID_NEXTVALUE);
-        SQL_INSERT_OUT           = sql.get(SQL_INSERT_OUT);
-        SQL_FACET_ID_NEXTVAL     = sql.get(SQL_FACET_ID_NEXTVAL);
-        SQL_INSERT_FACET         = sql.get(SQL_INSERT_FACET);
-        SQL_UPDATE_COLLECTION_NAME = sql.get(SQL_UPDATE_COLLECTION_NAME);
-        SQL_DELETE_ARTIFACT_FROM_COLLECTION =
-            sql.get(SQL_DELETE_ARTIFACT_FROM_COLLECTION);
-        SQL_DELETE_COLLECTION_BY_GID = sql.get(SQL_DELETE_COLLECTION_BY_GID);
-        SQL_DELETE_USER_BY_GID       = sql.get(SQL_DELETE_USER_BY_GID);
-        SQL_DELETE_ARTIFACT_DATA_BY_ARTIFACT_ID =
-            sql.get(SQL_DELETE_ARTIFACT_DATA_BY_ARTIFACT_ID);
-        SQL_DELETE_OUTS_BY_ARTIFACT_ID =
-            sql.get(SQL_DELETE_OUTS_BY_ARTIFACT_ID);
-        SQL_DELETE_FACETS_BY_ARTIFACT_ID =
-            sql.get(SQL_DELETE_FACETS_BY_ARTIFACT_ID);
-        SQL_DELETE_ARTIFACT_BY_GID =
-            sql.get(SQL_DELETE_ARTIFACT_BY_GID);
-    }
-
-    protected static final int numFacets(List<Output> outs) {
-        int sum = 0;
-        for (Output out: outs) {
-            sum += out.getFacets().size();
-        }
-        return sum;
-    }
-
-    protected static final void setString(
-        PreparedStatement stmnt,
-        int               index,
-        Object            value
-    )
-    throws SQLException
-    {
-        if (value == null) {
-            stmnt.setNull(index, Types.VARCHAR);
-        }
-        else {
-            stmnt.setString(index, value.toString());
-        }
-    }
-
-    @Override
-    public void systemUp(GlobalContext context) {
-        log.debug("systemUp entered");
-        initialScan(context);
-        context.put(DATACAGE_KEY, this);
-        log.debug("systemUp leaved");
-    }
-
-    protected void initialScan(GlobalContext context) {
-        log.debug("initialScan");
-
-        Object adbObject = context.get(ARTEFACT_DATABASE_KEY);
-
-        if (!(adbObject instanceof ArtifactDatabase)) {
-            log.error("missing artefact database. Cannot scan");
-            return;
-        }
-
-        ArtifactDatabase adb = (ArtifactDatabase)adbObject;
-
-        if (!cleanDatabase()) {
-            log.error("cleaning database failed");
-            return;
-        }
-
-        InitialScan is = new InitialScan(context);
-
-        if (!is.scan(adb)) {
-            log.error("initial scan failed");
-            return;
-        }
-
-    }
-
-    protected boolean cleanDatabase() {
-        log.debug("cleanDatabase");
-
-        boolean success = sqlExecutor.new Instance() {
-            @Override
-            public boolean doIt() throws SQLException {
-                prepareStatement(SQL_DELETE_ALL_USERS);
-                stmnt.execute();
-                prepareStatement(SQL_DELETE_ALL_ARTIFACTS);
-                stmnt.execute();
-                conn.commit();
-                return true;
-            }
-        }.runWrite();
-
-        log.debug("after runWrite(): " + success);
-
-        return success;
-    }
-
-
-    @Override
-    public void systemDown(GlobalContext context) {
-        log.debug("systemDown");
-    }
-
-    public void setup(GlobalContext globalContext) {
-        log.debug("setup");
-    }
-
-    public void createdArtifact(
-        Artifact      artifact,
-        Backend       backend,
-        GlobalContext context
-    ) {
-        log.debug("createdArtifact");
-
-        if (artifact == null) {
-            log.warn("artifact to create is null");
-            return;
-        }
-
-        if (!(artifact instanceof FLYSArtifact)) {
-            log.warn("need FLYSArtifact here (have " + artifact.getClass() + ")");
-            return;
-        }
-
-        final FLYSArtifact flys = (FLYSArtifact)artifact;
-
-        final int [] res = new int[1];
-
-        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
-            @Override
-            public boolean doIt() throws SQLException {
-                prepareStatement(SQL_ARTIFACT_ID_NEXTVAL);
-                result = stmnt.executeQuery();
-                if (!result.next()) {
-                    log.error("id generation for artifact failed");
-                    return false;
-                }
-                res[0] = result.getInt(1);
-                reset();
-                prepareStatement(SQL_INSERT_ARTIFACT);
-                stmnt.setInt      (1, res[0]);
-                stmnt.setString   (2, flys.identifier());
-                stmnt.setString   (3, flys.getCurrentStateId());
-                stmnt.setTimestamp(4,
-                    new Timestamp(System.currentTimeMillis()));
-                stmnt.execute();
-                conn.commit();
-                return true;
-            }
-        };
-
-        if (!exec.runWrite()) {
-            log.error("storing of artifact failed.");
-            return;
-        }
-
-        storeData(res[0], flys);
-        storeOuts(res[0], flys, context);
-    }
-
-    public void storedArtifact(
-        Artifact      artifact,
-        Backend       backend,
-        GlobalContext context
-    ) {
-        log.debug("storedArtifact");
-        if (!(artifact instanceof FLYSArtifact)) {
-            log.warn("need FLYSArtifact here but have a " + artifact.getClass());
-            return;
-        }
-
-        final FLYSArtifact flys = (FLYSArtifact)artifact;
-
-        final Integer [] res = new Integer[1];
-
-        // check first if artifact already exists
-        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
-            @Override
-            public boolean doIt() throws SQLException {
-                prepareStatement(SQL_ARTIFACT_BY_GID);
-                stmnt.setString(1, flys.identifier());
-                result = stmnt.executeQuery();
-                if (!result.next()) {
-                    // new artifact
-                    return true;
-                }
-                res[0] = result.getInt(1);
-                return true;
-            }
-        };
-
-        if (!exec.runRead()) {
-            log.error("querying artifact failed");
-            return;
-        }
-
-        if (res[0] == null) { // new artifact
-            createdArtifact(artifact, backend, context);
-            return;
-        }
-
-        // artifact already exists -> delete old data
-        exec = sqlExecutor.new Instance() {
-            @Override
-            public boolean doIt() throws SQLException {
-                prepareStatement(SQL_DELETE_ARTIFACT_DATA_BY_ARTIFACT_ID);
-                stmnt.setInt(1, res[0]);
-                stmnt.execute();
-                prepareStatement(SQL_DELETE_FACETS_BY_ARTIFACT_ID);
-                stmnt.setInt(1, res[0]);
-                stmnt.execute();
-                prepareStatement(SQL_DELETE_OUTS_BY_ARTIFACT_ID);
-                stmnt.setInt(1, res[0]);
-                stmnt.execute();
-                conn.commit();
-                return true;
-            }
-        };
-
-        if (!exec.runWrite()) {
-            log.error("deleting old artifact data failed");
-            return;
-        }
-
-        // write new data
-        storeData(res[0], flys);
-        storeOuts(res[0], flys, context);
-    }
-
-    public void createdUser(
-        final User    user,
-        Backend       backend,
-        GlobalContext context
-    ) {
-        log.debug("createdUser");
-        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
-            @Override
-            public boolean doIt() throws SQLException {
-                prepareStatement(SQL_USER_ID_NEXTVAL);
-                result = stmnt.executeQuery();
-                if (!result.next()) {
-                    log.error("id generation for user failed");
-                    return false;
-                }
-                int uId = result.getInt(1);
-                reset();
-                prepareStatement(SQL_INSERT_USER);
-                stmnt.setInt(1, uId);
-                stmnt.setString(2, user.identifier());
-                stmnt.execute();
-                conn.commit();
-                return true;
-            }
-        };
-
-        if (!exec.runWrite()) {
-            log.error("create user failed");
-        }
-    }
-
-    public void deletedUser(
-        final String  identifier,
-        Backend       backend,
-        GlobalContext context
-    ) {
-        log.debug("deletedUser");
-        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
-            @Override
-            public boolean doIt() throws SQLException {
-                prepareStatement(SQL_DELETE_USER_BY_GID);
-                stmnt.setString(1, identifier);
-                stmnt.execute();
-                conn.commit();
-                return true;
-            }
-        };
-
-        if (!exec.runWrite()) {
-            log.error("delete user failed");
-        }
-    }
-
-    public void createdCollection(
-        final ArtifactCollection collection,
-        Backend                  backend,
-        GlobalContext            context
-    ) {
-        log.debug("createdCollection");
-        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
-            @Override
-            public boolean doIt() throws SQLException {
-                String userId = collection.getUser().identifier();
-                prepareStatement(SQL_USER_BY_GID);
-                stmnt.setString(1, userId);
-                result = stmnt.executeQuery();
-                int uId;
-                if (result.next()) {
-                    uId = result.getInt(1);
-                    reset();
-                }
-                else {
-                    // need to create user first
-                    reset();
-                    prepareStatement(SQL_USER_ID_NEXTVAL);
-                    result = stmnt.executeQuery();
-                    if (!result.next()) {
-                        log.error("id generation for user failed");
-                        return false;
-                    }
-                    uId = result.getInt(1);
-                    reset();
-                    prepareStatement(SQL_INSERT_USER);
-                    stmnt.setInt(1, uId);
-                    stmnt.setString(2, userId);
-                    stmnt.execute();
-                    conn.commit();
-                    reset();
-                }
-
-                prepareStatement(SQL_COLLECTION_ID_NEXTVAL);
-                result = stmnt.executeQuery();
-                if (!result.next()) {
-                    log.error("id generation for collection failed");
-                    return false;
-                }
-                int cId = result.getInt(1);
-                reset();
-
-                String identifier = collection.identifier();
-                String name       = collection.getName();
-
-                prepareStatement(SQL_INSERT_COLLECTION);
-                stmnt.setInt(1, cId);
-                stmnt.setString(2, identifier);
-                stmnt.setInt(3, uId);
-                setString(stmnt, 4, name);
-                stmnt.setTimestamp(5,
-                    new Timestamp(System.currentTimeMillis()));
-                stmnt.execute();
-
-                conn.commit();
-                return true;
-            }
-        };
-
-        if (!exec.runWrite()) {
-            log.error("create collection failed");
-        }
-    }
-
-    public void deletedCollection(
-        final String  identifier,
-        Backend       backend,
-        GlobalContext context
-    ) {
-        log.debug("deletedCollection");
-        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
-            @Override
-            public boolean doIt() throws SQLException {
-                prepareStatement(SQL_DELETE_COLLECTION_BY_GID);
-                stmnt.setString(1, identifier);
-                stmnt.execute();
-                conn.commit();
-                return true;
-            }
-        };
-
-        if (!exec.runWrite()) {
-            log.error("delete collection failed");
-        }
-    }
-
-    public void changedCollectionAttribute(
-        String        identifier,
-        Document      document,
-        Backend       backend,
-        GlobalContext context
-    ) {
-        log.debug("changedCollectionAttribute");
-    }
-
-    public void changedCollectionItemAttribute(
-        String        collectionId,
-        String        artifactId,
-        Document      document,
-        Backend       backend,
-        GlobalContext context
-    ) {
-        log.debug("changedCollectionItemAttribute");
-    }
-
-    public void addedArtifactToCollection(
-        final String  artifactId,
-        final String  collectionId,
-        Backend       backend,
-        GlobalContext context
-    ) {
-        log.debug("addedArtifactToCollection");
-        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
-            @Override
-            public boolean doIt() throws SQLException {
-                prepareStatement(SQL_ARTIFACT_BY_GID);
-                stmnt.setString(1, artifactId);
-                result = stmnt.executeQuery();
-                if (!result.next()) {
-                    return false;
-                }
-                int aId = result.getInt(1);
-                reset();
-
-                prepareStatement(SQL_COLLECTION_BY_GID);
-                stmnt.setString(1, collectionId);
-                result = stmnt.executeQuery();
-                if (!result.next()) {
-                    return false;
-                }
-                int cId = result.getInt(1);
-                reset();
-
-                prepareStatement(SQL_COLLECTION_ITEM_ID_NEXTVAL);
-                result = stmnt.executeQuery();
-                if (!result.next()) {
-                    return false;
-                }
-                int ciId = result.getInt(1);
-                reset();
-
-                prepareStatement(SQL_INSERT_COLLECTION_ITEM);
-                stmnt.setInt(1, ciId);
-                stmnt.setInt(2, cId);
-                stmnt.setInt(3, aId);
-                stmnt.execute();
-
-                conn.commit();
-                return true;
-            }
-        };
-        if (!exec.runWrite()) {
-            log.error("added artifact to collection failed");
-        }
-    }
-
-    public void removedArtifactFromCollection(
-        final String  artifactId,
-        final String  collectionId,
-        Backend       backend,
-        GlobalContext context
-    ) {
-        log.debug("removedArtifactFromCollection");
-        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
-            @Override
-            public boolean doIt() throws SQLException {
-                prepareStatement(SQL_ARTIFACT_BY_GID);
-                stmnt.setString(1, artifactId);
-                result = stmnt.executeQuery();
-                if (!result.next()) {
-                    return false;
-                }
-                int aId = result.getInt(1);
-                reset();
-                prepareStatement(SQL_COLLECTION_BY_GID);
-                stmnt.setString(1, collectionId);
-                result = stmnt.executeQuery();
-                if (!result.next()) {
-                    return false;
-                }
-                int cId = result.getInt(1);
-                reset();
-                prepareStatement(SQL_DELETE_ARTIFACT_FROM_COLLECTION);
-                stmnt.setInt(1, cId);
-                stmnt.setInt(2, aId);
-                stmnt.execute();
-                conn.commit();
-                return true;
-            }
-        };
-        if (!exec.runWrite()) {
-            log.error("removing artifact from collection failed");
-        }
-    }
-
-    public void setCollectionName(
-        final String  collectionId,
-        final String  name,
-        GlobalContext context
-    ) {
-        log.debug("setCollectionName");
-        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
-            @Override
-            public boolean doIt() throws SQLException {
-                prepareStatement(SQL_UPDATE_COLLECTION_NAME);
-                stmnt.setString(1, name);
-                stmnt.setString(2, collectionId);
-                stmnt.execute();
-                conn.commit();
-                return true;
-            }
-        };
-        if (!exec.runWrite()) {
-            log.error("changing name failed");
-        }
-    }
-
-    protected void storeData(
-        final int     artifactId,
-        FLYSArtifact  artifact
-    ) {
-        final Collection<StateData> data = artifact.getAllData();
-
-        if (data.isEmpty()) {
-            return;
-        }
-
-        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
-            @Override
-            public boolean doIt() throws SQLException {
-                int [] ids = new int[data.size()];
-                prepareStatement(SQL_ARTIFACT_DATA_ID_NEXTVAL);
-
-                for (int i = 0; i < ids.length; ++i) {
-                    result = stmnt.executeQuery();
-                    if (!result.next()) {
-                        log.error("generating id for artifact data failed");
-                        return false;
-                    }
-                    ids[i] = result.getInt(1);
-                    result.close(); result = null;
-                }
-                reset();
-                prepareStatement(SQL_INSERT_ARTIFACT_DATA);
-
-                int i = 0;
-                for (StateData sd: data) {
-                    int id = ids[i++];
-                    stmnt.setInt(1, id);
-                    stmnt.setInt(2, artifactId);
-                    // XXX: Where come the nulls from?
-                    String type = sd.getType();
-                    if (type == null) type = "String";
-                    stmnt.setString(3, type);
-                    stmnt.setString(4, sd.getName());
-                    setString(stmnt, 5, sd.getValue());
-                    stmnt.execute();
-                }
-
-                conn.commit();
-                return true;
-            }
-        };
-
-        if (!exec.runWrite()) {
-            log.error("storing artifact data failed");
-        }
-    }
-
-    protected void storeOuts(
-        final int          artifactId,
-        final FLYSArtifact artifact,
-        GlobalContext      context
-    ) {
-        final List<Output> outs = artifact.getOutputs(context);
-
-        if (outs.isEmpty()) {
-            return;
-        }
-
-        final int [] outIds = new int[outs.size()];
-
-        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
-            @Override
-            public boolean doIt() throws SQLException {
-                prepareStatement(SQL_OUT_ID_NEXTVALUE);
-                for (int i = 0; i < outIds.length; ++i) {
-                    result = stmnt.executeQuery();
-                    if (!result.next()) {
-                        log.error("generation of out ids failed");
-                        return false;
-                    }
-                    outIds[i] = result.getInt(1);
-                    result.close(); result = null;
-                }
-                reset();
-                prepareStatement(SQL_INSERT_OUT);
-                for (int i = 0; i < outIds.length; ++i) {
-                    Output out = outs.get(i);
-                    stmnt.setInt(1, outIds[i]);
-                    stmnt.setInt(2, artifactId);
-                    stmnt.setString(3, out.getName());
-                    setString(stmnt, 4, out.getDescription());
-                    setString(stmnt, 5, out.getType());
-                    stmnt.execute();
-                }
-                conn.commit();
-                return true;
-            }
-        };
-
-        if (!exec.runWrite()) {
-            log.error("storing artifact outs failed");
-            return;
-        }
-
-        final int FACETS = numFacets(outs);
-
-        if (FACETS == 0) {
-            return;
-        }
-
-        exec = sqlExecutor.new Instance() {
-            @Override
-            public boolean doIt() throws SQLException {
-                int [] facetIds = new int[FACETS];
-                prepareStatement(SQL_FACET_ID_NEXTVAL);
-                for (int i = 0; i < facetIds.length; ++i) {
-                    result = stmnt.executeQuery();
-                    if (!result.next()) {
-                        log.error("generation of facet ids failed");
-                        return false;
-                    }
-                    facetIds[i] = result.getInt(1);
-                    result.close(); result = null;
-                }
-                reset();
-                prepareStatement(SQL_INSERT_FACET);
-                int index = 0;
-                for (int i = 0, N = outs.size(); i < N; ++i) {
-                    Output out = outs.get(i);
-                    int outId = outIds[i];
-                    for (Facet facet: out.getFacets()) {
-                        stmnt.setInt(1, facetIds[index]);
-                        stmnt.setInt(2, outId);
-                        stmnt.setString(3, facet.getName());
-                        stmnt.setInt(4, facet.getIndex());
-                        stmnt.setString(5, "XXX"); // TODO: handle states
-                        setString(stmnt, 6, facet.getDescription());
-                        stmnt.execute();
-                        ++index;
-                    }
-                }
-                conn.commit();
-                return true;
-            }
-        };
-
-        if (!exec.runWrite()) {
-            log.error("storing facets failed");
-        }
-    }
-
-    public void killedCollections(
-        final List<String> identifiers,
-        GlobalContext      context
-    ) {
-        log.debug("killedCollections");
-
-        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
-            @Override
-            public boolean doIt() throws SQLException {
-                prepareStatement(SQL_DELETE_COLLECTION_BY_GID);
-                for (String identifier: identifiers) {
-                    stmnt.setString(1, identifier);
-                    stmnt.execute();
-                }
-                conn.commit();
-                return true;
-            }
-        };
-
-        if (!exec.runWrite()) {
-            log.error("killing collections failed");
-        }
-    }
-
-    public void killedArtifacts(
-        final List<String> identifiers,
-        GlobalContext      context
-    ) {
-        log.debug("killedArtifacts");
-
-        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
-            @Override
-            public boolean doIt() throws SQLException {
-                prepareStatement(SQL_DELETE_ARTIFACT_BY_GID);
-                for (String identifier: identifiers) {
-                    stmnt.setString(1, identifier);
-                    stmnt.execute();
-                }
-                conn.commit();
-                return true;
-            }
-        };
-
-        if (!exec.runWrite()) {
-            log.error("killing artifacts failed");
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/DatacageBackendListener.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,191 +0,0 @@
-package de.intevation.flys.artifacts.datacage;
-
-import java.util.List;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.ArtifactCollection;
-import de.intevation.artifacts.GlobalContext;
-import de.intevation.artifacts.User;
-
-import de.intevation.artifactdatabase.BackendListener;
-import de.intevation.artifactdatabase.Backend;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Document;
-
-/** Triggers Datacage to update db. */
-public class DatacageBackendListener
-implements   BackendListener
-{
-    private static Logger log =
-        Logger.getLogger(DatacageBackendListener.class);
-
-    protected GlobalContext context;
-
-    public DatacageBackendListener() {
-        log.debug("new DatacageBackendListener");
-    }
-
-    protected Datacage getDatacage() {
-        Object listener = context.get(Datacage.DATACAGE_KEY);
-        return listener instanceof Datacage
-            ? (Datacage)listener
-            : null;
-    }
-
-    @Override
-    public void setup(GlobalContext context) {
-        log.debug("setup");
-        this.context = context;
-        Datacage l = getDatacage();
-        if (l != null) {
-            l.setup(context);
-        }
-    }
-
-    @Override
-    public void createdArtifact(Artifact artifact, Backend backend) {
-        log.debug("createdArtifact");
-        Datacage l = getDatacage();
-        if (l != null) {
-            l.createdArtifact(artifact, backend, context);
-        }
-    }
-
-    @Override
-    public void storedArtifact(Artifact artifact, Backend backend) {
-        log.debug("storedArtifact");
-        Datacage l = getDatacage();
-        if (l != null) {
-            l.storedArtifact(artifact, backend, context);
-        }
-    }
-
-    @Override
-    public void createdUser(User user, Backend backend) {
-        log.debug("createdUser");
-        Datacage l = getDatacage();
-        if (l != null) {
-            l.createdUser(user, backend, context);
-        }
-    }
-
-    @Override
-    public void deletedUser(String identifier, Backend backend) {
-        log.debug("deletedUser");
-        Datacage l = getDatacage();
-        if (l != null) {
-            l.deletedUser(identifier, backend, context);
-        }
-    }
-
-    @Override
-    public void createdCollection(
-        ArtifactCollection collection,
-        Backend            backend
-    ) {
-        log.debug("createdCollection");
-        Datacage l = getDatacage();
-        if (l != null) {
-            l.createdCollection(collection, backend, context);
-        }
-    }
-
-    @Override
-    public void deletedCollection(String identifier, Backend backend) {
-        log.debug("deletedCollection");
-        Datacage l = getDatacage();
-        if (l != null) {
-            l.deletedCollection(identifier, backend, context);
-        }
-    }
-
-    @Override
-    public void changedCollectionAttribute(
-        String   identifier,
-        Document document,
-        Backend  backend
-    ) {
-        log.debug("changedCollectionAttribute");
-        Datacage l = getDatacage();
-        if (l != null) {
-            l.changedCollectionAttribute(
-                identifier, document, backend, context);
-        }
-    }
-
-    @Override
-    public void changedCollectionItemAttribute(
-        String   collectionId,
-        String   artifactId,
-        Document document,
-        Backend  backend
-    ) {
-        log.debug("changedCollectionItemAttribute");
-        Datacage l = getDatacage();
-        if (l != null) {
-            l.changedCollectionItemAttribute(
-                collectionId, artifactId, document, backend, context);
-        }
-    }
-
-    @Override
-    public void addedArtifactToCollection(
-        String  artifactId,
-        String  collectionId,
-        Backend backend
-    ) {
-        log.debug("addedArtifactToCollection");
-        Datacage l = getDatacage();
-        if (l != null) {
-            l.addedArtifactToCollection(
-                artifactId, collectionId, backend, context);
-        }
-    }
-
-    @Override
-    public void removedArtifactFromCollection(
-        String  artifactId,
-        String  collectionId,
-        Backend backend
-    ) {
-        log.debug("removedArtifactFromCollection");
-        Datacage l = getDatacage();
-        if (l != null) {
-            l.removedArtifactFromCollection(
-                artifactId, collectionId, backend, context);
-        }
-    }
-
-    @Override
-    public void setCollectionName(
-        String collectionId,
-        String name
-    ) {
-        log.debug("setCollectionName");
-        Datacage l = getDatacage();
-        if (l != null) {
-            l.setCollectionName(collectionId, name, context);
-        }
-    }
-
-    @Override
-    public void killedCollections(List<String> identifiers, Backend backend) {
-        log.debug("killedCollections");
-        Datacage l = getDatacage();
-        if (l != null) {
-            l.killedCollections(identifiers, context);
-        }
-    }
-
-    @Override
-    public void killedArtifacts(List<String> identifiers, Backend backend) {
-        log.debug("killedArtifacts");
-        Datacage l = getDatacage();
-        if (l != null) {
-            l.killedArtifacts(identifiers, context);
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/Recommendations.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,344 +0,0 @@
-package de.intevation.flys.artifacts.datacage;
-
-import java.util.Map;
-import java.util.HashMap;
-import java.util.List;
-import java.util.ArrayList;
-
-import java.io.InputStream;
-import java.io.IOException;
-import java.io.File;
-
-import java.io.FileInputStream;
-
-import java.sql.Connection;
-import java.sql.SQLException;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Node;
-
-import org.hibernate.Session;
-import org.hibernate.SessionFactory;
-
-import org.hibernate.jdbc.Work;
-
-import de.intevation.artifacts.common.utils.Config;
-import de.intevation.artifacts.common.utils.XMLUtils;
-import de.intevation.artifacts.common.utils.StringUtils;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.flys.backend.SessionFactoryProvider;
-import de.intevation.flys.backend.SessionHolder;
-
-import de.intevation.artifactdatabase.data.StateData;
-
-import de.intevation.flys.artifacts.datacage.templating.Builder;
-import de.intevation.flys.artifacts.datacage.templating.BuilderPool;
-
-
-/**
- * Also accessible as Singleton with getInstance().
- */
-public class Recommendations
-{
-    public static final String CONNECTION_USER   = "user";
-    public static final String CONNECTION_SYSTEM = "system";
-    public static final String CONNECTION_SEDDB  = "seddb";
-
-    public static final String DEFAULT_CONNECTION_NAME = CONNECTION_SYSTEM;
-
-    private static Logger log = Logger.getLogger(Recommendations.class);
-
-    private static final boolean DEVELOPMENT_MODE =
-        Boolean.getBoolean("flys.datacage.recommendations.development");
-
-    public static final String XPATH_TEMPLATE =
-        "/artifact-database/metadata/template/text()";
-
-    public static final String DEFAULT_TEMPLATE_PATH =
-        "${artifacts.config.dir}/meta-data.xml";
-
-    private static Recommendations INSTANCE;
-
-    public static class BuilderPoolProvider
-    {
-        protected BuilderPool builderPool;
-
-        public BuilderPoolProvider() {
-        }
-
-        public BuilderPoolProvider(BuilderPool builderPool) {
-            this.builderPool = builderPool;
-        }
-
-        public BuilderPool getBuilderPool() {
-            return builderPool;
-        }
-    } // class BuilderProvider
-
-    public static class FileBuilderPoolProvider
-    extends             BuilderPoolProvider
-    {
-        protected File file;
-        protected long lastModified;
-
-        public FileBuilderPoolProvider() {
-        }
-
-        public FileBuilderPoolProvider(File file) {
-            this.file    = file;
-            lastModified = Long.MIN_VALUE;
-        }
-
-        @Override
-        public synchronized BuilderPool getBuilderPool() {
-            long modified = file.lastModified();
-            if (modified > lastModified) {
-                lastModified = modified;
-                try {
-                    Document template = loadTemplate(file);
-                    builderPool = new BuilderPool(template);
-                }
-                catch (IOException ioe) {
-                    log.error(ioe);
-                }
-            }
-            return builderPool;
-        }
-
-        public BuilderPoolProvider toStaticProvider() {
-            return new BuilderPoolProvider(builderPool);
-        }
-    } // class BuilderProvider
-
-    protected BuilderPoolProvider builderPoolProvider;
-
-    public Recommendations() {
-    }
-
-    public Recommendations(BuilderPoolProvider builderPoolProvider) {
-        this.builderPoolProvider = builderPoolProvider;
-    }
-
-    public BuilderPool getBuilderPool() {
-        return builderPoolProvider.getBuilderPool();
-    }
-
-    protected static void artifactToParameters(
-        FLYSArtifact        artifact,
-        Map<String, Object> parameters
-    ) {
-        parameters.put("CURRENT-STATE-ID", artifact.getCurrentStateId());
-        parameters.put("ARTIFACT-ID",      artifact.identifier());
-
-        for (StateData sd: artifact.getAllData()) {
-            Object value = sd.getValue();
-            if (value == null) {
-                continue;
-            }
-            String key = sd.getName().replace('.', '-').toUpperCase();
-            parameters.put(key, value);
-        }
-    }
-
-    /**
-     * Put Key/Values from \param src to \param dst, but uppercase
-     * both Keys and Values.
-     */
-    public static void convertKeysToUpperCase(
-        Map<String, Object> src,
-        Map<String, Object> dst
-    ) {
-        for (Map.Entry<String, Object> entry: src.entrySet()) {
-            dst.put(entry.getKey().toUpperCase(), entry.getValue());
-        }
-    }
-
-
-    /**
-     * Append recommendations to \param result.
-     * @param extraParameters parameters (typicall example: 'recommended')
-     */
-    public void  recommend(
-        FLYSArtifact        artifact,
-        String              userId,
-        String []           outs,
-        Map<String, Object> extraParameters,
-        Node                result
-    ) {
-        Map<String, Object> parameters = new HashMap<String, Object>();
-
-        if (extraParameters != null) {
-            convertKeysToUpperCase(extraParameters, parameters);
-        }
-
-        if (userId != null) {
-            parameters.put("USER-ID", userId);
-        }
-
-        if (artifact != null) {
-            artifactToParameters(artifact, parameters);
-        }
-
-        parameters.put("ARTIFACT-OUTS", StringUtils.toUpperCase(outs));
-
-        parameters.put("PARAMETERS", parameters);
-
-        recommend(parameters, userId, result);
-    }
-
-
-    /**
-     * Append recommendations to \param result.
-     */
-    public void recommend(
-        Map<String, Object> parameters,
-        String              userId,
-        Node                result
-    ) {
-        recommend(parameters, userId, result, SessionHolder.HOLDER.get());
-    }
-
-    public void recommend(
-        final Map<String, Object> parameters,
-        final String              userId,
-        final Node                result,
-        Session                   systemSession
-    ) {
-        systemSession.doWork(new Work() {
-            @Override
-            public void execute(final Connection systemConnection)
-            throws SQLException
-            {
-                SessionFactory sedDBFactory =
-                    SessionFactoryProvider.getSedDBSessionFactory();
-
-                Session sedDBSession = sedDBFactory.openSession();
-                try {
-                    sedDBSession.doWork(new Work() {
-                        @Override
-                        public void execute(Connection sedDBConnection)
-                        throws SQLException
-                        {
-                            recommend(
-                                parameters, userId, result,
-                                systemConnection,
-                                sedDBConnection);
-                        }
-                    });
-                }
-                finally {
-                    sedDBSession.close();
-                }
-            }
-        });
-    }
-
-    public void recommend(
-        Map<String, Object> parameters,
-        String              userId,
-        Node                result,
-        Connection          systemConnection,
-        Connection          seddbConnection
-    ) throws SQLException
-    {
-        List<Builder.NamedConnection> connections =
-            new ArrayList<Builder.NamedConnection>(3);
-
-        Connection userConnection = userId != null
-            ? DBConfig
-                .getInstance()
-                .getDBConnection()
-                .getDataSource()
-                .getConnection()
-            : null;
-
-        try {
-            connections.add(new Builder.NamedConnection(
-                CONNECTION_SYSTEM, systemConnection, true));
-
-            if (seddbConnection != null) {
-                connections.add(new Builder.NamedConnection(
-                    CONNECTION_SEDDB, seddbConnection, true));
-            }
-
-            if (userConnection != null) {
-                connections.add(new Builder.NamedConnection(
-                    CONNECTION_USER, userConnection, false));
-            }
-
-            getBuilderPool().build(connections, result, parameters);
-        }
-        finally {
-            if (userConnection != null) {
-                userConnection.close();
-            }
-        }
-    }
-
-    /** Get singleton instance. */
-    public static synchronized Recommendations getInstance() {
-        if (INSTANCE == null) {
-            INSTANCE = createRecommendations();
-        }
-        return INSTANCE;
-    }
-
-
-    protected static Document loadTemplate(File file) throws IOException {
-        InputStream in = new FileInputStream(file);
-
-        try {
-            Document template = XMLUtils.parseDocument(in);
-            if (template == null) {
-                throw new IOException("cannot load template");
-            }
-            return template;
-        }
-        finally {
-            in.close();
-        }
-    }
-
-    public static Recommendations createRecommendations(File file) {
-        log.debug("Recommendations.createBuilder");
-
-        if (!file.isFile() || !file.canRead()) {
-            log.error("Cannot open template file '" + file + "'");
-            return null;
-        }
-
-        FileBuilderPoolProvider fbp = new FileBuilderPoolProvider(file);
-
-        if (fbp.getBuilderPool() == null) {
-            log.error("failed loading builder");
-            return null;
-        }
-
-        BuilderPoolProvider bp = DEVELOPMENT_MODE
-            ? fbp
-            : fbp.toStaticProvider();
-
-        return new Recommendations(bp);
-    }
-
-    protected static Recommendations createRecommendations() {
-        log.debug("Recommendations.createRecommendations");
-
-        String path = Config.getStringXPath(XPATH_TEMPLATE);
-
-        if (path == null) {
-            path = DEFAULT_TEMPLATE_PATH;
-        }
-
-        path = Config.replaceConfigDir(path);
-
-        log.info("Meta data template: " + path);
-
-        return createRecommendations(new File(path));
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/App.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,109 +0,0 @@
-package de.intevation.flys.artifacts.datacage.templating;
-
-import java.util.Map;
-import java.util.HashMap;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-
-import de.intevation.flys.backend.SessionFactoryProvider;
-
-import org.hibernate.Session;
-
-import org.w3c.dom.Document;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.common.utils.XMLUtils;
-
-import de.intevation.flys.artifacts.datacage.Recommendations;
-
-public class App
-{
-    private static Logger log = Logger.getLogger(App.class);
-
-    public static final String template =
-        System.getProperty("meta.data.template", "meta-data.xml");
-
-    public static final String userId =
-        System.getProperty("user.id");
-
-    public static final String PARAMETERS =
-        System.getProperty("meta.data.parameters", "");
-
-    public static final String OUTPUT =
-        System.getProperty("meta.data.output");
-
-    public static Map<String, Object> getParameters() {
-        HashMap<String, Object> map = new HashMap<String, Object>();
-        String [] parts = PARAMETERS.split("\\s*;\\s*");
-        for (String part: parts) {
-            String [] kv = part.split("\\s*:\\s*");
-            if (kv.length < 2 || (kv[0] = kv[0].trim()).length() == 0) {
-                continue;
-            }
-            String [] values = kv[1].split("\\s*,\\s*");
-            map.put(kv[0], values.length == 1 ? values[0] : values);
-        }
-        return map;
-    }
-
-    public static void main(String [] args) {
-
-        Recommendations rec = Recommendations.createRecommendations(
-            new File(template));
-
-        if (rec == null) {
-            System.err.println("No recommendations created");
-            return;
-        }
-
-        final Document result = XMLUtils.newDocument();
-
-        final Map<String, Object> parameters = getParameters();
-
-        Session session = SessionFactoryProvider
-            .createSessionFactory()
-            .openSession();
-
-        try {
-            rec.recommend(parameters, userId, result, session);
-        }
-        finally {
-            session.close();
-        }
-
-        OutputStream out;
-
-        if (OUTPUT == null) {
-            out = System.out;
-        }
-        else {
-            try {
-                out = new FileOutputStream(OUTPUT);
-            }
-            catch (IOException ioe) {
-                log.error(ioe);
-                return;
-            }
-        }
-
-        try {
-            XMLUtils.toStream(result, out);
-        }
-        finally {
-            if (OUTPUT != null) {
-                try {
-                    out.close();
-                }
-                catch (IOException ioe) {
-                    log.error(ioe);
-                }
-            }
-        }
-        System.exit(0);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/Builder.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,845 +0,0 @@
-package de.intevation.flys.artifacts.datacage.templating;
-
-import de.intevation.artifacts.common.utils.XMLUtils;
-
-import de.intevation.flys.utils.Pair;
-
-import java.sql.Connection;
-import java.sql.SQLException;
-
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Deque;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import javax.xml.namespace.QName;
-
-import javax.xml.xpath.XPath;
-import javax.xml.xpath.XPathConstants;
-import javax.xml.xpath.XPathExpression;
-import javax.xml.xpath.XPathExpressionException;
-import javax.xml.xpath.XPathFactory;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Attr;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-import org.w3c.dom.NamedNodeMap;
-
-
-/** Handles and evaluate meta-data template against dbs. */
-public class Builder
-{
-    private static Logger log = Logger.getLogger(Builder.class);
-
-    public static final Pattern STRIP_LINE_INDENT =
-        Pattern.compile("\\s*\\r?\\n\\s*");
-
-    public static final Pattern BRACKET_XPATH =
-        Pattern.compile("\\{([^}]+)\\}");
-
-    public static final String DC_NAMESPACE_URI =
-        "http://www.intevation.org/2011/Datacage";
-
-    private static final Document EVAL_DOCUMENT =
-        XMLUtils.newDocument();
-
-    private static final XPathFactory XPATH_FACTORY =
-        XPathFactory.newInstance();
-
-    protected Document template;
-
-    protected Map<String, CompiledStatement> compiledStatements;
-
-    protected Map<String, Element> macros;
-
-    /** Connection to either of the databases. */
-    public static class NamedConnection {
-
-        protected String     name;
-        protected Connection connection;
-        protected boolean    cached;
-
-        public NamedConnection() {
-        }
-
-        public NamedConnection(
-            String     name,
-            Connection connection
-        ) {
-            this(name, connection, true);
-        }
-
-        public NamedConnection(
-            String     name,
-            Connection connection,
-            boolean    cached
-        ) {
-            this.name       = name;
-            this.connection = connection;
-            this.cached     = cached;
-        }
-    } // class NamedConnection
-
-    public class BuildHelper
-    {
-        protected Node                                     output;
-        protected Document                                 owner;
-        protected StackFrames                              frames;
-        protected List<NamedConnection>                    connections;
-        protected Map<String, CompiledStatement.Instance>  statements;
-        protected Deque<Pair<NamedConnection, ResultData>> connectionsStack;
-        protected Deque<NodeList>                          macroBodies;
-        protected FunctionResolver                         functionResolver;
-        protected Map<String, XPathExpression>             expressions;
-
-
-        public BuildHelper(
-            Node                  output,
-            List<NamedConnection> connections,
-            Map<String, Object>   parameters
-        ) {
-            if (connections.isEmpty()) {
-                throw new IllegalArgumentException("no connections given.");
-            }
-
-            this.connections = connections;
-            connectionsStack =
-                new ArrayDeque<Pair<NamedConnection, ResultData>>();
-            this.output      = output;
-            frames           = new StackFrames(parameters);
-            owner            = getOwnerDocument(output);
-            macroBodies      = new ArrayDeque<NodeList>();
-            functionResolver = new FunctionResolver(this);
-            expressions      = new HashMap<String, XPathExpression>();
-            statements       =
-                new HashMap<String, CompiledStatement.Instance>();
-        }
-
-        public void build() throws SQLException {
-            try {
-                // XXX: Thread safety is now established by the builder pool.
-                //synchronized (template) {
-                    for (Node current: rootsToList()) {
-                        build(output, current);
-                    }
-                //}
-            }
-            finally {
-                closeStatements();
-            }
-        }
-
-        protected void closeStatements() {
-            for (CompiledStatement.Instance csi: statements.values()) {
-                csi.close();
-            }
-            statements.clear();
-        }
-
-        /**
-         * Return first statement node in NodeList, respecting
-         * macros but not doing evaluation (e.g. of <dc:if>s).
-         */
-        private Node findStatementNode(NodeList nodes) {
-            int S = nodes.getLength();
-
-            // Check direct children and take special care of macros.
-            for (int i = 0; i < S; ++i) {
-                Node node = nodes.item(i);
-                String ns;
-                // Regular statement node.
-                if (node.getNodeType() == Node.ELEMENT_NODE
-                && node.getLocalName().equals("statement")
-                && (ns = node.getNamespaceURI()) != null
-                && ns.equals(DC_NAMESPACE_URI)) {
-                    return node;
-                }
-                // Macro node. Descend.
-                else if (node.getNodeType() == Node.ELEMENT_NODE
-                    && node.getLocalName().equals("call-macro")
-                    && (ns = node.getNamespaceURI()) != null
-                    && ns.equals(DC_NAMESPACE_URI)) {
-
-                    String macroName = ((Element)node).getAttribute("name");
-                    Node inMacroNode =
-                        findStatementNode(getMacroChildren(macroName));
-                    if (inMacroNode != null) {
-                        return inMacroNode;
-                    }
-                }
-
-            }
-
-            return null;
-        }
-
-        /**
-         * Handle a dc:context node.
-         */
-        protected void context(Node parent, Element current)
-        throws SQLException
-        {
-            log.debug("dc:context");
-
-            NodeList subs = current.getChildNodes();
-            Node stmntNode = findStatementNode(subs);
-            int S = subs.getLength();
-
-            if (stmntNode == null) {
-                log.warn("dc:context: cannot find statement");
-                return;
-            }
-
-            String stmntText = stmntNode.getTextContent();
-
-            String con = current.getAttribute("connection");
-
-            String key = con + "-" + stmntText;
-
-            CompiledStatement.Instance csi = statements.get(key);
-
-            if (csi == null) {
-                CompiledStatement cs = compiledStatements.get(stmntText);
-                csi = cs.new Instance();
-                statements.put(key, csi);
-            }
-
-            NamedConnection connection = connectionsStack.isEmpty()
-                ? connections.get(0)
-                : connectionsStack.peek().getA();
-
-            if (con.length() > 0) {
-                for (NamedConnection nc: connections) {
-                    if (con.equals(nc.name)) {
-                        connection = nc;
-                        break;
-                    }
-                }
-            }
-
-            ResultData rd = csi.execute(
-                connection.connection,
-                frames,
-                connection.cached);
-
-            // only descent if there are results
-            if (!rd.isEmpty()) {
-                connectionsStack.push(
-                    new Pair<NamedConnection, ResultData>(connection, rd));
-                try {
-                    for (int i = 0; i < S; ++i) {
-                        build(parent, subs.item(i));
-                    }
-                }
-                finally {
-                    connectionsStack.pop();
-                }
-            }
-        }
-
-        public boolean hasResult() {
-            return !connectionsStack.isEmpty()
-                && !connectionsStack.peek().getB().isEmpty();
-        }
-
-        protected ResultData createFilteredResultData(ResultData rd, String filter) {
-            if (filter == null) return rd;
-
-            List<Object []> rows = rd.getRows();
-            String [] columns = rd.getColumnLabels();
-
-            List<Object []> filtered = new ArrayList<Object[]>(rows.size());
-
-            for (Object [] row: rows) {
-                frames.enter();
-                try {
-                    frames.put(columns, row);
-                    boolean traverse = filter == null;
-
-                    if (!traverse) {
-                        Boolean b = evaluateXPathToBoolean(filter);
-                        traverse = b != null && b;
-                    }
-                    if (traverse) {
-                        filtered.add(row);
-                    }
-                }
-                finally {
-                    frames.leave();
-                }
-            }
-            return new ResultData(rd.getColumnLabels(), filtered);
-        }
-
-        protected void filter(Node parent, Element current)
-        throws SQLException
-        {
-            String expr = current.getAttribute("expr");
-
-            if ((expr = expr.trim()).length() == 0) {
-                expr = null;
-            }
-
-            NodeList subs = current.getChildNodes();
-            int S = subs.getLength();
-            if (S == 0) {
-                log.debug("dc:filter has no children");
-                return;
-            }
-
-            ResultData orig = null;
-            Pair<Builder.NamedConnection, ResultData> pair = null;
-
-            if (expr != null && !connectionsStack.isEmpty()) {
-                pair = connectionsStack.peek();
-                orig = pair.getB();
-                pair.setB(createFilteredResultData(orig, expr));
-            }
-
-            try {
-                for (int i = 0; i < S; ++i) {
-                    build(parent, subs.item(i));
-                }
-            }
-            finally {
-                if (orig != null) {
-                    pair.setB(orig);
-                }
-            }
-        }
-
-        /**
-         * Kind of foreach over results of a statement within a context.
-         */
-        protected void foreach(Node parent, Element current)
-        throws SQLException
-        {
-            log.debug("dc:for-each");
-
-            if (connectionsStack.isEmpty()) {
-                log.debug("dc:for-each without having results");
-                return;
-            }
-
-            NodeList subs = current.getChildNodes();
-            int S = subs.getLength();
-
-            if (S == 0) {
-                log.debug("dc:for-each has no children");
-                return;
-            }
-
-            Pair<Builder.NamedConnection, ResultData> pair =
-                connectionsStack.peek();
-
-            ResultData rd = pair.getB();
-
-            String [] columns = rd.getColumnLabels();
-
-            for (Object [] row: rd.getRows()) {
-                frames.enter();
-                try {
-                    frames.put(columns, row);
-                    for (int i = 0; i < S; ++i) {
-                        build(parent, subs.item(i));
-                    }
-                }
-                finally {
-                    frames.leave();
-                }
-            }
-        }
-
-        /**
-         * Create element.
-         */
-        protected void element(Node parent, Element current)
-        throws SQLException
-        {
-            String attr = expand(current.getAttribute("name"));
-
-            if (log.isDebugEnabled()) {
-                log.debug("dc:element -> '" + attr + "'");
-            }
-
-            if (attr.length() == 0) {
-                log.warn("no name attribute found");
-                return;
-            }
-
-            Element element = owner.createElement(attr);
-
-            NodeList children = current.getChildNodes();
-            for (int i = 0, N = children.getLength(); i < N; ++i) {
-                build(element, children.item(i));
-            }
-
-            parent.appendChild(element);
-        }
-
-        protected void text(Node parent, Element current)
-        throws SQLException
-        {
-            log.debug("dc:text");
-            String value = expand(current.getTextContent());
-            parent.appendChild(owner.createTextNode(value));
-        }
-
-        /**
-         * Add attribute to an element
-         * @see Element
-         */
-        protected void attribute(Node parent, Element current) {
-
-            if (parent.getNodeType() != Node.ELEMENT_NODE) {
-                log.warn("need element here");
-                return;
-            }
-
-            String name  = expand(current.getAttribute("name"));
-            String value = expand(current.getAttribute("value"));
-
-            Element element = (Element)parent;
-
-            element.setAttribute(name, value);
-        }
-
-        /**
-         * Call-Macro node.
-         * Evaluate child-nodes of the given macro element (not its text).
-         */
-        protected void callMacro(Node parent, Element current)
-        throws SQLException
-        {
-            String name = current.getAttribute("name");
-
-            if (name.length() == 0) {
-                log.warn("missing 'name' attribute in 'call-macro'");
-                return;
-            }
-
-            Element macro = macros.get(name);
-
-            if (macro != null) {
-                macroBodies.push(current.getChildNodes());
-                try {
-                    NodeList subs = macro.getChildNodes();
-                    for (int j = 0, M = subs.getLength(); j < M; ++j) {
-                        build(parent, subs.item(j));
-                    }
-                }
-                finally {
-                    macroBodies.pop();
-                }
-            }
-            else {
-                log.warn("no macro '" + name + "' found.");
-            }
-        }
-
-        protected void macroBody(Node parent, Element current)
-        throws SQLException
-        {
-            if (!macroBodies.isEmpty()) {
-                NodeList children = macroBodies.peek();
-                for (int i = 0, N = children.getLength(); i < N; ++i) {
-                    build(parent, children.item(i));
-                }
-            }
-            else {
-                log.warn("no current macro");
-            }
-        }
-
-        /** Get macro node children, not resolving bodies. */
-        protected NodeList getMacroChildren(String name) {
-            NodeList macros = template.getElementsByTagNameNS(
-                DC_NAMESPACE_URI, "macro");
-
-            Element macro = null;
-
-            for (int i = 0, N = macros.getLength(); i < N; ++i) {
-                Element m = (Element) macros.item(i);
-                if (name.equals(m.getAttribute("name"))) {
-                    macro = m;
-                    break;
-                }
-            }
-
-            if (macro != null) {
-                return macro.getChildNodes();
-            }
-            return null;
-        }
-
-        protected void ifClause(Node parent, Element current)
-        throws SQLException
-        {
-            String test = current.getAttribute("test");
-
-            if (test.length() == 0) {
-                log.warn("missing 'test' attribute in 'if'");
-                return;
-            }
-
-            Boolean result = evaluateXPathToBoolean(test);
-
-            if (result != null && result.booleanValue()) {
-                NodeList subs = current.getChildNodes();
-                for (int i = 0, N = subs.getLength(); i < N; ++i) {
-                    build(parent, subs.item(i));
-                }
-            }
-        }
-
-        protected void choose(Node parent, Element current)
-        throws SQLException
-        {
-            Node branch = null;
-
-            NodeList children = current.getChildNodes();
-            for (int i = 0, N = children.getLength(); i < N; ++i) {
-                Node child = children.item(i);
-                String ns = child.getNamespaceURI();
-                if (ns == null
-                || !ns.equals(DC_NAMESPACE_URI)
-                || child.getNodeType() != Node.ELEMENT_NODE
-                ) {
-                    continue;
-                }
-                String name = child.getLocalName();
-                if ("when".equals(name)) {
-                    Element when = (Element)child;
-                    String test = when.getAttribute("test");
-                    if (test.length() == 0) {
-                        log.warn("no 'test' attribute found for when");
-                        continue;
-                    }
-
-                    Boolean result = evaluateXPathToBoolean(test);
-                    if (result != null && result.booleanValue()) {
-                        branch = child;
-                        break;
-                    }
-
-                    continue;
-                }
-                else if ("otherwise".equals(name)) {
-                    branch = child;
-                    // No break here.
-                }
-            }
-
-            if (branch != null) {
-                NodeList subs = branch.getChildNodes();
-                for (int i = 0, N = subs.getLength(); i < N; ++i) {
-                    build(parent, subs.item(i));
-                }
-            }
-        }
-
-        protected XPathExpression getXPathExpression(String expr)
-        throws XPathExpressionException
-        {
-            XPathExpression x = expressions.get(expr);
-            if (x == null) {
-                XPath xpath = XPATH_FACTORY.newXPath();
-                xpath.setXPathVariableResolver(frames);
-                xpath.setXPathFunctionResolver(functionResolver);
-                x = xpath.compile(expr);
-                expressions.put(expr, x);
-            }
-            return x;
-        }
-
-        protected Object evaluateXPath(String expr, QName returnType) {
-
-            if (log.isDebugEnabled()) {
-                log.debug("evaluate: '" + expr + "'");
-            }
-
-            try {
-                XPathExpression x = getXPathExpression(expr);
-                return x.evaluate(EVAL_DOCUMENT, returnType);
-            }
-            catch (XPathExpressionException xpee) {
-                log.error("expression: " + expr, xpee);
-            }
-            return null;
-        }
-
-        protected Boolean evaluateXPathToBoolean(String expr) {
-
-            Object result = evaluateXPath(expr, XPathConstants.BOOLEAN);
-
-            return result instanceof Boolean
-                ? (Boolean)result
-                : null;
-        }
-
-        protected void convert(Element current) {
-
-            String variable = expand(current.getAttribute("var"));
-            String type     = expand(current.getAttribute("type"));
-
-            Object [] result = new Object[1];
-
-            if (frames.getStore(variable, result)) {
-                Object object = TypeConverter.convert(result[0], type);
-                frames.put(variable.toUpperCase(), object);
-            }
-        }
-
-
-        /** Put <dc:variable> content as variable on stackframes. */
-        protected void variable(Element current) {
-
-            String varName = expand(current.getAttribute("name"));
-            String expr    = current.getAttribute("expr");
-            String type    = current.getAttribute("type");
-
-            if (varName.length() == 0 || expr.length() == 0) {
-                log.error("dc:variable 'name' or 'expr' empty.");
-            }
-            else {
-                frames.put(
-                    varName.toUpperCase(),
-                    evaluateXPath(expr, typeToQName(type)));
-            }
-        }
-
-        protected String expand(String s) {
-            Matcher m = CompiledStatement.VAR.matcher(s);
-
-            Object [] result = new Object[1];
-
-            StringBuffer sb = new StringBuffer();
-            while (m.find()) {
-                String key = m.group(1);
-                result[0] = null;
-                if (frames.getStore(key, result)) {
-                    m.appendReplacement(
-                        sb, result[0] != null ? result[0].toString() : "");
-                }
-                else {
-                    m.appendReplacement(sb, "\\${" + key + "}");
-                }
-            }
-            m.appendTail(sb);
-            return sb.toString();
-        }
-
-        protected void evaluateAttributeValue(Attr attr) {
-            String value = attr.getValue();
-            if (value.indexOf('{') >= 0) {
-                StringBuffer sb = new StringBuffer();
-                Matcher m = BRACKET_XPATH.matcher(value);
-                while (m.find()) {
-                    String expr = m.group(1);
-                    Object result = evaluateXPath(expr, XPathConstants.STRING);
-                    if (result instanceof String) {
-                        m.appendReplacement(sb, (String)result);
-                    }
-                    else {
-                        m.appendReplacement(sb, "");
-                    }
-                }
-                m.appendTail(sb);
-                attr.setValue(sb.toString());
-            }
-        }
-
-        protected void build(Node parent, Node current)
-        throws SQLException
-        {
-            String ns = current.getNamespaceURI();
-            if (ns != null && ns.equals(DC_NAMESPACE_URI)) {
-                if (current.getNodeType() != Node.ELEMENT_NODE) {
-                    log.warn("need elements here");
-                }
-                else {
-                    String localName = current.getLocalName();
-                    Element curr = (Element)current;
-                    if ("attribute".equals(localName)) {
-                        attribute(parent, curr);
-                    }
-                    else if ("context".equals(localName)) {
-                        context(parent, curr);
-                    }
-                    else if ("if".equals(localName)) {
-                        ifClause(parent, curr);
-                    }
-                    else if ("choose".equals(localName)) {
-                        choose(parent, curr);
-                    }
-                    else if ("call-macro".equals(localName)) {
-                        callMacro(parent, curr);
-                    }
-                    else if ("macro-body".equals(localName)) {
-                        macroBody(parent, curr);
-                    }
-                    else if ("macro".equals(localName)
-                         ||  "comment".equals(localName)
-                         ||  "statement".equals(localName)) {
-                        // Simply ignore them.
-                    }
-                    else if ("element".equals(localName)) {
-                        element(parent, curr);
-                    }
-                    else if ("for-each".equals(localName)) {
-                        foreach(parent, curr);
-                    }
-                    else if ("filter".equals(localName)) {
-                        filter(parent, curr);
-                    }
-                    else if ("text".equals(localName)) {
-                        text(parent, curr);
-                    }
-                    else if ("variable".equals(localName)) {
-                        variable(curr);
-                    }
-                    else if ("convert".equals(localName)) {
-                        convert(curr);
-                    }
-                    else {
-                        log.warn("unknown '" + localName + "' -> ignore");
-                    }
-                }
-                return;
-            }
-
-            if (current.getNodeType() == Node.TEXT_NODE) {
-                String txt = current.getNodeValue();
-                if (txt != null && txt.trim().length() == 0) {
-                    return;
-                }
-            }
-
-            if (current.getNodeType() == Node.COMMENT_NODE) {
-                // Ignore XML comments
-                return;
-            }
-
-            Node copy = owner.importNode(current, false);
-
-            NodeList children = current.getChildNodes();
-            for (int i = 0, N = children.getLength(); i < N; ++i) {
-                build(copy, children.item(i));
-            }
-            if (copy.getNodeType() == Node.ELEMENT_NODE) {
-                NamedNodeMap nnm = ((Element)copy).getAttributes();
-                for (int i = 0, N = nnm.getLength(); i < N; ++i) {
-                    Node n = nnm.item(i);
-                    if (n.getNodeType() == Node.ATTRIBUTE_NODE) {
-                        evaluateAttributeValue((Attr)n);
-                    }
-                }
-            }
-            parent.appendChild(copy);
-        }
-    } // class BuildHelper
-
-
-    public Builder() {
-        compiledStatements = new HashMap<String, CompiledStatement>();
-        macros             = new HashMap<String, Element>();
-    }
-
-    public Builder(Document template) {
-        this();
-        this.template = template;
-        extractMacros();
-        compileStatements();
-    }
-
-    protected static QName typeToQName(String type) {
-        if ("number" .equals(type)) return XPathConstants.NUMBER;
-        if ("bool"   .equals(type)) return XPathConstants.BOOLEAN;
-        if ("node"   .equals(type)) return XPathConstants.NODE;
-        if ("nodeset".equals(type)) return XPathConstants.NODESET;
-        return XPathConstants.STRING;
-    }
-
-    /** Handle <dc:statement> elements. */
-    protected void compileStatements() {
-
-        NodeList nodes = template.getElementsByTagNameNS(
-            DC_NAMESPACE_URI, "statement");
-
-        for (int i = 0, N = nodes.getLength(); i < N; ++i) {
-            Element stmntElement = (Element)nodes.item(i);
-            String stmnt = trimStatement(stmntElement.getTextContent());
-            if (stmnt == null || stmnt.length() == 0) {
-                throw new IllegalArgumentException("found empty statement");
-            }
-            CompiledStatement cs = new CompiledStatement(stmnt);
-            // For faster lookup store a shortend string into the template.
-            stmnt = "s" + i;
-            stmntElement.setTextContent(stmnt);
-            compiledStatements.put(stmnt, cs);
-        }
-    }
-
-    protected void extractMacros() {
-        NodeList ms = template.getElementsByTagNameNS(
-            DC_NAMESPACE_URI, "macro");
-
-        for (int i = 0, N = ms.getLength(); i < N; ++i) {
-            Element m = (Element)ms.item(i);
-            macros.put(m.getAttribute("name"), m);
-        }
-    }
-
-    protected List<Node> rootsToList() {
-
-        NodeList roots = template.getElementsByTagNameNS(
-            DC_NAMESPACE_URI, "template");
-
-        List<Node> elements = new ArrayList<Node>();
-
-        for (int i = 0, N = roots.getLength(); i < N; ++i) {
-            NodeList rootChildren = roots.item(i).getChildNodes();
-            for (int j = 0, M = rootChildren.getLength(); j < M; ++j) {
-                Node child = rootChildren.item(j);
-                if (child.getNodeType() == Node.ELEMENT_NODE) {
-                    elements.add(child);
-                }
-            }
-        }
-
-        return elements;
-    }
-
-    protected static final String trimStatement(String stmnt) {
-        if (stmnt == null) return null;
-        //XXX: Maybe a bit to radical for multiline strings?
-        return STRIP_LINE_INDENT.matcher(stmnt.trim()).replaceAll(" ");
-    }
-
-    protected static Document getOwnerDocument(Node node) {
-        Document document = node.getOwnerDocument();
-        return document != null ? document : (Document)node;
-    }
-
-    public void build(
-        List<NamedConnection> connections,
-        Node                  output,
-        Map<String, Object>   parameters
-    )
-    throws SQLException
-    {
-        BuildHelper helper = new BuildHelper(output, connections, parameters);
-
-        helper.build();
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/BuilderPool.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,102 +0,0 @@
-package de.intevation.flys.artifacts.datacage.templating;
-
-import java.util.ArrayDeque;
-import java.util.Deque;
-import java.util.List;
-import java.util.Map;
-
-import java.sql.SQLException;
-
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Node;
-
-/** A little round robin pool of builders to mitigate 
-  * the fact the XML DOM documents are not thread safe.
-  */
-public class BuilderPool
-{
-    private static Logger log = Logger.getLogger(BuilderPool.class);
-
-    private static final int DEFAULT_POOL_SIZE = 4;
-
-    private static final int POOL_SIZE = Math.max(
-        Integer.getInteger("flys.datacage.pool.size", DEFAULT_POOL_SIZE), 1);
-
-    private Deque<Builder> pool;
-
-    public BuilderPool(Document document) {
-        this(document, POOL_SIZE);
-    }
-
-    public BuilderPool(Document document, int poolSize) {
-
-        if (log.isDebugEnabled()) {
-            log.debug("Create build pool with " + poolSize + " elements.");
-        }
-
-        pool = new ArrayDeque<Builder>(poolSize);
-        for (int i = 0; i < poolSize; ++i) {
-            pool.add(new Builder(cloneDocument(document)));
-        }
-    }
-
-    private final static Document cloneDocument(Document document) {
-
-        try {
-            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
-            DocumentBuilder db = dbf.newDocumentBuilder();
-
-            Node origRoot = document.getDocumentElement();
-
-            Document copy = db.newDocument();
-            Node copyRoot = copy.importNode(origRoot, true);
-            copy.appendChild(copyRoot);
-
-            return copy;
-        }
-        catch (ParserConfigurationException pce) {
-            log.error(pce);
-        }
-
-        log.error("Returning original document. This will lead to threading issues.");
-
-        return document;
-    }
-
-    public void build(
-        List<Builder.NamedConnection> connections,
-        Node                          output,
-        Map<String, Object>           parameters
-    )
-    throws SQLException
-    {
-        Builder builder;
-        synchronized (pool) {
-            try {
-                while ((builder = pool.poll()) == null) {
-                    pool.wait();
-                }
-            }
-            catch (InterruptedException ie) {
-                log.debug("Waiting for builder interrupted. Build canceled.");
-                return;
-            }
-        }
-        try {
-            builder.build(connections, output, parameters);
-        }
-        finally {
-            synchronized (pool) {
-                pool.add(builder);
-                pool.notify();
-            }
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/CompiledStatement.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,216 +0,0 @@
-package de.intevation.flys.artifacts.datacage.templating;
-
-import java.util.regex.Pattern;
-import java.util.regex.Matcher;
-
-import java.util.List;
-import java.util.Map;
-import java.util.TreeMap;
-import java.util.ArrayList;
-
-import java.sql.PreparedStatement;
-import java.sql.SQLException;
-import java.sql.Connection;
-import java.sql.ResultSet;
-
-import net.sf.ehcache.Cache;
-import net.sf.ehcache.Element;
-
-import de.intevation.flys.artifacts.cache.CacheFactory;
-
-import org.apache.log4j.Logger;
-
-/** SQL Statement, create PreparedStatement. */
-public class CompiledStatement
-{
-    private static Logger log = Logger.getLogger(CompiledStatement.class);
-
-    public static final String DATACAGE_DB_CACHE =
-        "datacage.db";
-
-    public static final Pattern VAR =
-        Pattern.compile("\\$\\{([a-zA-Z0-9_-]+)\\}");
-
-    protected String original;
-    protected String statement;
-
-    protected Map<String, List<Integer>> positions;
-
-    protected int numVars;
-
-    public class Instance {
-
-        protected PreparedStatement preparedStatement;
-
-        public Instance() {
-        }
-
-        /** Executes a Statement. */
-        protected ResultData executeCached(
-            Cache       cache,
-            Connection  connection,
-            StackFrames frames
-        )
-        throws SQLException
-        {
-            log.debug("executeCached");
-            Object [] values = new Object[numVars];
-
-            StringBuilder sb = new StringBuilder(original);
-
-            for (Map.Entry<String, List<Integer>> entry: positions.entrySet()) {
-                String key   = entry.getKey();
-                Object value = frames.get(key);
-                sb.append(';').append(key).append(':').append(value);
-                for (Integer index: entry.getValue()) {
-                    values[index] = value;
-                }
-            }
-
-            // XXX: Maybe too many collisions?
-            // String key = original + Arrays.hashCode(values);
-            String key = sb.toString();
-
-            Element element = cache.get(key);
-
-            if (element != null) {
-                return (ResultData)element.getValue();
-            }
-
-            if (preparedStatement == null) {
-                preparedStatement = connection.prepareStatement(statement);
-            }
-
-            for (int i = 0; i < values.length; ++i) {
-                preparedStatement.setObject(i+1, values[i]);
-            }
-
-            ResultData data;
-
-            if (log.isDebugEnabled()) {
-                log.debug("executing: " + statement);
-            }
-
-            ResultSet result = preparedStatement.executeQuery();
-            try {
-                data = new ResultData(preparedStatement.getMetaData())
-                    .addAll(result);
-            }
-            finally {
-                result.close();
-            }
-
-            element = new Element(key, data);
-            cache.put(element);
-
-            return data;
-        }
-
-        protected ResultData executeUncached(
-            Connection  connection,
-            StackFrames frames
-        )
-        throws SQLException
-        {
-            log.debug("executeUncached");
-            if (preparedStatement == null) {
-                if (log.isDebugEnabled()) {
-                    log.debug("preparing statement: " + statement);
-                }
-                preparedStatement = connection.prepareStatement(statement);
-            }
-
-            for (Map.Entry<String, List<Integer>> entry: positions.entrySet()) {
-                Object value = frames.get(entry.getKey());
-                for (Integer index: entry.getValue()) {
-                    preparedStatement.setObject(index+1, value);
-                }
-            }
-
-            if (log.isDebugEnabled()) {
-                log.debug("executing: " + statement);
-            }
-
-            ResultSet result = preparedStatement.executeQuery();
-            try {
-                return new ResultData(preparedStatement.getMetaData())
-                    .addAll(result);
-            }
-            finally {
-                result.close();
-            }
-        }
-
-        public ResultData execute(
-            Connection  connection,
-            StackFrames frames,
-            boolean     cached
-        )
-        throws SQLException
-        {
-            if (!cached) {
-                return executeUncached(connection, frames);
-            }
-
-            Cache cache = CacheFactory.getCache(DATACAGE_DB_CACHE);
-
-            return cache != null
-                ? executeCached(cache, connection, frames)
-                : executeUncached(connection, frames);
-        }
-
-        public void close() {
-            if (preparedStatement != null) {
-                try {
-                    preparedStatement.close();
-                }
-                catch (SQLException sqle) {
-                }
-                preparedStatement = null;
-            }
-        }
-    } // class Instance
-
-    public CompiledStatement() {
-    }
-
-    public CompiledStatement(String original) {
-        this.original = original;
-        // TreeMap to ensure order
-        positions = new TreeMap<String, List<Integer>>();
-        compile();
-    }
-
-    protected void compile() {
-
-        StringBuffer sb = new StringBuffer();
-
-        Matcher m = VAR.matcher(original);
-
-        int index = 0;
-
-        // Find variables like ${varname}.
-        while (m.find()) {
-            String key = m.group(1).toUpperCase();
-            List<Integer> indices = positions.get(key);
-            if (indices == null) {
-                indices = new ArrayList<Integer>();
-                positions.put(key, indices);
-            }
-            indices.add(index);
-            m.appendReplacement(sb, "?");
-            ++index;
-        }
-
-        m.appendTail(sb);
-
-        numVars = index;
-
-        statement = sb.toString();
-    }
-
-    public String getStatement() {
-        return statement;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/FunctionResolver.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,325 +0,0 @@
-package de.intevation.flys.artifacts.datacage.templating;
-
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-import javax.xml.namespace.QName;
-
-import javax.xml.xpath.XPathFunction;
-import javax.xml.xpath.XPathFunctionException;
-import javax.xml.xpath.XPathFunctionResolver;
-
-import org.apache.log4j.Logger;
-
-
-/** Resolves functions (e.g. dc:contains) in Datacage/Meta-Data system. */
-public class FunctionResolver
-implements   XPathFunctionResolver
-{
-    /** Home logger. */
-    private static Logger log = Logger.getLogger(FunctionResolver.class);
-
-    public static final String FUNCTION_NAMESPACE_URI = "dc";
-
-    public static final double FAR_AWAY = 99999d;
-
-    public static final class Entry {
-
-        String        name;
-        XPathFunction function;
-        int           arity;
-
-        public Entry() {
-        }
-
-        public Entry(String name, XPathFunction function, int arity) {
-            this.name     = name;
-            this.function = function;
-            this.arity    = arity;
-        }
-    } // class Entry
-
-    /** List of functions. */
-    protected List<Entry> functions;
-
-    protected Builder.BuildHelper buildHelper;
-
-
-    public FunctionResolver() {
-        this(null);
-    }
-
-    public FunctionResolver(Builder.BuildHelper buildHelper) {
-        this.buildHelper = buildHelper;
-
-        functions = new ArrayList<Entry>();
-
-        addFunction("contains", 2, new XPathFunction() {
-            @Override
-            public Object evaluate(List args) throws XPathFunctionException {
-                return contains(args);
-            }
-        });
-
-        addFunction("fromValue", 3, new XPathFunction() {
-            @Override
-            public Object evaluate(List args) throws XPathFunctionException {
-                return fromValue(args);
-            }
-        });
-
-        addFunction("toValue", 3, new XPathFunction() {
-            @Override
-            public Object evaluate(List args) throws XPathFunctionException {
-                return toValue(args);
-            }
-        });
-
-        addFunction("replace", 3, new XPathFunction() {
-            @Override
-            public Object evaluate(List args) throws XPathFunctionException {
-                return replace(args);
-            }
-        });
-
-        addFunction("has-result", 0, new XPathFunction() {
-            @Override
-            public Object evaluate(List args) throws XPathFunctionException {
-                return FunctionResolver.this.buildHelper.hasResult();
-            }
-        });
-
-        addFunction("date-format", 2, new XPathFunction() {
-            @Override
-            public Object evaluate(List args) throws XPathFunctionException {
-                return dateFormat(args);
-            }
-        });
-    }
-
-    /**
-     * Create a new function.
-     * @param name  Name of the function.
-     * @param arity Number of arguments for function.
-     * @param function the function itself.
-     */
-    public void addFunction(String name, int arity, XPathFunction function) {
-        functions.add(new Entry(name, function, arity));
-    }
-
-    @Override
-    public XPathFunction resolveFunction(QName functionName, int arity) {
-
-        if (!functionName.getNamespaceURI().equals(FUNCTION_NAMESPACE_URI)) {
-            return null;
-        }
-
-        String name = functionName.getLocalPart();
-        for (Entry entry: functions) {
-            if (entry.arity == arity && entry.name.equals(name)) {
-                return entry.function;
-            }
-        }
-
-        return null;
-    }
-
-    /** Implementation of case-ignoring dc:contains. */
-    public Object contains(List args) throws XPathFunctionException {
-        Object haystack = args.get(0);
-        Object needle   = args.get(1);
-
-        if (needle instanceof String && !(haystack instanceof String)) {
-            needle = ((String)needle).toUpperCase();
-        }
-
-        try {
-            if (haystack instanceof Collection) {
-                return Boolean.valueOf(
-                    ((Collection)haystack).contains(needle));
-            }
-
-            if (haystack instanceof Map) {
-                return Boolean.valueOf(
-                    ((Map)haystack).containsKey(needle));
-            }
-
-            if (haystack instanceof Object []) {
-                for (Object straw: (Object [])haystack) {
-                    if (straw.equals(needle)) {
-                        return Boolean.TRUE;
-                    }
-                }
-            }
-
-            if (haystack instanceof String && needle instanceof String) {
-                String h = (String)haystack;
-                String n = (String)needle;
-                return h.contains(n);
-            }
-
-            return Boolean.FALSE;
-        }
-        catch (Exception e) {
-            log.error(e);
-            throw new XPathFunctionException(e);
-        }
-    }
-
-    /** Implementation for getting the minimum value of location or distance
-     *  dc:fromValue.
-     */
-    public Object fromValue(List args) throws XPathFunctionException {
-        Object mode      = args.get(0);
-        Object locations = args.get(1);
-        Object from      = args.get(2);
-
-        if (!(mode instanceof String)){
-            return -FAR_AWAY;
-        }
-
-        if (mode.equals("locations")) {
-            if (!(locations instanceof String)) {
-                return -FAR_AWAY;
-            }
-            String loc = ((String)locations).replace(" ", "");
-            String[] split = loc.split(",");
-            if (split.length < 1) {
-                return -FAR_AWAY;
-            }
-            try {
-                double min = Double.parseDouble(split[0]);
-                for (int i = 1; i < split.length; ++i) {
-                    double v = Double.parseDouble(split[i]);
-                    if (v < min) {
-                        min = v;
-                    }
-                }
-                return min;
-            }
-            catch (NumberFormatException nfe) {
-                return -FAR_AWAY;
-            }
-        }
-        else if (mode.equals("distance")) {
-            if (!(from instanceof String)) {
-                return -FAR_AWAY;
-            }
-            String f = (String)from;
-            try {
-                return Double.parseDouble(f);
-            }
-            catch(NumberFormatException nfe) {
-                return -FAR_AWAY;
-            }
-        }
-        else {
-            return -FAR_AWAY;
-        }
-    }
-
-    /** Implementation for getting the maximum value of location or distance
-     *  dc:toValue.
-     */
-    public Object toValue(List args) throws XPathFunctionException {
-        Object mode      = args.get(0);
-        Object locations = args.get(1);
-        Object to        = args.get(2);
-
-        if (!(mode instanceof String)){
-            return FAR_AWAY;
-        }
-
-        if (mode.equals("locations")) {
-            if (!(locations instanceof String)) {
-                return FAR_AWAY;
-            }
-            try {
-                String loc = ((String)locations).replace(" ", "");
-                String[] split = loc.split(",");
-                if (split.length < 1) {
-                    return FAR_AWAY;
-                }
-                double max = Double.parseDouble(split[0]);
-                for (int i = 1; i < split.length; ++i) {
-                    double v = Double.parseDouble(split[i]);
-                    if (v > max) {
-                        max = v;
-                    }
-                }
-                return max;
-            }
-            catch (NumberFormatException nfe) {
-                return FAR_AWAY;
-            }
-        }
-        else if (mode.equals("distance")) {
-            if (!(to instanceof String)) {
-                return FAR_AWAY;
-            }
-            else {
-                String t = (String)to;
-                try {
-                    return Double.parseDouble(t);
-                }
-                catch(NumberFormatException nfe) {
-                    return FAR_AWAY;
-                }
-            }
-        }
-        else {
-            return FAR_AWAY;
-        }
-    }
-
-    /** Implementation for doing a string replace
-     *  dc:replace
-     */
-    public Object replace(List args) throws XPathFunctionException {
-        Object haystack    = args.get(0);
-        Object needle      = args.get(1);
-        Object replacement = args.get(2);
-
-        if (needle instanceof String &&
-            haystack instanceof String &&
-            replacement instanceof String) {
-            return ((String)haystack).replace(
-                    (String)needle, (String)replacement);
-        } else {
-            return haystack;
-        }
-    }
-
-    public Object dateFormat(List args) throws XPathFunctionException {
-        Object pattern = args.get(0);
-        Object date    = args.get(1);
-
-        try {
-            // Oracle does not return a date object but an oracle.sql.TIMESTAMP
-            Method meth = date.getClass().getMethod("dateValue", new Class[] {});
-            date = meth.invoke(date, new Object [] {});
-        } catch (IllegalArgumentException e) {
-        } catch (IllegalAccessException e) {
-        } catch (InvocationTargetException e) {
-        } catch (NoSuchMethodException e) {
-        }
-        if (pattern instanceof String && date instanceof Date) {
-            try {
-                // TODO: Take locale into account.
-                return new SimpleDateFormat((String)pattern).format((Date)date);
-            }
-            catch (IllegalArgumentException iae) {
-                throw new XPathFunctionException(iae);
-            }
-        }
-        return "";
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/ResultData.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,84 +0,0 @@
-package de.intevation.flys.artifacts.datacage.templating;
-
-import java.io.Serializable;
-
-import java.sql.ResultSetMetaData;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-
-import java.util.List;
-import java.util.ArrayList;
-
-import org.apache.log4j.Logger;
-
-
-/** Result Data from a DB/SQL query. */
-public class ResultData
-implements   Serializable
-{
-    private static Logger log = Logger.getLogger(ResultData.class);
-
-    protected String [] columns;
-
-    protected List<Object []> rows;
-
-    public ResultData() {
-        rows = new ArrayList<Object []>();
-    }
-
-    public ResultData(String [] columns, List<Object []> rows) {
-        this.columns = columns;
-        this.rows = rows;
-    }
-
-    public ResultData(ResultSetMetaData meta)
-    throws SQLException
-    {
-        this();
-
-        boolean debug = log.isDebugEnabled();
-
-        int N = meta.getColumnCount();
-
-        columns = new String[N];
-
-        if (debug) {
-            log.debug("ResultSet column names:");
-        }
-
-        for (int i = 1; i <= N; ++i) {
-            columns[i-1] = meta.getColumnLabel(i).toUpperCase();
-            if (debug) {
-                log.debug("    " + i + ": " + columns[i-1]);
-            }
-        }
-    }
-
-    public String [] getColumnLabels() {
-        return columns;
-    }
-
-    public ResultData addAll(ResultSet result) throws SQLException {
-        while (result.next()) {
-            add(result);
-        }
-        return this;
-    }
-
-    public void add(ResultSet result) throws SQLException {
-        Object [] row = new Object[columns.length];
-        for (int i = 0; i < columns.length; ++i) {
-            row[i] = result.getObject(i+1);
-        }
-        rows.add(row);
-    }
-
-    public List<Object []> getRows() {
-        return rows;
-    }
-
-    public boolean isEmpty() {
-        return rows.isEmpty();
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/StackFrames.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,168 +0,0 @@
-package de.intevation.flys.artifacts.datacage.templating;
-
-import java.util.ArrayList;
-import java.util.Map;
-import java.util.List;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Set;
-
-import javax.xml.xpath.XPathVariableResolver;
-
-import javax.xml.namespace.QName;
-
-import org.apache.log4j.Logger;
-
-
-/**
- * Maintains stack of 'frames' which are maps from string to object.
- * Used for variables in datacage/meta-data system.
- */
-public class StackFrames
-implements   XPathVariableResolver
-{
-    private static Logger log = Logger.getLogger(StackFrames.class);
-
-    public static final Object NULL = new Object();
-
-    /** The frames (used like a stack). */
-    protected List<Map<String, Object>> frames;
-
-    public StackFrames() {
-        frames = new ArrayList<Map<String, Object>>();
-    }
-
-    public StackFrames(Map<String, Object> initialFrame) {
-        this();
-        if (initialFrame != null) {
-            frames.add(new HashMap<String, Object>(initialFrame));
-        }
-    }
-
-    /** Push a new String->Object map. */
-    public void enter() {
-        frames.add(new HashMap<String, Object>());
-    }
-
-    /** Pop/Remove last String->Object map. */
-    public void leave() {
-        frames.remove(frames.size()-1);
-    }
-
-    /** Put Key/Value in last String->Object map. */
-    public void put(String key, Object value) {
-        int N = frames.size();
-        if (N > 0) {
-            frames.get(N-1).put(key, value);
-        }
-    }
-
-    /** Put multiple Key/Values in last String->Object map. */
-    public void put(String [] keys, Object [] values) {
-        Map<String, Object> top = frames.get(frames.size()-1);
-        for (int i = 0; i < keys.length; ++i) {
-            top.put(keys[i], values[i]);
-        }
-    }
-
-    /** Check last frame (string->object map) for key. */
-    public boolean containsKey(String key) {
-        key = key.toUpperCase();
-        for (int i = frames.size()-1; i >= 0; --i) {
-            if (frames.get(i).containsKey(key)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Get element (variable) key.
-     * Returns null if not found.
-     * @param key name to resolve
-     * @return resolution, null if not found.
-     */
-    public Object get(String key) {
-        return get(key, null);
-    }
-
-    /** result[0] is modified with value when true returned.
-     * @return false if key not found in any frame. */
-    public boolean getStore(String key, Object [] result) {
-
-        key = key.toUpperCase();
-
-        for (int i = frames.size()-1; i >= 0; --i) {
-            Map<String, Object> frame = frames.get(i);
-            if (frame.containsKey(key)) {
-                result[0] = frame.get(key);
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    public Object get(String key, Object def) {
-
-        key = key.toUpperCase();
-
-        for (int i = frames.size()-1; i >= 0; --i) {
-            Map<String, Object> frame = frames.get(i);
-            if (frame.containsKey(key)) {
-                return frame.get(key);
-            }
-        }
-
-        return def;
-    }
-
-    public Object getNull(String key) {
-        return getNull(key, null);
-    }
-
-    public Object getNull(String key, Object def) {
-
-        key = key.toUpperCase();
-
-        for (int i = frames.size()-1; i >= 0; --i) {
-            Map<String, Object> frame = frames.get(i);
-            if (frame.containsKey(key)) {
-                Object value = frame.get(key);
-                return value != null ? value : NULL;
-            }
-        }
-
-        return def;
-    }
-
-    @Override
-    public Object resolveVariable(QName variableName) {
-        if (log.isDebugEnabled()) {
-            log.debug("resolve var: " + variableName);
-        }
-
-        return getNull(variableName.getLocalPart());
-    }
-
-    public String dump() {
-        StringBuilder sb = new StringBuilder("[");
-        Set<String> already = new HashSet<String>();
-
-        boolean first = true;
-
-        for (int i = frames.size()-1; i >= 0; --i) {
-            Map<String, Object> frame = frames.get(i);
-            for (Map.Entry<String, Object> entry: frame.entrySet()) {
-                if (already.add(entry.getKey())) {
-                    if (first) { first = false;   }
-                    else       { sb.append(", "); }
-                    sb.append('\'').append(entry.getKey())
-                      .append("'='").append(entry.getValue()).append('\'');
-                }
-            }
-        }
-        return sb.append(']').toString();
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/TypeConverter.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,31 +0,0 @@
-package de.intevation.flys.artifacts.datacage.templating;
-
-public class TypeConverter
-{
-    private TypeConverter() {
-    }
-
-    public static Object convert(Object object, String type) {
-
-        if (type == null) {
-            return object;
-        }
-
-        if ("Integer".equals(type)) {
-            return Integer.valueOf(object.toString());
-        }
-
-        if ("Double".equals(type)) {
-            return Double.valueOf(object.toString());
-        }
-
-        if ("String".equals(type)) {
-            return object.toString();
-        }
-
-        // TODO: Add more types
-
-        return object;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/geom/Lines.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,403 +0,0 @@
-package de.intevation.flys.artifacts.geom;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Iterator;
-
-import java.awt.geom.Point2D;
-import java.awt.geom.Line2D;
-
-import de.intevation.flys.artifacts.math.Linear;
-
-import org.apache.log4j.Logger;
-
-import gnu.trove.TDoubleArrayList;
-
-/**
- * Utility to create lines (intersect water with cross-section etc).
- */
-public class Lines
-{
-    private static Logger log = Logger.getLogger(Lines.class);
-
-    public static final double EPSILON = 1e-4;
-
-    public static enum Mode { UNDEF, WET, DRY };
-
-
-    /** Never instantiate Lines, use static functions instead. */
-    protected Lines() {
-    }
-
-
-    /**
-     * Calculate area of polygon with four vertices.
-     * @return area of polygon with four vertices.
-     */
-    public static double area(Point2D p1, Point2D p2, Point2D p3, Point2D p4) {
-        double[] x = new double[] {p1.getX(), p2.getX(), p3.getX(), p4.getX(), p1.getX() };
-        double[] y = new double[] {p1.getY(), p2.getY(), p3.getY(), p4.getY(), p1.getY() };
-        double area = 0d;
-        for (int i=0; i <4; i++) {
-            area += (x[i] * y[i+1]) - (x[i+1] * y[i]);
-        }
-        return Math.abs(area * 0.5d);
-    }
-
-
-    /**
-     * Calculate the 'length' of the given lines.
-     * @param lines lines of which to calculate length.
-     */
-    public static double length(List<Line2D> lines) {
-        double sum = 0d;
-        for (Line2D line: lines) {
-            double xDiff = line.getX1() - line.getX2();
-            double yDiff = line.getY1() - line.getY2();
-            sum += Math.sqrt(xDiff*xDiff + yDiff*yDiff);
-        }
-        return sum;
-    }
-
-
-    /** List of lines and a double-precision area. */
-    private static class ListWithArea {
-        public List<Line2D> lines;
-        public double area;
-        public ListWithArea(List<Line2D> lines, double area) {
-            this.lines = lines;
-            this.area = area;
-        }
-    }
-
-
-    /**
-     * For a cross section given as points and a waterlevel (in meters),
-     * create a set of lines that represent the water surface, assuming it
-     * is distributed horizontally equally.
-     * @param points the points describing the river bed.
-     * @param waterLevel the height of the horizontal water line.
-     * @return A list of Lines representing the water surface and the
-     *         calculated area between water surface and river bed.
-     */
-    public static ListWithArea fillWater(List<Point2D> points, double waterLevel) {
-
-        boolean debug = log.isDebugEnabled();
-
-        if (debug) {
-            log.debug("fillWater");
-            log.debug("----------------------------");
-        }
-
-        List<Line2D> result = new ArrayList();
-
-        int N = points.size();
-
-        if (N == 0) {
-            return new ListWithArea(result, 0d);
-        }
-
-        if (N == 1) {
-            Point2D p = points.get(0);
-            // Only generate point if over profile
-            if (waterLevel > p.getY()) {
-                result.add(new Line2D.Double(
-                    p.getX(), waterLevel,
-                    p.getX(), waterLevel));
-            }
-            // TODO continue calculating area.
-            return new ListWithArea(result, 0d);
-        }
-
-        double minX =  Double.MAX_VALUE;
-        double minY =  Double.MAX_VALUE;
-        double maxX = -Double.MAX_VALUE;
-        double maxY = -Double.MAX_VALUE;
-
-        // To ensure for sequences of equals x's that
-        // the original index order is preserved.
-        for (Point2D p: points) {
-            double x = p.getX(), y = p.getY();
-            if (x < minX) minX = x;
-            if (x > maxX) maxX = x;
-            if (y < minY) minY = y;
-            if (y > maxY) maxY = y;
-        }
-
-        if (minY > waterLevel) { // profile completely over water level
-            log.debug("complete over water");
-            return new ListWithArea(result, 0d);
-        }
-
-        if (waterLevel > maxY) { // water floods profile
-            log.debug("complete under water");
-            result.add(new Line2D.Double(minX, waterLevel, maxX, waterLevel));
-            return new ListWithArea(result, 0d);
-        }
-
-        // Water is sometimes above, sometimes under profile.
-        Mode mode = Mode.UNDEF;
-
-        double startX = minX;
-
-        double area = 0d;
-        // Walking along the profile.
-        for (int i = 1; i < N; ++i) {
-            Point2D p1 = points.get(i-1);
-            Point2D p2 = points.get(i);
-
-            if (p1.getY() < waterLevel && p2.getY() < waterLevel) {
-                // completely under water
-                if (debug) {
-                    log.debug("under water: " + p1 + " " + p2);
-                }
-                if (mode != Mode.WET) {
-                    startX = p1.getX();
-                    mode = Mode.WET;
-                }
-                area += area(p1, p2,
-                    new Point2D.Double(p2.getX(), waterLevel),
-                    new Point2D.Double(p1.getX(), waterLevel));
-                continue;
-            }
-
-            // TODO trigger area calculation
-            if (p1.getY() > waterLevel && p2.getY() > waterLevel) {
-                if (debug) {
-                    log.debug("over water: " + p1 + " " + p2);
-                }
-                // completely over water
-                if (mode == Mode.WET) {
-                    log.debug("over/wet");
-                    result.add(new Line2D.Double(
-                        startX, waterLevel,
-                        p1.getX(), waterLevel));
-                }
-                mode = Mode.DRY;
-                continue;
-            }
-
-            // TODO trigger area calculation
-            if (Math.abs(p1.getX() - p2.getX()) < EPSILON) {
-                // vertical line
-                switch (mode) {
-                    case WET:
-                        log.debug("vertical/wet");
-                        mode = Mode.DRY;
-                        result.add(new Line2D.Double(
-                            startX, waterLevel,
-                            p1.getX(), waterLevel));
-                        break;
-                    case DRY:
-                        log.debug("vertical/dry");
-                        mode = Mode.WET;
-                        startX = p2.getX();
-                        break;
-                    default: // UNDEF
-                        log.debug("vertical/undef");
-                        if (p2.getY() < waterLevel) {
-                            mode = Mode.WET;
-                            startX = p2.getX();
-                        }
-                        else {
-                            mode = Mode.DRY;
-                        }
-                }
-                continue;
-            }
-
-            // check if waterlevel directly hits the vertices;
-
-            boolean p1W = Math.abs(waterLevel - p1.getY()) < EPSILON;
-            boolean p2W = Math.abs(waterLevel - p2.getY()) < EPSILON;
-
-            // TODO trigger area calculation
-            if (p1W || p2W) {
-                if (debug) {
-                    log.debug("water hits vertex: " + p1 + " " + p2 + " " + mode);
-                }
-                if (p1W && p2W) { // parallel to water -> dry
-                    log.debug("water hits both vertices");
-                    if (mode == Mode.WET) {
-                        result.add(new Line2D.Double(
-                            startX, waterLevel,
-                            p1.getX(), waterLevel));
-                    }
-                    mode = Mode.DRY;
-                }
-                else if (p1W) { // p1 == waterlevel
-                    log.debug("water hits first vertex");
-                    if (p2.getY() > waterLevel) { // --> dry
-                        if (mode == Mode.WET) {
-                            result.add(new Line2D.Double(
-                                startX, waterLevel,
-                                p1.getX(), waterLevel));
-                        }
-                        mode = Mode.DRY;
-                    }
-                    else { // --> wet
-                        if (mode != Mode.WET) {
-                            startX = p1.getX();
-                            mode = Mode.WET;
-                        }
-                        area += area(p1, p2,
-                            new Point2D.Double(p2.getX(), waterLevel),
-                            new Point2D.Double(p2.getX(), waterLevel));
-                    }
-                }
-                else { // p2 == waterlevel
-                    log.debug("water hits second vertex");
-                    if (p1.getY() > waterLevel) { // --> wet
-                        if (mode != Mode.WET) {
-                            startX = p2.getX();
-                            mode = Mode.WET;
-                        }
-                    }
-                    else { // --> dry
-                        if (mode == Mode.WET) {
-                            result.add(new Line2D.Double(
-                                startX, waterLevel,
-                                p2.getX(), waterLevel));
-                        }
-                        mode = Mode.DRY;
-                        area += area(p1, p2,
-                            new Point2D.Double(p1.getX(), waterLevel),
-                            new Point2D.Double(p1.getX(), waterLevel));
-                    }
-                }
-                if (debug) {
-                    log.debug("mode is now: " + mode);
-                }
-                continue;
-            }
-
-            // TODO trigger area calculation
-            // intersection case
-            double x = Linear.linear(
-                waterLevel,
-                p1.getY(), p2.getY(),
-                p1.getX(), p2.getX());
-
-            if (debug) {
-                log.debug("intersection p1:" + p1);
-                log.debug("intersection p2:" + p2);
-                log.debug("intersection at x: " + x);
-            }
-
-            // Add area of that part of intersection that is 'wet'.
-            if (p1.getY() > waterLevel) {
-                area += area(new Point2D.Double(x, waterLevel),
-                             p2,
-                             new Point2D.Double(p2.getX(), waterLevel),
-                             new Point2D.Double(x, waterLevel));
-            }
-            else {
-                area += area(new Point2D.Double(x, waterLevel),
-                             p1,
-                             new Point2D.Double(p1.getX(), waterLevel),
-                             new Point2D.Double(x, waterLevel));
-            }
-
-            switch (mode) {
-                case WET:
-                    log.debug("intersect/wet");
-                    mode = Mode.DRY;
-                    result.add(new Line2D.Double(
-                        startX, waterLevel,
-                        x, waterLevel));
-                    break;
-
-                case DRY:
-                    log.debug("intersect/dry");
-                    mode   = Mode.WET;
-                    startX = x;
-                    break;
-
-                default: // UNDEF
-                    log.debug("intersect/undef");
-                    if (p2.getY() > waterLevel) {
-                        log.debug("intersect/undef/over");
-                        mode = Mode.DRY;
-                        result.add(new Line2D.Double(
-                            p1.getX(), waterLevel,
-                            x, waterLevel));
-                    }
-                    else {
-                        mode = Mode.WET;
-                        startX = x;
-                    }
-            } // switch mode
-        } // for all points p[i] and p[i-1]
-
-        if (mode == Mode.WET) {
-            result.add(new Line2D.Double(
-                startX, waterLevel,
-                maxX, waterLevel));
-        }
-
-        return new ListWithArea(result, area);
-    }
-
-
-    /**
-     * Class holding points that form lines and the calculated length.
-     */
-    public static class LineData {
-        public double [][] points;
-        public double width;
-        public double area;
-        public LineData(double[][] points, double width, double area) {
-            this.points = points;
-            this.width = width;
-            this.area = area;
-        }
-    }
-
-
-    /** Return length of a single line. */
-    public static double lineLength(Line2D line) {
-        double xDiff = line.getX1() - line.getX2();
-        double yDiff = line.getY1() - line.getY2();
-        return Math.sqrt(xDiff*xDiff + yDiff*yDiff);
-    }
-
-
-    /**
-     * @param points the riverbed.
-     */
-    public static LineData createWaterLines(
-        List<Point2D> points,
-        double        waterlevel
-    ) {
-        ListWithArea listAndArea = fillWater(points, waterlevel);
-        List<Line2D> lines = listAndArea.lines;
-
-        TDoubleArrayList lxs = new TDoubleArrayList();
-        TDoubleArrayList lys = new TDoubleArrayList();
-        double linesLength = 0.0f;
-
-        for (Iterator<Line2D> iter = lines.iterator(); iter.hasNext();) {
-            Line2D  line = iter.next();
-            Point2D p1   = line.getP1();
-            Point2D p2   = line.getP2();
-            lxs.add(p1.getX());
-            lys.add(p1.getY());
-            lxs.add(p2.getX());
-            lys.add(p2.getY());
-
-            // Length calculation.
-            linesLength += lineLength(line);
-
-            if (iter.hasNext()) {
-                lxs.add(Double.NaN);
-                lys.add(Double.NaN);
-            }
-        }
-
-        return new LineData(
-            new double [][] { lxs.toNativeArray(), lys.toNativeArray() },
-            linesLength, listAndArea.area
-            );
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/map/PrintMap.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,131 +0,0 @@
-package de.intevation.flys.artifacts.map;
-
-import java.awt.Color;
-import java.awt.Rectangle;
-
-import java.io.File;
-import java.io.IOException;
-
-import java.awt.Graphics2D;
-import java.awt.image.BufferedImage;
-
-import java.util.List;
-
-import java.net.URL;
-import java.net.MalformedURLException;
-
-import javax.imageio.ImageIO;
-
-
-import org.geotools.data.ows.Layer;
-import org.geotools.data.ows.WMSCapabilities;
-import org.geotools.data.wms.WebMapServer;
-import org.geotools.geometry.jts.ReferencedEnvelope;
-import org.geotools.map.MapContext;
-import org.geotools.map.WMSMapLayer;
-import org.geotools.ows.ServiceException;
-import org.geotools.renderer.lite.StreamingRenderer;
-import org.geotools.renderer.GTRenderer;
-
-
-public class PrintMap {
-
-    public static final String DEFAULT_WMS = "http://map1.naturschutz.rlp.de/service_lanis/mod_wms/wms_getmap.php?mapfile=group_gdide&REQUEST=GetCapabilities&SERVICE=WMS";
-    public static final String DEFAULT_OUTFILE = "~/map.jpeg";
-
-    public static final String MAPSERVER = System.getProperty("wms", DEFAULT_WMS);
-    public static final String MAP_IMAGE = System.getProperty("outfile", DEFAULT_OUTFILE);
-
-
-    public static void main(String[] args) {
-        System.out.println("-> start PrintMap");
-        System.out.println("  -> Print layers of WMS: " + MAPSERVER);
-
-        try {
-            WebMapServer    server = getMapserver();
-            WMSMapLayer[] wmsLayer = getWMSLayers(server);
-
-            MapContext mapcontent = new MapContext( wmsLayer );
-            mapcontent.setTitle(" NEW MAP CONTENT TITLE ");
-
-            printMap(mapcontent);
-        }
-        catch (Exception e) {
-            e.printStackTrace();
-        }
-
-        System.out.println("-> finished PrintMap");
-    }
-
-
-    public static void printMap(MapContext map) throws Exception {
-        int imageWidth = 600;
-
-        GTRenderer renderer = new StreamingRenderer();
-        renderer.setContext(map);
-
-        Rectangle imageBounds = null;
-        ReferencedEnvelope mapBounds = null;
-
-        try {
-            mapBounds = map.getLayerBounds();
-            double heightToWidth = mapBounds.getSpan(1) / mapBounds.getSpan(0);
-            imageBounds = new Rectangle(
-                0, 0,
-                imageWidth,
-                (int) Math.round(imageWidth * heightToWidth));
-
-        }
-        catch (Exception e) {
-            // failed to access map layers
-            throw new RuntimeException(e);
-        }
-
-        BufferedImage image = new BufferedImage(imageBounds.width, imageBounds.height, BufferedImage.TYPE_INT_RGB);
-        Graphics2D gr = image.createGraphics();
-        gr.setPaint(Color.WHITE);
-        gr.fill(imageBounds);
-
-        try {
-            renderer.paint(gr, imageBounds, mapBounds);
-            File fileToSave = new File(MAP_IMAGE);
-            ImageIO.write(image, "jpeg", fileToSave);
-        }
-        catch (IOException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-
-    public static WebMapServer getMapserver() throws MalformedURLException, IOException, ServiceException {
-        return new WebMapServer(getServerUrl());
-    }
-
-
-    public static URL getServerUrl() throws MalformedURLException {
-        return new URL(MAPSERVER);
-    }
-
-
-    public static WMSMapLayer[] getWMSLayers(WebMapServer server) {
-        if (server == null) {
-            System.out.println("WebMapServer == null");
-            throw new RuntimeException("WebMapServer == null");
-        }
-
-        WMSCapabilities capabilities = server.getCapabilities();
-
-        List<Layer> layers = capabilities.getLayerList();
-        WMSMapLayer[] wmslayers = new WMSMapLayer[layers.size()];
-
-        for (int i = 0, L = layers.size(); i < L; i++) {
-            Layer l = layers.get(i);
-
-            System.out.println("   -> add layer: " + l);
-            wmslayers[i] = new WMSMapLayer(server, l);
-        }
-
-        return wmslayers;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/map/RiverMapfileGeneratorStarter.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +0,0 @@
-package de.intevation.flys.artifacts.map;
-
-import de.intevation.artifactdatabase.LifetimeListener;
-import de.intevation.artifacts.GlobalContext;
-import de.intevation.flys.utils.RiverMapfileGenerator;
-
-import org.apache.log4j.Logger;
-import org.w3c.dom.Document;
-
-/**
- * LifetimeListener that creates a Mapfile containing river axis layers.
- * The listener is called when the server has completed its startup.
- *
- * @author <a href="mailto:christian.lins@intevation.de">Christian Lins</a>
- */
-public class RiverMapfileGeneratorStarter implements LifetimeListener {
-
-    private static Logger logger = Logger.getLogger(RiverMapfileGeneratorStarter.class);
-
-    @Override
-    public void setup(Document document) {
-        // Nothing to setup here
-    }
-
-    /**
-     * Calls RiverMapfileGenerator.generate().
-     */
-    @Override
-    public void systemUp(GlobalContext globalContext) {
-        logger.debug("systemUp()");
-
-        RiverMapfileGenerator fmfg = new RiverMapfileGenerator();
-        fmfg.generate();
-    }
-
-    @Override
-    public void systemDown(GlobalContext globalContext) {
-        // No, we're not cleaning up our generated mapfile
-    }
-
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/AddScaleFunction.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,19 +0,0 @@
-package de.intevation.flys.artifacts.math;
-
-public class AddScaleFunction
-implements   Function
-{
-    protected double b;
-    protected double m;
-
-    public AddScaleFunction(double b, double m) {
-        this.b = b;
-        this.m = m;
-    }
-
-    @Override
-    public double value(double x) {
-        return (x + b)*m;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/BackJumpCorrector.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,217 +0,0 @@
-package de.intevation.flys.artifacts.math;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import java.io.Serializable;
-
-import org.apache.commons.math.analysis.interpolation.SplineInterpolator;
-
-import org.apache.commons.math.analysis.polynomials.PolynomialSplineFunction;
-
-import org.apache.commons.math.ArgumentOutsideDomainException;
-
-import org.apache.commons.math.exception.MathIllegalArgumentException;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.flys.artifacts.model.Calculation;
-
-import de.intevation.flys.utils.DoubleUtil;
-
-public class BackJumpCorrector
-implements   Serializable
-{
-    private static Logger log = Logger.getLogger(BackJumpCorrector.class);
-
-    protected ArrayList<Double> backjumps;
-
-    protected double [] corrected;
-
-    public BackJumpCorrector() {
-        backjumps = new ArrayList<Double>();
-    }
-
-    public boolean hasBackJumps() {
-        return !backjumps.isEmpty();
-    }
-
-    public List<Double> getBackJumps() {
-        return backjumps;
-    }
-
-    public double [] getCorrected() {
-        return corrected;
-    }
-
-    public boolean doCorrection(
-        double []   km,
-        double []   ws,
-        Calculation errors
-    ) {
-        boolean wsUp = DoubleUtil.isIncreasing(ws);
-
-        if (wsUp) {
-            km = DoubleUtil.swapClone(km);
-            ws = DoubleUtil.swapClone(ws);
-        }
-
-        boolean kmUp = DoubleUtil.isIncreasing(km);
-
-        if (!kmUp) {
-            km = DoubleUtil.sumDiffs(km);
-        }
-
-        if (log.isDebugEnabled()) {
-            log.debug("BackJumpCorrector.doCorrection ------- enter");
-            log.debug("  km increasing: " + DoubleUtil.isIncreasing(km));
-            log.debug("  ws increasing: " + DoubleUtil.isIncreasing(ws));
-            log.debug("BackJumpCorrector.doCorrection ------- leave");
-        }
-
-        boolean hasBackJumps = doCorrectionClean(km, ws, errors);
-
-        if (hasBackJumps && wsUp) {
-            // mirror back
-            DoubleUtil.swap(corrected);
-        }
-
-        return hasBackJumps;
-    }
-
-    protected boolean doCorrectionClean(
-        double []   km,
-        double []   ws,
-        Calculation errors
-    ) {
-        int N = km.length;
-
-        if (N != ws.length) {
-            throw new IllegalArgumentException("km.length != ws.length");
-        }
-
-        if (N < 2) {
-            return false;
-        }
-
-        SplineInterpolator interpolator = null;
-
-        for (int i = 1; i < N; ++i) {
-            if (ws[i] <= ws[i-1]) {
-                // no back jump
-                continue;
-            }
-            backjumps.add(km[i]);
-
-            if (corrected == null) {
-                // lazy cloning
-                ws = corrected = (double [])ws.clone();
-            }
-
-            double above = aboveWaterKM(km, ws, i);
-
-            if (Double.isNaN(above)) { // run over start km
-                // fill all previous
-                for (int j = 0; j < i; ++j) {
-                    ws[j] = ws[i];
-                }
-                continue;
-            }
-
-            double distance = Math.abs(km[i] - above);
-
-            double quarterDistance = 0.25*distance;
-
-            double start = above - quarterDistance;
-
-            double startHeight = DoubleUtil.interpolateSorted(km, ws, start);
-
-            if (Double.isNaN(startHeight)) {
-                // run over start km
-                startHeight = ws[0];
-            }
-
-            double between = above + quarterDistance;
-
-            double aboveHeight = ws[i] + 0.25*(startHeight - ws[i]);
-
-            double [] x = { start,  above,  between };
-            double [] y = { startHeight, aboveHeight, ws[i] };
-
-            if (log.isDebugEnabled()) {
-                for (int j = 0; j < x.length; ++j) {
-                    log.debug("   " + x[j] + " -> " + y[j]);
-                }
-            }
-
-            if (interpolator == null) {
-                interpolator = new SplineInterpolator();
-            }
-
-            PolynomialSplineFunction spline;
-
-            try {
-                spline = interpolator.interpolate(x, y);
-            }
-            catch (MathIllegalArgumentException miae) {
-                errors.addProblem("spline.creation.failed");
-                log.error(miae);
-                continue;
-            }
-
-            try {
-                if (log.isDebugEnabled()) {
-                    log.debug("spline points:");
-                    for (int j = 0; j < x.length; ++j) {
-                        log.debug(x[j] + " " + y[j] + " " + spline.value(x[j]));
-                    }
-                }
-
-                int j = i-1;
-
-                for (; j >= 0 && km[j] >= between; --j) {
-                    ws[j] = ws[i];
-                }
-
-                for (; j >= 0 && ws[j] < startHeight; --j) {
-                    ws[j] = spline.value(km[j]);
-                }
-            }
-            catch (ArgumentOutsideDomainException aode) {
-                errors.addProblem("spline.interpolation.failed");
-                log.error("spline interpolation failed", aode);
-            }
-        } // for all km
-
-        return !backjumps.isEmpty();
-    }
-
-
-    protected static double aboveWaterKM(
-        double [] km,
-        double [] ws,
-        int       wIndex
-    ) {
-        double w = ws[wIndex];
-
-        while (--wIndex >= 0) {
-            // still under water
-            if (ws[wIndex] < w) continue;
-
-            if (ws[wIndex] > w) {
-                // f(ws[wIndex])   = km[wIndex]
-                // f(ws[wIndex+1]) = km[wIndex+1]
-                return Linear.linear(
-                    w,
-                    ws[wIndex], ws[wIndex+1],
-                    km[wIndex], km[wIndex+1]);
-            }
-            else {
-                return km[wIndex];
-            }
-        }
-
-        return Double.NaN;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/Distance.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@
-package de.intevation.flys.artifacts.math;
-
-/** Helper to calculate distance(s). */
-public final class Distance {
-    /** Return distance between two values. */
-    public static double distance(double a, double b) {
-        return Math.abs(a - b);
-    }
-
-    /** Return whether a and be are within a certain distance. */
-    public static boolean within(double a, double b, double threshold) {
-        return distance(a, b) <= threshold;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/FilterFunction.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,16 +0,0 @@
-package de.intevation.flys.artifacts.math;
-
-public class FilterFunction implements Function {
-
-    protected Function parent;
-
-    public FilterFunction(Function parent) {
-        this.parent = parent;
-    }
-
-    @Override
-    public double value(double x) {
-        return parent.value(x);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/Function.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,6 +0,0 @@
-package de.intevation.flys.artifacts.math;
-
-public interface Function {
-    double value(double x);
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/GrubbsOutlier.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,113 +0,0 @@
-package de.intevation.flys.artifacts.math;
-
-import java.util.List;
-
-import org.apache.commons.math.MathException;
-
-import org.apache.commons.math.distribution.TDistributionImpl;
-
-import org.apache.commons.math.stat.descriptive.moment.Mean;
-import org.apache.commons.math.stat.descriptive.moment.StandardDeviation;
-
-import org.apache.log4j.Logger;
-
-public class GrubbsOutlier
-{
-    public static final double EPSILON = 1e-5;
-
-    public static final double DEFAULT_ALPHA = 0.05;
-
-    private static Logger log = Logger.getLogger(GrubbsOutlier.class);
-
-    protected GrubbsOutlier() {
-    }
-
-    public static Integer findOutlier(List<Double> values) {
-        return findOutlier(values, DEFAULT_ALPHA, null);
-    }
-
-    public static Integer findOutlier(
-        List<Double> values,
-        double alpha,
-        double[] stdDevResult
-    ) {
-        boolean debug = log.isDebugEnabled();
-
-        if (debug) {
-            log.debug("outliers significance: " + alpha);
-        }
-
-        alpha = 1d - alpha;
-
-        int N = values.size();
-
-        if (debug) {
-            log.debug("Values to check: " + N);
-        }
-
-        if (N < 3) {
-            return null;
-        }
-
-        Mean mean = new Mean();
-        StandardDeviation std = new StandardDeviation();
-
-        for (Double value: values) {
-            double v = value.doubleValue();
-            mean.increment(v);
-            std .increment(v);
-        }
-
-        double m = mean.getResult();
-        double s = std.getResult();
-
-        if (debug) {
-            log.debug("mean: " + m);
-            log.debug("std dev: " + s);
-        }
-
-        double maxZ = -Double.MAX_VALUE;
-        int iv = -1;
-        for (int i = N-1; i >= 0; --i) {
-            double v = values.get(i).doubleValue();
-            double z = Math.abs(v - m);
-            if (z > maxZ) {
-                maxZ = z;
-                iv = i;
-            }
-        }
-
-        if (Math.abs(s) < EPSILON) {
-            return null;
-        }
-
-        maxZ /= s;
-
-        TDistributionImpl tdist = new TDistributionImpl(N-2);
-
-        double t;
-
-        try {
-            t = tdist.inverseCumulativeProbability(alpha/(N+N));
-        }
-        catch (MathException me) {
-            log.error(me);
-            return null;
-        }
-
-        t *= t;
-
-        double za = ((N-1)/Math.sqrt(N))*Math.sqrt(t/(N-2d+t));
-
-        if (debug) {
-            log.debug("max: " + maxZ + " crit: " + za);
-        }
-        if (stdDevResult != null) {
-            stdDevResult[0] = std.getResult();
-        }
-        return maxZ > za
-            ? Integer.valueOf(iv)
-            : null;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/Identity.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@
-package de.intevation.flys.artifacts.math;
-
-public final class Identity
-implements         Function
-{
-    public static final Identity IDENTITY = new Identity();
-
-    public Identity() {
-    }
-
-    public double value(double x) {
-        return x;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/Linear.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,78 +0,0 @@
-package de.intevation.flys.artifacts.math;
-
-public final class Linear
-implements         Function
-{
-    private double m;
-    private double b;
-
-    public Linear(
-        double x1, double x2,
-        double y1, double y2
-    ) {
-        // y1 = m*x1 + b
-        // y2 = m*x2 + b
-        // y2 - y1 = m*(x2 - x1)
-        // m = (y2 - y1)/(x2 - x1) # x2 != x1
-        // b = y1 - m*x1
-
-        if (x1 == x2) {
-            m = 0;
-            b = 0.5*(y1 + y2);
-        }
-        else {
-            m = (y2 - y1)/(x2 - x1);
-            b = y1 - m*x1;
-        }
-    }
-
-    public static final double linear(
-        double x,
-        double x1, double x2,
-        double y1, double y2
-    ) {
-        // y1 = m*x1 + b
-        // y2 = m*x2 + b
-        // y2 - y1 = m*(x2 - x1)
-        // m = (y2 - y1)/(x2 - x1) # x2 != x1
-        // b = y1 - m*x1
-
-        if (x1 == x2) {
-            return 0.5*(y1 + y2);
-        }
-        double m = (y2 - y1)/(x2 - x1);
-        double b = y1 - m*x1;
-        return x*m + b;
-    }
-
-    @Override
-    public double value(double x) {
-        return m*x + b;
-    }
-
-    public static final double factor(double x, double p1, double p2) {
-        // 0 = m*p1 + b <=> b = -m*p1
-        // 1 = m*p2 + b
-        // 1 = m*(p2 - p1)
-        // m = 1/(p2 - p1) # p1 != p2
-        // f(x) = x/(p2-p1) - p1/(p2-p1) <=> (x-p1)/(p2-p1)
-
-        return p1 == p2 ? 0.0 : (x-p1)/(p2-p1);
-    }
-
-    public static final double weight(double factor, double a, double b) {
-        //return (1.0-factor)*a + factor*b;
-        return a + factor*(b-a);
-    }
-
-    public static final void weight(
-        double factor,
-        double [] a, double [] b, double [] c
-    ) {
-        int N = Math.min(Math.min(a.length, b.length), c.length);
-        for (int i = 0; i < N; ++i) {
-            c[i] = weight(factor, a[i], b[i]);
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/LinearFilterFunction.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,24 +0,0 @@
-package de.intevation.flys.artifacts.math;
-
-public class LinearFilterFunction
-extends      FilterFunction
-{
-    protected double m;
-    protected double b;
-
-    public LinearFilterFunction(double m, double b) {
-        this(Identity.IDENTITY, m, b);
-    }
-
-    public LinearFilterFunction(Function parent, double m, double b) {
-        super(parent);
-        this.m = m;
-        this.b = b;
-    }
-
-    @Override
-    public double value(double x) {
-        return super.value(x)*m + b;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/LinearFunction.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,19 +0,0 @@
-package de.intevation.flys.artifacts.math;
-
-public class LinearFunction
-implements   Function
-{
-    protected double m;
-    protected double b;
-
-    public LinearFunction(double m, double b) {
-        this.m = m;
-        this.b = b;
-    }
-
-    @Override
-    public double value(double x) {
-        return x*m + b;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/MovingAverage.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,67 +0,0 @@
-package de.intevation.flys.artifacts.math;
-
-import java.util.Map;
-import java.util.SortedMap;
-import java.util.TreeMap;
-
-
-public class MovingAverage
-{
-
-    public static double[][] simple(double[][] values, double radius) {
-        TreeMap<Double, Double> map = toMap(values);
-        int N = map.size();
-        double [] xs = new double[N];
-        double [] ys = new double[N];
-        int ndx = 0;
-        for (double x: map.keySet()) {
-            SortedMap<Double, Double> range =
-                map.subMap(x-radius, true, x+radius, true);
-            double avg = 0d;
-            for (double v: range.values()) {
-                avg += v;
-            }
-            avg /= range.size();
-            xs[ndx] = x;
-            ys[ndx] = avg;
-            ndx++;
-        }
-        return new double [][] { xs, ys };
-    }
-
-    public static double[][] weighted(double[][] values, double radius) {
-        TreeMap<Double, Double> map = toMap(values);
-        int N = map.size();
-        double [] xs = new double[N];
-        double [] ys = new double[N];
-        int ndx = 0;
-        double _1radius = 1d/radius;
-        for (double x: map.keySet()) {
-            double avg = 0d;
-            double weights = 0d;
-            for (Map.Entry<Double, Double> e:
-                map.subMap(x-radius, false, x+radius, false).entrySet()
-            ) {
-                double weight = 1d - Math.abs(x - e.getKey())*_1radius;
-                avg += weight*e.getValue();
-                weights += weight;
-            }
-            avg /= weights;
-            xs[ndx] = x;
-            ys[ndx] = avg;
-            ndx++;
-        }
-        return new double [][] { xs, ys };
-    }
-
-    private static TreeMap<Double, Double> toMap(double[][] values) {
-        TreeMap<Double, Double> map = new TreeMap<Double, Double>();
-        double [] xs = values[0];
-        double [] ys = values[1];
-        for (int i = 0; i < xs.length; i++) {
-            map.put(xs[i], ys[i]);
-        }
-        return map;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/NaNFunction.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,16 +0,0 @@
-package de.intevation.flys.artifacts.math;
-
-public final class NaNFunction
-implements         Function
-{
-    public static final Function INSTANCE = new NaNFunction();
-
-    private NaNFunction() {
-    }
-
-    @Override
-    public double value(double x) {
-        return Double.NaN;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/StdDevOutlier.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,73 +0,0 @@
-package de.intevation.flys.artifacts.math;
-
-import java.util.List;
-
-import org.apache.commons.math.stat.descriptive.moment.StandardDeviation;
-
-import org.apache.log4j.Logger;
-
-public class StdDevOutlier
-{
-    public static final double DEFAULT_FACTOR = 3;
-
-    private static Logger log = Logger.getLogger(StdDevOutlier.class);
-
-    protected StdDevOutlier() {
-    }
-
-    public static Integer findOutlier(List<Double> values) {
-        return findOutlier(values, DEFAULT_FACTOR, null);
-    }
-
-    public static Integer findOutlier(
-        List<Double> values,
-        double       factor,
-        double []    stdDevResult
-    ) {
-        boolean debug = log.isDebugEnabled();
-
-        if (debug) {
-            log.debug("factor for std dev: " + factor);
-        }
-
-        int N = values.size();
-
-        if (debug) {
-            log.debug("Values to check: " + N);
-        }
-
-        if (N < 3) {
-            return null;
-        }
-
-        StandardDeviation stdDev = new StandardDeviation();
-
-        double maxValue = -Double.MAX_VALUE;
-        int    maxIndex = -1;
-        for (int i = N-1; i >= 0; --i) {
-            double value = Math.abs(values.get(i));
-            stdDev.increment(value);
-            if (value > maxValue) {
-                maxValue = value;
-                maxIndex = i;
-            }
-        }
-
-        double sd = stdDev.getResult();
-
-        double accepted = factor * sd;
-
-        if (debug) {
-            log.debug("std dev: " + stdDev);
-            log.debug("accepted: " + accepted);
-            log.debug("max value: " + maxValue);
-        }
-
-        if (stdDevResult != null) {
-            stdDevResult[0] = sd;
-        }
-
-        return maxValue > accepted ? maxIndex : null;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/Sub.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-package de.intevation.flys.artifacts.math;
-
-public final class Sub
-implements         Function
-{
-    private double s;
-
-    public Sub(double s) {
-        this.s = s;
-    }
-
-    @Override
-    public double value(double x) {
-        return x - s;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/UnivariateRealFunctionFunction.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,34 +0,0 @@
-package de.intevation.flys.artifacts.math;
-
-import org.apache.commons.math.FunctionEvaluationException;
-
-import org.apache.commons.math.analysis.UnivariateRealFunction;
-
-public final class UnivariateRealFunctionFunction
-implements         Function
-{
-    private UnivariateRealFunction function;
-
-    public UnivariateRealFunctionFunction(UnivariateRealFunction function) {
-        this.function = function;
-    }
-
-    @Override
-    public double value(double x) {
-        try {
-            return function.value(x);
-        }
-        catch (FunctionEvaluationException fee) {
-            return Double.NaN;
-        }
-    }
-
-    public UnivariateRealFunction getFunction() {
-        return function;
-    }
-
-    public void setFunction(UnivariateRealFunction function) {
-        this.function = function;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/Utils.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +0,0 @@
-package de.intevation.flys.artifacts.math;
-
-
-public final class Utils {
-
-    public static final double EPSILON = 1e-3;
-
-    private Utils() {
-    }
-
-    public static final boolean epsilonEquals(double a, double b) {
-        return epsilonEquals(a, b, EPSILON);
-    }
-
-    public static final boolean epsilonEquals(double a, double b, double eps) {
-        return Math.abs(a - b) < eps;
-    }
-
-    public static int relativeCCW(
-        double x1, double y1,
-        double x2, double y2,
-        double px, double py
-    ) {
-        if ((epsilonEquals(x1, x2) && epsilonEquals(y1, y2))
-        || ((epsilonEquals(x1, px) && epsilonEquals(y1, py)))) {
-            return 0; // Coincident points.
-        }
-        // Translate to the origin.
-        x2 -= x1;
-        y2 -= y1;
-        px -= x1;
-        py -= y1;
-        double slope2 = y2 / x2;
-        double slopep = py / px;
-        if (epsilonEquals(slope2, slopep)
-        || (epsilonEquals(x2, 0.0) && epsilonEquals(px, 0.0))) {
-            return y2 > EPSILON // Colinear.
-                ? (py < -EPSILON ? -1 : py > y2 ? 1 : 0)
-                : (py > -EPSILON ? -1 : py < y2 ? 1 : 0);
-        }
-        if (x2 >= EPSILON && slope2 >= EPSILON) {
-            return px >= EPSILON // Quadrant 1.
-                ? (slope2 > slopep ? 1 : -1)
-                : (slope2 < slopep ? 1 : -1);
-        }
-
-        if (y2 > EPSILON) {
-            return px < -EPSILON // Quadrant 2.
-                ? (slope2 > slopep ? 1 : -1)
-                : (slope2 < slopep ? 1 : -1);
-        }
-        if (slope2 >= EPSILON) {
-            return px >= EPSILON // Quadrant 3.
-                ? (slope2 < slopep ? 1 : -1)
-                : (slope2 > slopep ? 1 : -1);
-        }
-        return px < -EPSILON // Quadrant 4.
-            ? (slope2 < slopep ? 1 : -1)
-            : (slope2 > slopep ? 1 : -1);
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/WKmsOperation.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,148 +0,0 @@
-package de.intevation.flys.artifacts.math;
-
-import de.intevation.flys.artifacts.model.WKms;
-import de.intevation.flys.artifacts.model.WKmsImpl;
-
-import java.util.Arrays;
-
-public abstract class WKmsOperation
-{
-    public static final double EPSILON = 1e-6;
-
-    public static final class KmW
-    implements                Comparable<KmW>
-    {
-        protected double km;
-        protected double w;
-
-        public KmW(double km, double w) {
-            this.km = km;
-            this.w  = w;
-        }
-
-        public int compareTo(KmW other) {
-            return km < other.km
-                ? -1
-                : km > other.km ? +1 : 0;
-        }
-
-        public boolean kmEquals(KmW other) {
-            return Math.abs(km - other.km) < EPSILON;
-        }
-
-        public double subtract(KmW other) {
-            return w - other.w;
-        }
-    } // class KmW
-
-    public static final WKmsOperation SUBTRACTION = new WKmsOperation() {
-
-        @Override
-        public WKms operate(WKms a, WKms b) {
-            return subtract(a, b);
-        }
-    };
-
-    protected WKmsOperation() {
-    }
-
-    public abstract WKms operate(WKms a, WKms b);
-
-    /**
-     * Subtract two series from each other, interpolate values
-     * missing in one series in the other.
-     */
-    public static WKms subtract(WKms minuend, WKms subtrahend) {
-
-        int M = minuend   .size();
-        int S = subtrahend.size();
-
-        // Don't subtract empty sets
-        if (M < 1 || S < 1) {
-            return new WKmsImpl();
-        }
-
-        KmW [] ms = new KmW[M];
-        KmW [] ss = new KmW[S];
-
-        for (int i = 0; i < M; ++i) {
-            ms[i] = new KmW(minuend.getKm(i), minuend.getW(i));
-        }
-
-        for (int i = 0; i < S; ++i) {
-            ss[i] = new KmW(subtrahend.getKm(i), subtrahend.getW(i));
-        }
-
-        Arrays.sort(ms);
-        Arrays.sort(ss);
-
-        // no overlap -> empty result set
-        if (ms[0].km > ss[S-1].km || ss[0].km > ms[M-1].km) {
-            return new WKmsImpl();
-        }
-
-        WKmsImpl result = new WKmsImpl();
-
-        int mi = 0;
-        int si = 0;
-
-        OUT: while (mi < M && si < S) {
-            KmW m = ms[mi];
-            KmW s = ss[si];
-
-            if (m.km + EPSILON < s.km) {
-                // minuend is before subtrahend
-
-                while (ms[mi].km + EPSILON < s.km) {
-                    if (++mi >= M) {
-                        break OUT;
-                    }
-                }
-
-                if (ms[mi].km + EPSILON > s.km) {
-                    double mw = Linear.linear(
-                        s.km,
-                        ms[mi-1].km, ms[mi].km,
-                        ms[mi-1].w,  ms[mi].w);
-                    result.add(s.km, mw - s.w);
-                    ++si;
-                }
-                else { // s.km == ms[mi].km
-                    result.add(s.km, ms[mi].subtract(s));
-                    ++mi;
-                    ++si;
-                }
-            }
-            else if (m.km > s.km + EPSILON) {
-                // subtrahend is before minuend
-
-                while (m.km > ss[si].km + EPSILON) {
-                    if (++si >= S) {
-                        break OUT;
-                    }
-                }
-
-                if (ss[si].km + EPSILON > m.km) {
-                    double sw = Linear.linear(
-                        m.km,
-                        ss[si-1].km, ss[si].km,
-                        ss[si-1].w,  ss[si].w);
-                    result.add(m.km, m.w - sw);
-                }
-                else { // ss[si].km == m.km
-                    result.add(m.km, m.subtract(ss[si]));
-                    ++mi;
-                    ++si;
-                }
-            }
-            else { // m.km == s.km
-                result.add(s.km, m.subtract(s));
-                ++mi;
-                ++si;
-            }
-        }
-
-        return result;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/fitting/App.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,127 +0,0 @@
-package de.intevation.flys.artifacts.math.fitting;
-
-import java.util.List;
-import java.util.Map;
-import java.util.ArrayList;
-import java.util.TreeMap;
-import java.util.Comparator;
-
-import java.io.IOException;
-import java.io.BufferedReader;
-import java.io.Reader;
-import java.io.InputStreamReader;
-
-import org.apache.commons.math.optimization.fitting.CurveFitter;
-
-import org.apache.commons.math.optimization.general.LevenbergMarquardtOptimizer;
-
-import org.apache.commons.math.MathException;
-
-public class App
-{
-    public static final double EPS = 1e-5;
-
-    public static final String FUNCTION_NAME =
-        System.getProperty("function", "linear");
-
-    public static final Comparator<Double> EPS_CMP =
-        new Comparator<Double>()  {
-            @Override
-            public int compare(Double a, Double b) {
-                double diff = a - b;
-                if (diff < -EPS) return -1;
-                if (diff >  EPS) return +1;
-                return 0;
-            }
-        };
-
-    public static final List<Double []>readPoints(Reader reader)
-    throws IOException
-    {
-        Map<Double, Double> map = new TreeMap<Double, Double>(EPS_CMP);
-
-        BufferedReader input = new BufferedReader(reader);
-
-        String line;
-        while ((line = input.readLine()) != null) {
-            if ((line = line.trim()).length() == 0 || line.startsWith("#")) {
-                continue;
-            }
-
-            String [] parts = line.split("\\s+");
-
-            if (parts.length < 2) {
-                continue;
-            }
-
-            try {
-                Double x = Double.valueOf(parts[0]);
-                Double y = Double.valueOf(parts[1]);
-
-                Double old = map.put(x, y);
-
-                if (old != null) {
-                    System.err.println("duplicate x: " + x);
-                }
-            }
-            catch (NumberFormatException nfe) {
-                nfe.printStackTrace();
-            }
-        }
-
-        List<Double []> list = new ArrayList<Double []>(map.size());
-
-        for (Map.Entry<Double, Double> entry: map.entrySet()) {
-            list.add(new Double [] { entry.getKey(), entry.getValue() });
-        }
-
-        return list;
-    }
-
-    public static void main(String [] args) {
-
-        Function function = FunctionFactory
-            .getInstance()
-            .getFunction(FUNCTION_NAME);
-
-        if (function == null) {
-            System.err.println("Cannot find function '" + FUNCTION_NAME + "'.");
-            System.exit(1);
-        }
-
-        List<Double []> points = null;
-
-        try {
-            points = readPoints(new InputStreamReader(System.in));
-        }
-        catch (IOException ioe) {
-            ioe.printStackTrace();
-            System.exit(1);
-        }
-
-        LevenbergMarquardtOptimizer lmo = new LevenbergMarquardtOptimizer();
-
-        CurveFitter cf = new CurveFitter(lmo);
-
-        for (Double [] point: points) {
-            cf.addObservedPoint(point[0], point[1]);
-        }
-
-        double [] parameters = null;
-
-        try {
-            parameters = cf.fit(function, function.getInitialGuess());
-        }
-        catch (MathException me) {
-            me.printStackTrace();
-            System.exit(1);
-        }
-
-        String [] parameterNames = function.getParameterNames();
-
-        for (int i = 0; i < parameterNames.length; ++i) {
-            System.err.println(parameterNames[i] + ": " + parameters[i]);
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/fitting/Exp.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,62 +0,0 @@
-package de.intevation.flys.artifacts.math.fitting;
-
-public class Exp
-extends      Function
-{
-    public static final Derivative DERIVATIVE =
-        new Derivative("W'(Q) = a^Q*log(a)*m") {
-
-        @Override
-        public de.intevation.flys.artifacts.math.Function
-            instantiate(double [] parameters)
-        {
-                  double m     = parameters[0];
-            final double a     = parameters[1];
-            final double logam = Math.log(a)*m;
-
-            return new de.intevation.flys.artifacts.math.Function() {
-                @Override
-                public double value(double Q) {
-                    return Math.pow(a, Q)*logam;
-                }
-            };
-        }
-    };
-
-    public static final Function INSTANCE = new Exp();
-
-    public Exp() {
-        super(
-            "exp",
-            "W(Q) = m * a^Q + b",
-            new String [] { "m", "a", "b" });
-    }
-
-    @Override
-    public double value(double x, double [] parameters) {
-        return parameters[0]*Math.pow(parameters[1], x) + parameters[2];
-    }
-
-    @Override
-    public double [] gradient(double Q, double [] parameters) {
-        double m = parameters[0];
-        double a = parameters[1];
-        double b = parameters[2];
-        return new double [] {
-            Math.pow(a, Q),
-            Math.pow(a, Q-1d)*m*Q,
-            1d
-        };
-    }
-
-    @Override
-    public Derivative getDerivative() {
-        return DERIVATIVE;
-    }
-
-    @Override
-    public Function getInverse() {
-        return InvExp.INSTANCE;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/fitting/Function.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,100 +0,0 @@
-package de.intevation.flys.artifacts.math.fitting;
-
-import org.apache.commons.math.FunctionEvaluationException;
-
-import org.apache.commons.math.optimization.fitting.ParametricRealFunction;
-
-import de.intevation.flys.utils.DoubleUtil;
-
-public abstract class Function
-implements            ParametricRealFunction
-{
-    protected String    name;
-    protected String    description;
-    protected String [] parameterNames;
-    protected double [] initialGuess;
-
-    public static abstract class Derivative {
-
-        protected String description;
-
-        public Derivative() {
-        }
-
-        public Derivative(String description) {
-            this.description = description;
-        }
-
-        public String getDescription() {
-            return description;
-        }
-
-        public abstract de.intevation.flys.artifacts.math.Function
-            instantiate(double [] parameters);
-
-    } // interface Derivative
-
-    public Function() {
-    }
-
-    public Function(
-        String    name,
-        String    description,
-        String [] parameterNames
-    ) {
-        this(name,
-            description,
-            parameterNames,
-            DoubleUtil.fill(parameterNames.length, 1d));
-    }
-
-    public Function(
-        String    name,
-        String    description,
-        String [] parameterNames,
-        double [] initialGuess
-    ) {
-        this.name           = name;
-        this.description    = description;
-        this.parameterNames = parameterNames;
-        this.initialGuess   = initialGuess;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public String getDescription() {
-        return description;
-    }
-
-    public String [] getParameterNames() {
-        return parameterNames;
-    }
-
-    public double [] getInitialGuess() {
-        return initialGuess;
-    }
-
-    public de.intevation.flys.artifacts.math.Function instantiate(
-        final double [] parameters
-    ) {
-        return new de.intevation.flys.artifacts.math.Function() {
-
-            @Override
-            public double value(double x) {
-                try {
-                    return Function.this.value(x, parameters);
-                }
-                catch (FunctionEvaluationException fee) {
-                    return Double.NaN;
-                }
-            }
-        };
-    }
-
-    public abstract Derivative getDerivative();
-
-    public abstract Function getInverse();
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/fitting/FunctionFactory.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,44 +0,0 @@
-package de.intevation.flys.artifacts.math.fitting;
-
-import java.util.Collection;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-public final class FunctionFactory
-{
-    private static FunctionFactory instance;
-
-    private Map<String, Function> functions;
-
-    private FunctionFactory() {
-        functions = new LinkedHashMap<String, Function>();
-
-        registerFunction(Log.INSTANCE);
-        registerFunction(Linear.INSTANCE);
-        registerFunction(LogLinear.INSTANCE);
-        registerFunction(Exp.INSTANCE);
-        registerFunction(Quad.INSTANCE);
-        registerFunction(Pow.INSTANCE);
-        registerFunction(SQPow.INSTANCE);
-    }
-
-    public static synchronized FunctionFactory getInstance() {
-        if (instance == null) {
-            instance = new FunctionFactory();
-        }
-        return instance;
-    }
-
-    public Function getFunction(String name) {
-        return functions.get(name);
-    }
-
-    public void registerFunction(Function function) {
-        functions.put(function.getName(), function);
-    }
-
-    public Collection<Function> getFunctions() {
-        return functions.values();
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/fitting/InvExp.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
-package de.intevation.flys.artifacts.math.fitting;
-
-public class InvExp
-extends      Function
-{
-    public static final Derivative DERIVATIVE =
-        new Derivative("W'(Q) = 1/(log(a)*(Q-b))") {
-
-        @Override
-        public de.intevation.flys.artifacts.math.Function
-            instantiate(double [] parameters)
-        {
-            final double a = parameters[1];
-            final double b = parameters[2];
-            final double loga = Math.log(a);
-
-            return new de.intevation.flys.artifacts.math.Function() {
-                @Override
-                public double value(double Q) {
-                    return 1d/(loga*(Q-a));
-                }
-            };
-        }
-    };
-
-    public static final Function INSTANCE = new InvExp();
-
-    public InvExp() {
-        super(
-            "inv-exp",
-            "Q(W) = log((W-b)/m)/log(a)",
-            new String [] { "m", "a", "b" });
-    }
-
-    @Override
-    public double value(double W, double [] parameters) {
-        double m = parameters[0];
-        double a = parameters[1];
-        double b = parameters[2];
-        return Math.log((W-b)/m)/Math.log(a);
-    }
-
-    @Override
-    public double [] gradient(double Q, double [] parameters) {
-        double m = parameters[0];
-        double a = parameters[1];
-        double b = parameters[2];
-        double loga = Math.log(a);
-        return new double [] {
-            -1d/(loga*m),
-            -Math.log((Q-b)/m)/(a*loga*loga),
-            -1d/(loga*(Q-b))
-        };
-    }
-
-    @Override
-    public Derivative getDerivative() {
-        return DERIVATIVE;
-    }
-
-    @Override
-    public Function getInverse() {
-        return Exp.INSTANCE;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/fitting/InvLinear.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,57 +0,0 @@
-package de.intevation.flys.artifacts.math.fitting;
-
-public class InvLinear
-extends      Function
-{
-    public static Function INSTANCE = new InvLinear();
-
-    public static final Derivative DERIVATIVE =
-        new Derivative("Q'(W) = 1/m") {
-
-        @Override
-        public de.intevation.flys.artifacts.math.Function
-            instantiate(double [] parameters)
-        {
-            final double _1m = 1d/parameters[0];
-
-            return new de.intevation.flys.artifacts.math.Function() {
-                @Override
-                public double value(double Q) {
-                    return _1m;
-                }
-            };
-        }
-    };
-
-    public InvLinear() {
-        super("inv-linear", "W(Q) = (Q-b)/m", new String [] { "m", "b" });
-    }
-
-    @Override
-    public double value(double Q, double [] parameters) {
-        double m = parameters[0];
-        double b = parameters[1];
-        return (Q-b)/m;
-    }
-
-    @Override
-    public double [] gradient(double Q, double [] parameters) {
-        double m = parameters[0];
-        double b = parameters[1];
-        return new double [] {
-            -(Q-b)/(m*m),
-            -1d/m
-        };
-    }
-
-    @Override
-    public Derivative getDerivative() {
-        return DERIVATIVE;
-    }
-
-    @Override
-    public Function getInverse() {
-        return Linear.INSTANCE;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/fitting/InvLog.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,55 +0,0 @@
-package de.intevation.flys.artifacts.math.fitting;
-
-public class InvLog
-extends      Function
-{
-    public static final Derivative DERIVATIVE =
-        new Derivative("Q'(W) = exp(W/m)/m") {
-
-        @Override
-        public de.intevation.flys.artifacts.math.Function
-            instantiate(double [] parameters)
-        {
-            final double _1m = 1d / parameters[0];
-
-            return new de.intevation.flys.artifacts.math.Function() {
-                @Override
-                public double value(double W) {
-                    return Math.exp(W*_1m)*_1m;
-                }
-            };
-        }
-    };
-
-    public static final Function INSTANCE = new InvLog();
-
-    public InvLog() {
-        super("inv-log",  "Q(W) = e^(W/m) - b", new String [] { "m", "b" });
-    }
-
-    @Override
-    public double value(double w, double [] parameters) {
-        double m = parameters[0];
-        double b = parameters[1];
-        return Math.exp(w/m) - b;
-    }
-
-    @Override
-    public double [] gradient(double w, double [] parameters) {
-        double m   = parameters[0];
-        double b   = parameters[1];
-        double ewm = Math.exp(w/m);
-        return new double [] { -w*ewm/(m*m), -1 };
-    }
-
-    @Override
-    public Derivative getDerivative() {
-        return DERIVATIVE;
-    }
-
-    @Override
-    public Function getInverse() {
-        return Log.INSTANCE;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/fitting/InvLogLinear.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-package de.intevation.flys.artifacts.math.fitting;
-
-public class InvLogLinear
-extends      Function
-{
-    public static final Derivative DERIVATIVE =
-        new Derivative("Q'(W) = e^(W/a)/(a*m)") {
-
-        @Override
-        public de.intevation.flys.artifacts.math.Function
-            instantiate(double [] parameters)
-        {
-            final double _1a = 1d/parameters[0];
-            final double _1am = 1d/(parameters[0] * parameters[1]);
-
-            return new de.intevation.flys.artifacts.math.Function() {
-                @Override
-                public double value(double W) {
-                    return Math.exp(W*_1a)*_1am;
-                }
-            };
-        }
-    };
-    public static final Function INSTANCE = new InvLogLinear();
-
-    public InvLogLinear() {
-        super(
-            "inv-log-linear",
-            "Q(W)=(e^(W/a)-b)/m",
-            new String [] { "a", "m", "b" });
-    }
-
-    @Override
-    public double value(double W, double [] parameters) {
-        double a = parameters[0];
-        double m = parameters[1];
-        double b = parameters[2];
-        return (Math.exp(W/a) - b)/m;
-    }
-
-    @Override
-    public double [] gradient(double W, double [] parameters) {
-        double a   = parameters[0];
-        double m   = parameters[1];
-        double b   = parameters[2];
-        double eWa = Math.exp(W/a);
-        return new double [] {
-            -(W*eWa)/(a*a*m)
-            -(eWa-b)/(m*m),
-            -1/m
-        };
-    }
-
-    @Override
-    public Derivative getDerivative() {
-        return DERIVATIVE;
-    }
-
-    @Override
-    public Function getInverse() {
-        return LogLinear.INSTANCE;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/fitting/InvPow.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,72 +0,0 @@
-package de.intevation.flys.artifacts.math.fitting;
-
-public class InvPow
-extends      Function
-{
-    public static final Derivative DERIVATIVE =
-        new Derivative("Q'(W) = ((W-d)/a)^(1/c)/(c*(W-d))") {
-
-        @Override
-        public de.intevation.flys.artifacts.math.Function
-            instantiate(double [] parameters)
-        {
-                  double  a = parameters[0];
-            final double  c = parameters[1];
-            final double  d = parameters[2];
-            final double _1a = 1d/a;
-            final double _1c = 1d/c;
-
-            return new de.intevation.flys.artifacts.math.Function() {
-                @Override
-                public double value(double W) {
-                    double Wd = W-d;
-                    return Math.pow(Wd*_1a, _1c)/(c*Wd);
-                }
-            };
-        }
-    };
-
-    public static final Function INSTANCE = new InvPow();
-
-    public InvPow() {
-        super(
-            "pow",
-            "Q(W) = ((W-d)/a)^(1/c)",
-            new String [] { "a", "c", "d" });
-    }
-
-    @Override
-    public double value(double W, double [] parameters) {
-        double a = parameters[0];
-        double c = parameters[1];
-        double d = parameters[2];
-        return Math.pow((W-d)/a, 1d/c);
-    }
-
-    @Override
-    public double [] gradient(double W, double [] parameters) {
-        double a = parameters[0];
-        double c = parameters[1];
-        double d = parameters[2];
-        double _1c = 1d/c;
-        double Wdac = Math.pow((W-d)/a, 1d/c);
-        double Wd = W-d;
-        return new double [] {
-            -Wdac/(a*c),
-            (Wdac*Math.log(Wd/a))/(c*c),
-            -Wdac/(c*Wd)
-        };
-    }
-
-    @Override
-    public Derivative getDerivative() {
-        // TODO: Implement me!
-        return null;
-    }
-
-    @Override
-    public Function getInverse() {
-        return Pow.INSTANCE;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/fitting/InvQuad.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +0,0 @@
-package de.intevation.flys.artifacts.math.fitting;
-
-public class InvQuad
-extends      Function
-{
-    public static final Derivative DERIVATIVE =
-        new Derivative("Q'(W) = 1/sqrt(4*n*(W-b)+m^2)") {
-
-        @Override
-        public de.intevation.flys.artifacts.math.Function
-            instantiate(double [] parameters)
-        {
-                  double n  = parameters[0];
-                  double m  = parameters[1];
-            final double b  = parameters[2];
-            final double n4 = 4d*n;
-            final double mm = m*m;
-
-            return new de.intevation.flys.artifacts.math.Function() {
-                @Override
-                public double value(double W) {
-                    return 1d/Math.sqrt(n4*(W-b)+mm);
-                }
-            };
-        }
-    };
-
-    public static final Function INSTANCE = new InvQuad();
-
-    public InvQuad() {
-        super(
-            "inv-quad",
-            "(sqrt(4*n*W-4*b*n+m^2)-m)/(2*n)",
-            new String [] { "n", "m", "b" });
-    }
-
-    @Override
-    public double value(double W, double [] parameters) {
-        double n = parameters[0];
-        double m = parameters[1];
-        double b = parameters[2];
-        return (Math.sqrt(4d*n*(W - b) + m*m)-m)/(2d*n);
-    }
-
-    @Override
-    public double [] gradient(double W, double [] parameters) {
-        double n  = parameters[0];
-        double m  = parameters[1];
-        double b  = parameters[2];
-        double Wb = W-b;
-        double sn4Wb = Math.sqrt(4d*n*Wb + m*m);
-        return new double [] {
-            Wb/(n*sn4Wb)-(sn4Wb-m)/(2d*n*n),
-            (m/sn4Wb-1d)/(2d*n),
-            -1d/sn4Wb
-        };
-    }
-
-    @Override
-    public Derivative getDerivative() {
-        return DERIVATIVE;
-    }
-
-    @Override
-    public Function getInverse() {
-        return Quad.INSTANCE;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/fitting/InvSQPow.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-package de.intevation.flys.artifacts.math.fitting;
-
-public class InvSQPow
-extends      Function
-{
-    public static final Derivative DERIVATIVE =
-        new Derivative("Q'(S) = (S/a)^(1/b)/(b*S)") {
-
-        @Override
-        public de.intevation.flys.artifacts.math.Function
-            instantiate(double [] parameters)
-        {
-            final double _1a = 1d/parameters[0];
-            final double   b = parameters[1];
-            final double _1b = 1d/b;
-
-            return new de.intevation.flys.artifacts.math.Function() {
-                @Override
-                public double value(double S) {
-                    return Math.pow(S*_1a, _1b)/(b*S);
-                }
-            };
-        }
-    };
-    public static final Function INSTANCE = new InvSQPow();
-
-    public InvSQPow() {
-        super(
-            "inv-sq-pow",
-            "Q(S) = Q=(S/a)^(1/b)",
-            new String [] { "a", "b" });
-    }
-
-    @Override
-    public double value(double S, double [] parameters) {
-        double a = parameters[0];
-        double b = parameters[1];
-        return Math.pow(S/a, 1d/b);
-    }
-
-    @Override
-    public double [] gradient(double S, double [] parameters) {
-        double a     = parameters[0];
-        double b     = parameters[1];
-        double Sa    = S/a;
-        double _1b   = 1d/b;
-        double eSa1b = Math.pow(Sa, _1b);
-        return new double [] {
-            -eSa1b/(a*b),
-            -(eSa1b*Math.log(Sa))/(b*b)
-        };
-    }
-
-    @Override
-    public Derivative getDerivative() {
-        return DERIVATIVE;
-    }
-
-    @Override
-    public Function getInverse() {
-        return SQPow.INSTANCE;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/fitting/Linear.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-package de.intevation.flys.artifacts.math.fitting;
-
-public class Linear
-extends      Function
-{
-    public static final Derivative DERIVATIVE =
-        new Derivative("W'(Q) = m") {
-
-        @Override
-        public de.intevation.flys.artifacts.math.Function
-            instantiate(double [] parameters)
-        {
-            final double m = parameters[0];
-
-            return new de.intevation.flys.artifacts.math.Function() {
-                @Override
-                public double value(double Q) {
-                    return m;
-                }
-            };
-        }
-    };
-
-    public static final Function INSTANCE = new Linear();
-
-    public Linear() {
-        super("linear", "W(Q) = m*Q + b", new String [] { "m", "b" });
-    }
-
-    @Override
-    public double value(double x, double [] parameters) {
-        return x*parameters[0] + parameters[1];
-    }
-
-    @Override
-    public double [] gradient(double x, double [] parameters) {
-        return new double [] { x, 1d };
-    }
-
-    @Override
-    public Derivative getDerivative() {
-        return DERIVATIVE;
-    }
-
-    @Override
-    public Function getInverse() {
-        return InvLinear.INSTANCE;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/fitting/Log.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,57 +0,0 @@
-package de.intevation.flys.artifacts.math.fitting;
-
-public class Log
-extends      Function
-{
-    public static final Derivative DERIVATIVE =
-        new Derivative("W'(Q) = m/(Q+b)") {
-
-        @Override
-        public de.intevation.flys.artifacts.math.Function
-            instantiate(double [] parameters)
-        {
-            final double m = parameters[0];
-            final double b = parameters[1];
-
-            return new de.intevation.flys.artifacts.math.Function() {
-                @Override
-                public double value(double Q) {
-                    return m/(Q+b);
-                }
-            };
-        }
-    };
-
-    public static final Function INSTANCE = new Log();
-
-    public Log() {
-        super("log",  "W(Q) = m*ln(Q + b)", new String [] { "m", "b" });
-    }
-
-    @Override
-    public double value(double x, double [] parameters) {
-        return parameters[0]*Math.log(x + parameters[1]);
-    }
-
-    @Override
-    public double [] gradient(double x, double [] parameters) {
-        double m = parameters[0];
-        double b = parameters[1];
-        double b_x = b + x;
-        return new double [] {
-            Math.log(b_x),
-            m/b_x
-        };
-    }
-
-    @Override
-    public Derivative getDerivative() {
-        return DERIVATIVE;
-    }
-
-    @Override
-    public Function getInverse() {
-        return InvLog.INSTANCE;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/fitting/LogLinear.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,65 +0,0 @@
-package de.intevation.flys.artifacts.math.fitting;
-
-public class LogLinear
-extends      Function
-{
-    public static final Derivative DERIVATIVE =
-        new Derivative("W'(Q) = a*m/(m*Q + b)") {
-
-        @Override
-        public de.intevation.flys.artifacts.math.Function
-            instantiate(double [] parameters)
-        {
-            final double a = parameters[0];
-            final double m = parameters[1];
-            final double b = parameters[2];
-
-            return new de.intevation.flys.artifacts.math.Function() {
-                @Override
-                public double value(double Q) {
-                    return a*m/(m*Q + b);
-                }
-            };
-        }
-    };
-
-    public static final Function INSTANCE = new LogLinear();
-
-    public LogLinear() {
-        super(
-            "log-linear",
-            "W(Q) = a*ln(m*Q + b)",
-            new String [] { "a", "m", "b" });
-    }
-
-    @Override
-    public double value(double x, double [] parameters) {
-        return parameters[0]*Math.log(parameters[1]*x + parameters[2]);
-    }
-
-    @Override
-    public double [] gradient(double x, double [] parameters) {
-        double a = parameters[0];
-        double m = parameters[1];
-        double b = parameters[2];
-
-        double lin = m*x + b;
-
-        return new double [] {
-            Math.log(lin),
-            a*x / lin,
-            a / lin
-        };
-    }
-
-    @Override
-    public Derivative getDerivative() {
-        return DERIVATIVE;
-    }
-
-    @Override
-    public Function getInverse() {
-        return InvLogLinear.INSTANCE;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/fitting/Pow.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +0,0 @@
-package de.intevation.flys.artifacts.math.fitting;
-
-public class Pow
-extends      Function
-{
-    public static final Derivative DERIVATIVE =
-        new Derivative("W'(Q) = a*c*Q^(c-1)") {
-
-        @Override
-        public de.intevation.flys.artifacts.math.Function
-            instantiate(double [] parameters)
-        {
-            final double a = parameters[0];
-            final double c = parameters[1];
-
-            return new de.intevation.flys.artifacts.math.Function() {
-                @Override
-                public double value(double Q) {
-                    return a*c*Math.pow(Q, c-1);
-                }
-            };
-        }
-    };
-
-    public static final Function INSTANCE = new Pow();
-
-    public Pow() {
-        super(
-            "pow",
-            "W(Q) = a*Q^c + d",
-            new String [] { "a", "c", "d" });
-    }
-
-    @Override
-    public double value(double x, double [] parameters) {
-        return parameters[0]*Math.pow(x, parameters[1]) + parameters[2];
-    }
-
-    @Override
-    public double [] gradient(double x, double [] parameters) {
-        double a   = parameters[0];
-        double c   = parameters[1];
-        double x_c = Math.pow(x, c);
-        return new double [] {
-            x_c,
-            a*x_c*Math.log(x),
-            1d
-        };
-    }
-
-    @Override
-    public Derivative getDerivative() {
-        return DERIVATIVE;
-    }
-
-    @Override
-    public Function getInverse() {
-        return InvPow.INSTANCE;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/fitting/Quad.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,55 +0,0 @@
-package de.intevation.flys.artifacts.math.fitting;
-
-public class Quad
-extends      Function
-{
-    public static final Derivative DERIVATIVE =
-        new Derivative("W'(Q) = 2*n*Q+m") {
-
-        @Override
-        public de.intevation.flys.artifacts.math.Function
-            instantiate(double [] parameters)
-        {
-            final double n2 = 2d*parameters[0];
-            final double m  = parameters[1];
-
-            return new de.intevation.flys.artifacts.math.Function() {
-                @Override
-                public double value(double Q) {
-                    return n2*Q+m;
-                }
-            };
-        }
-    };
-
-    public static final Function INSTANCE = new Quad();
-
-    public Quad() {
-        super(
-            "quad",
-            "W(Q) = n*Q^2 + m*Q + b",
-            new String [] { "n", "m", "b" });
-    }
-
-    @Override
-    public double value(double x, double [] parameters) {
-        // n*Q^2 + m*Q + b <=> Q*(n*Q + m) + b
-        return x*(parameters[0]*x + parameters[1]) + parameters[2];
-    }
-
-    @Override
-    public double [] gradient(double x, double [] parameters) {
-        return new double [] { x*x, x, 1d };
-    }
-
-    @Override
-    public Derivative getDerivative() {
-        return DERIVATIVE;
-    }
-
-    @Override
-    public Function getInverse() {
-        return InvQuad.INSTANCE;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/fitting/SQPow.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,60 +0,0 @@
-package de.intevation.flys.artifacts.math.fitting;
-
-public class SQPow
-extends      Function
-{
-    public static final Derivative DERIVATIVE =
-        new Derivative("S'(Q) = a*b*Q^(b-1)") {
-
-        @Override
-        public de.intevation.flys.artifacts.math.Function
-            instantiate(double [] parameters)
-        {
-            final double a = parameters[0];
-            final double b = parameters[1];
-
-            return new de.intevation.flys.artifacts.math.Function() {
-                @Override
-                public double value(double Q) {
-                    return a*b*Math.pow(Q, b-1);
-                }
-            };
-        }
-    };
-
-    public static final Function INSTANCE = new SQPow();
-
-    public SQPow() {
-        super(
-            "sq-pow",
-            "S(Q) = a*Q^b",
-            new String [] { "a", "b" });
-    }
-
-    @Override
-    public double value(double x, double [] parameters) {
-        return parameters[0]*Math.pow(x, parameters[1]);
-    }
-
-    @Override
-    public double [] gradient(double q, double [] parameters) {
-        double a   = parameters[0];
-        double b   = parameters[1];
-        double q_b = Math.pow(q, b);
-        return new double [] {
-            q_b,
-            a*q_b*Math.log(q),
-        };
-    }
-
-    @Override
-    public Derivative getDerivative() {
-        return DERIVATIVE;
-    }
-
-    @Override
-    public Function getInverse() {
-        return InvSQPow.INSTANCE;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/AnnotationFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,103 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.AnnotationArtifact;
-
-import de.intevation.flys.jfree.FLYSAnnotation;
-import de.intevation.flys.jfree.StickyAxisAnnotation;
-
-import de.intevation.flys.model.Annotation;
-import de.intevation.flys.model.FastAnnotations;
-
-import de.intevation.artifactdatabase.state.DefaultFacet;
-import de.intevation.artifactdatabase.state.Facet;
-
-import de.intevation.flys.utils.FLYSUtils;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-
-/**
- * Facet to access Annotations (landmarks, POIs) of a river.
- */
-public class AnnotationFacet
-extends      DefaultFacet
-{
-    /** Logger for this class. */
-    private static final Logger logger = Logger.getLogger(AnnotationFacet.class);
-
-
-    /**
-     * Trivial Constructor.
-     */
-    public AnnotationFacet() {
-    }
-
-
-    /**
-     * Trivial Constructor for a AnnotationFacet.
-     *
-     * @param index       Database-Index to use.
-     * @param name        Name (~type) of Facet.
-     * @param description Description of Facet.
-     */
-    public AnnotationFacet(int index, String name, String description) {
-        super(index, name, description);
-    }
-
-
-    /**
-     * Get List of Annotations for river from Artifact.
-     *
-     * @param artifact (Annotation-)Artifact to query for list of Annotations.
-     * @param context  Ignored.
-     */
-    @Override
-    public Object getData(Artifact artifact, CallContext context) {
-
-        // TODO issue880: Make annotations available _per type_
-        AnnotationArtifact annotationArtifact = (AnnotationArtifact) artifact;
-
-        String riverName = FLYSUtils.getRivername((FLYSArtifact)artifact);
-
-        FastAnnotations fas = LocationProvider.getAnnotations(riverName);
-
-        String filterName = annotationArtifact.getFilterName();
-
-        FastAnnotations.Filter filter = (filterName == null)
-            ? FastAnnotations.IS_POINT
-            : new FastAnnotations.NameFilter(filterName);
-
-        List<StickyAxisAnnotation> xy =
-            new ArrayList<StickyAxisAnnotation>(fas.size());
-
-        for (Iterator<FastAnnotations.Annotation> iter =
-                fas.filter(filter); iter.hasNext();) {
-            FastAnnotations.Annotation fa = iter.next();
-
-            xy.add(new StickyAxisAnnotation(
-                fa.getPosition(),
-                (float)fa.getA(),
-                StickyAxisAnnotation.SimpleAxis.X_AXIS));
-        }
-
-        return new FLYSAnnotation(description, xy);
-    }
-
-
-    @Override
-    public Facet deepCopy() {
-        AnnotationFacet copy = new AnnotationFacet();
-        copy.set(this);
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/AnnotationsFactory.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,113 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.math.BigDecimal;
-
-import java.util.List;
-import java.util.Iterator;
-import java.util.Collections;
-
-import de.intevation.flys.backend.SessionHolder;
-import de.intevation.flys.model.Annotation;
-import de.intevation.flys.model.River;
-
-import org.hibernate.Session;
-import org.hibernate.Query;
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class AnnotationsFactory {
-
-    /**
-     * Get Annotations which do not have a "b" ("to")-value set.
-     *
-     * @param river name of the river of interest.
-     *
-     * @return List of Annotations for river which have only "a" ("from")
-     *          value set.
-     */
-    public static List<Annotation> getPointAnnotations(String river) {
-        Session session = SessionHolder.HOLDER.get();
-
-        Query query = session.createQuery(
-            "from Annotation as an " +
-            "where an.range.b = null and an.range.river.name=:name " +
-            "order by range.a");
-        query.setParameter("name", river);
-        return query.list();
-    }
-
-
-    public static List<Annotation> getAnnotations(River river) {
-        Session session = SessionHolder.HOLDER.get();
-
-        Query query = session.createQuery(
-            "from Annotation as an where an.range.river = :river" +
-            " order by an.range.a");
-        query.setParameter("river", river);
-        return query.list();
-    }
-
-
-    public static Annotation getAnnotation(String river, double km) {
-        Session session = SessionHolder.HOLDER.get();
-
-        Query query = session.createQuery(
-            "from Annotation as a " +
-            "where a.range.river.name = :river AND a.range.a = :km");
-
-        query.setParameter("river", river);
-        query.setParameter("km", BigDecimal.valueOf(km));
-
-        List<Annotation> result = query.list();
-
-        return result != null && result.size() > 0 ? result.get(0) : null;
-    }
-
-
-    /**
-     * Get minimal "a" ("from") and maximal "b" ("to") value of annotations'
-     * ranges of a river.
-     *
-     * @param river name of the river of interest.
-     *
-     * @return Array containing minimal "a" and max "b" value of any
-     *         annotation stored for the given river.
-     */
-    public static double[] getAnnotationsBreadth(String river) {
-        Session session = SessionHolder.HOLDER.get();
-
-        Query minAQuery = session.createQuery(
-            "select min(a), max(b) from Range where river.name=:name");
-        minAQuery.setParameter("name", river);
-
-        double[] minAmaxB = {0.0f, 0.0f};
-        Object[] row = (Object[]) minAQuery.list().iterator().next();
-        minAmaxB[0] = ((BigDecimal) row[0]).doubleValue();
-        minAmaxB[1] = ((BigDecimal) row[1]).doubleValue();
-        return minAmaxB;
-    }
-
-
-    public static Iterator<Annotation> getAnnotationsIterator(
-        String riverName
-    ) {
-        Session session = SessionHolder.HOLDER.get();
-
-        Query riverQuery = session.createQuery(
-            "from River where name = :name");
-        riverQuery.setParameter("name", riverName);
-        List<River> rivers = riverQuery.list();
-        if (rivers.isEmpty()) {
-            return Collections.<Annotation>emptyList().iterator();
-        }
-
-        Query query = session.createQuery(
-            "from Annotation as an" +
-            " where an.range.river = :river order by an.range.a");
-        query.setParameter("river", rivers.get(0));
-
-        return (Iterator<Annotation>)query.iterate();
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/AreaFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,145 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.artifactdatabase.state.DefaultFacet;
-import de.intevation.artifactdatabase.state.Facet;
-
-import de.intevation.artifacts.DataProvider;
-
-import de.intevation.flys.artifacts.AreaArtifact;
-
-
-/**
- * Trival Facet for areas.
- * Note that this Facet comes in two "types" (names):
- *  <ul>
- *    <li>CROSS_SECTION_AREA (cross_section.area) and</li>
- *    <li>LONGITUDINAL_SECTION_AREA (longitudinal.area</li>
- *  </ul>
- * This is to support different diagram types without being painted in both
- * at the same time. The name has to be given when constructing.
- */
-public class AreaFacet
-extends      DefaultFacet
-{
-    private static Logger logger = Logger.getLogger(AreaFacet.class);
-
-    /**
-     * Constructor, set (maybe localized) description and name.
-     * @param idx Index given when querying artifact for data.
-     * @param name important to discern areas in different diagram types.
-     */
-    public AreaFacet(int idx, String name, String description) {
-        super(idx, name, description);
-    }
-
-
-    /**
-     * Gets Cross Section (profile).
-     * @param art artifact to get data from.
-     * @param context ignored
-     */
-    public Object getData(Artifact art, CallContext context) {
-        logger.debug("Get data for area.");
-
-        // Get information from artifact about which
-        // info to grab from blackboard.
-        //
-        // All compatible facets should provide their data
-        // under the key (Artifact-UUID + Facet-Index).
-        AreaArtifact artifact = (AreaArtifact) art;
-        Object lowerData      = null;
-        Object upperData      = null;
-        String stemFacetName  = null;
-
-        List<DataProvider> providers = context.
-            getDataProvider(artifact.getLowerDPKey());
-        if (providers.size() < 1) {
-            logger.warn("No 'lower' provider given for area [" +
-                artifact.getLowerDPKey() + "]");
-        }
-        else {
-            lowerData = providers.get(0).provideData(
-                artifact.getLowerDPKey(), null, context);
-            logger.debug("'Lower' data provider key for area [" +
-                artifact.getLowerDPKey() + "]");
-            stemFacetName = artifact.getLowerDPKey().split(":")[1];
-        }
-
-        providers = context.getDataProvider(artifact.getUpperDPKey());
-        if (providers.size() < 1) {
-            logger.warn("No 'upper' provider given for area [" +
-                artifact.getUpperDPKey() + "]");
-        }
-        else {
-            upperData = providers.get(0).provideData(
-                artifact.getUpperDPKey(), null, context);
-            logger.debug("'Upper' data provider key for area [" +
-                artifact.getUpperDPKey() + "]");
-            if (stemFacetName == null) {
-                stemFacetName = artifact.getUpperDPKey().split(":")[1];
-            }
-        }
-
-        if (upperData == null && lowerData == null) {
-            logger.warn("Not given 'upper' and 'lower' for area");
-        }
-
-        return new Data(stemFacetName, lowerData, upperData,
-            Boolean.valueOf(artifact.getPaintBetween()));
-    }
-
-
-    /** Do a deep copy. */
-    @Override
-    public Facet deepCopy() {
-        AreaFacet copy = new AreaFacet(this.index, this.name, this.description);
-        copy.set(this);
-        return copy;
-    }
-
-    /** Result data bundle. */
-    public class Data {
-        protected String  rootFacetName;
-        protected Object  upperData;
-        protected Object  lowerData;
-        protected boolean doPaintBetween;
-
-        /** Create a new result data bundle. */
-        public Data(String rootName, Object low, Object up, boolean between) {
-            this.rootFacetName  = rootName;
-            this.lowerData      = low;
-            this.upperData      = up;
-            this.doPaintBetween = between;
-        }
-
-        /** Get name of a facet that is involved in area generation
-         * to induce type (e.g. longitudinal_section.w -> "W over km"). */
-        public String getRootFacetName() {
-            return this.rootFacetName;
-        }
-
-        /** Get data for 'upper' curve of area. */
-        public Object getUpperData() {
-            return this.upperData;
-        }
-
-        /** Get data for 'lower' curve of area. */
-        public Object getLowerData() {
-            return this.lowerData;
-        }
-
-        /** Whether to fill whole area between (in contrast to 'under'
-         *  or 'over'). */
-        public boolean doPaintBetween() {
-            return this.doPaintBetween;
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/BlackboardDataFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,78 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.DefaultFacet;
-
-
-/**
- * Facet that writes artifact-uui and facet index on the blackboard,
- * delivers data if asked so.
- */
-public class BlackboardDataFacet extends DefaultFacet {
-
-    public BlackboardDataFacet() {}
-
-    /** Do not instantiate a BlackboardDataFacet, subclass it instead. */
-    public BlackboardDataFacet(int idx, String name, String description) {
-        super(idx, name, description);
-    }
-
-
-    /** Do not instantiate a BlackboardDataFacet, subclass it instead. */
-    public BlackboardDataFacet(String name, String description) {
-        super(0, name, description);
-    }
-
-
-    /** Define key to which to respond when asked for 'blackboard'
-     * (DataProvider)- data. */
-    public String areaDataKey(Artifact art) {
-        return art.identifier() + ":" + getName() + ":" + getIndex();
-    }
-
-
-    /** Hey, We can ArtifactUUID+:+FacetName+:+FacetIndex (i.e. getData)! */
-    @Override
-    public List getStaticDataProviderKeys(Artifact art) {
-        List list = new ArrayList();
-        list.add(areaDataKey(art));
-        return list;
-    }
-
-
-    /**
-     * Can provide whatever getData returns.
-     * @param key      will respond on uuid+index
-     * @param param    ignored
-     * @param context  ignored
-     * @return whatever getData delivers.
-     */
-    @Override
-    public Object provideBlackboardData(Artifact artifact,
-        Object key,
-        Object param,
-        CallContext context
-    ) {
-        if (key.equals(areaDataKey(artifact))) {
-            return getData(artifact, context);
-        }
-        else {
-            return null;
-        }
-    }
-
-    /** Copy deeply. */
-    @Override
-    public Facet deepCopy() {
-        BlackboardDataFacet copy = new BlackboardDataFacet();
-        copy.set(this);
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/Calculation.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,167 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-import java.util.List;
-import java.util.ArrayList;
-import java.util.Arrays;
-
-import java.io.Serializable;
-
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.flys.artifacts.resources.Resources;
-
-import de.intevation.flys.utils.Formatter;
-
-/** A calculation that can have result and problems. */
-public class Calculation
-implements   Serializable
-{
-    /** Problem of a calculation.
-     * Has location and message. */
-    public static class Problem
-    implements          Serializable
-    {
-        protected Double    km;
-        protected String    msg;
-        protected Object [] args;
-
-        public Problem() {
-        }
-
-        public Problem(String msg) {
-            this.msg = msg;
-        }
-
-        public Problem(String msg, Object [] args) {
-            this.msg  = msg;
-            this.args = args;
-        }
-
-        public Problem(double km, String msg) {
-            this.km  = km;
-            this.msg = msg;
-        }
-
-        public Problem(double km, String msg, Object [] args) {
-            this.km   = km;
-            this.msg  = msg;
-            this.args = args;
-        }
-
-        public Element toXML(Document document, CallMeta meta) {
-            Element problem = document.createElement("problem");
-            if (km != null) {
-                problem.setAttribute(
-                    "km",
-                    Formatter.getCalculationKm(meta).format(km));
-            }
-            String text = args != null
-                ? Resources.getMsg(meta, msg, msg, args)
-                : Resources.getMsg(meta, msg, msg);
-            problem.setTextContent(text);
-            return problem;
-        }
-
-        public String getMsg() {
-            return msg;
-        }
-
-        @Override
-        public boolean equals(Object other) {
-            if (!(other instanceof Problem)) {
-                return false;
-            }
-            Problem o = (Problem)other;
-            return !(!msg.equals(o.msg)
-                || (km == null && o.km != null)
-                || (km != null && o.km == null)
-                || (km != null && !km.equals(o.km))
-                || !Arrays.equals(args, o.args));
-        }
-    } // class Problem
-
-    protected List<Problem> problems;
-
-    public Calculation() {
-    }
-
-    public Calculation(String msg) {
-        addProblem(msg);
-    }
-
-    /** New Calculation with error which can be translated given args. */
-    public Calculation(String msg, Object ... args) {
-        addProblem(msg, args);
-    }
-
-    protected List<Problem> checkProblems() {
-        if (problems == null) {
-            problems = new ArrayList<Problem>();
-        }
-        return problems;
-    }
-
-    public void addProblems(Calculation other) {
-        List<Problem> otherProblems = other.problems;
-        if (otherProblems != null) {
-            List<Problem> problems = checkProblems();
-            for (Problem problem: otherProblems) {
-                if (!problems.contains(problem)) {
-                    problems.add(problem);
-                }
-            }
-        }
-    }
-
-    public void addProblem(Problem problem) {
-        List<Problem> problems = checkProblems();
-        if (!problems.contains(problem)) {
-            problems.add(problem);
-        }
-    }
-
-    public void addProblem(String msg) {
-        addProblem(new Problem(msg));
-    }
-
-    public void addProblem(String msg, Object ... args) {
-        addProblem(new Problem(msg, args));
-    }
-
-    public void addProblem(double km, String msg) {
-        addProblem(new Problem(km, msg));
-    }
-
-    public void addProblem(double km, String msg, Object ... args) {
-        addProblem(new Problem(km, msg, args));
-    }
-
-    public boolean hasProblems() {
-        return problems != null && !problems.isEmpty();
-    }
-
-    public int numProblems() {
-        return problems != null ? problems.size() : 0;
-    }
-
-    public List<Problem> getProblems() {
-        return problems;
-    }
-
-    public void toXML(Document document, CallMeta meta) {
-
-        Element root = document.createElement("problems");
-
-        if (hasProblems()) {
-            for (Problem problem: problems) {
-                root.appendChild(problem.toXML(document, meta));
-            }
-        }
-
-        document.appendChild(root);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/Calculation1.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,73 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.util.ArrayList;
-
-import org.apache.log4j.Logger;
-
-public class Calculation1
-extends      Calculation
-{
-    private static Logger logger = Logger.getLogger(Calculation1.class);
-
-    protected double [] kms;
-    protected double [] qs;
-    protected double [] ws;
-    protected double    refKm;
-
-    public Calculation1() {
-    }
-
-    public Calculation1(
-        double [] kms,
-        double [] qs,
-        double [] ws,
-        double    refKm
-    ) {
-        this.kms   = kms;
-        this.qs    = qs;
-        this.ws    = ws;
-        this.refKm = refKm;
-    }
-
-    public CalculationResult calculate(WstValueTable wst) {
-
-        ArrayList<WQKms> results = new ArrayList<WQKms>();
-
-        String    prefix;
-        double [] origData;
-
-        if (ws != null) { prefix = "W="; origData = ws; }
-        else            { prefix = "Q="; origData = qs; }
-
-        int oldNumProblems = numProblems();
-
-        for (int i = 0; i < qs.length; i++) {
-
-            double [] oqs = new double[kms.length];
-            double [] ows = new double[kms.length];
-
-            boolean success =
-                wst.interpolate(qs[i], refKm, kms, ows, oqs, this) != null;
-
-            int newNumProblems = numProblems();
-
-            if (success) {
-                WQKms result = new WQKms(kms, oqs, ows, prefix + origData[i]);
-                if (oldNumProblems != newNumProblems) {
-                    logger.debug(
-                        qs[i] + " caused " + (newNumProblems-oldNumProblems) +
-                        " new problem(s).");
-                    result.removeNaNs();
-                }
-                results.add(result);
-            }
-
-            oldNumProblems = newNumProblems;
-        }
-
-        return new CalculationResult(
-            results.toArray(new WQKms[results.size()]),
-            this);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/Calculation2.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.util.Arrays;
-
-import org.apache.log4j.Logger;
-
-
-/** ComputedDischargeCurve. */
-public class Calculation2
-extends      Calculation
-{
-    private static Logger logger = Logger.getLogger(Calculation2.class);
-
-    protected double km;
-
-    public Calculation2() {
-    }
-
-    public Calculation2(double km) {
-        this.km = km;
-    }
-
-    public CalculationResult calculate(WstValueTable wst) {
-
-        logger.debug("Calculation2.calculate");
-
-        double [][] wqs = wst.interpolateWQ(km, this);
-
-        if (wqs == null || wqs[0].length == 0) {
-            addProblem("cannot.compute.discharge.curve");
-            return new CalculationResult(new WQKms[0], this);
-        }
-
-        double [] ws = wqs[0];
-        double [] qs = wqs[1];
-        double [] kms = new double[ws.length];
-
-        Arrays.fill(kms, km);
-
-        WQKms wqkms = new WQKms(kms, qs, ws, String.valueOf(km));
-
-        if (hasProblems()) {
-            logger.debug("found " + numProblems() + " problems.");
-            wqkms.removeNaNs();
-        }
-
-        return new CalculationResult(new WQKms[] { wqkms }, this);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/Calculation3.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import org.apache.log4j.Logger;
-
-public class Calculation3
-extends      Calculation
-{
-    private static Logger logger = Logger.getLogger(Calculation3.class);
-
-    protected double    km;
-    protected int    [] days;
-    protected double [] qs;
-
-    public Calculation3() {
-    }
-
-    public Calculation3(double km, int [] days, double [] qs) {
-        this.km   = km;
-        this.days = days;
-        this.qs   = qs;
-    }
-
-    public CalculationResult calculate(WstValueTable wst) {
-
-        double [] ws = wst.interpolateW(km, qs, new double[qs.length], this);
-
-        if (days == null || days.length == 0) {
-            addProblem(km, "cannot.find.ds");
-        }
-
-        if (logger.isDebugEnabled()) {
-            logger.debug("Calculate duration curve data:");
-            logger.debug("    km       : " + km);
-            logger.debug("    num Days : " + (days != null ? days.length : 0));
-            logger.debug("    num Qs   : " + (qs != null ? qs.length : 0));
-            logger.debug("    result Ws: " + (ws != null ? ws.length : 0));
-        }
-
-        WQDay wqday = new WQDay(days, ws, qs);
-
-        if (hasProblems()) {
-            logger.debug("calculation caused "+numProblems()+" problem(s).");
-            wqday.removeNaNs();
-        }
-
-        return new CalculationResult(wqday, this);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/Calculation4.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,370 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import de.intevation.flys.artifacts.access.Calculation4Access;
-
-import de.intevation.flys.artifacts.math.BackJumpCorrector;
-import de.intevation.flys.artifacts.math.Function;
-import de.intevation.flys.artifacts.math.Identity;
-import de.intevation.flys.artifacts.math.Linear;
-
-import de.intevation.flys.artifacts.model.RiverFactory;
-
-import de.intevation.flys.artifacts.model.WstValueTable.QPosition;
-
-import de.intevation.flys.model.River;
-
-import de.intevation.flys.utils.DoubleUtil;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-public class Calculation4
-extends      Calculation
-{
-    private static Logger logger = Logger.getLogger(Calculation4.class);
-
-    public static final double MINIMAL_STEP_WIDTH = 1e-5;
-
-    protected List<Segment> segments;
-
-    protected boolean isQ;
-    protected double  from;
-    protected double  to;
-    protected double  step;
-    protected String  river;
-
-    public Calculation4() {
-    }
-
-    public Calculation4(Calculation4Access access) {
-        logger.debug("Calculation4Access.cnst");
-        String        river    = access.getRiver();
-        List<Segment> segments = access.getSegments();
-        double []     range    = access.getFromToStep();
-        boolean       isQ      = access.isQ();
-
-        if (river == null) {
-            addProblem("no.river.selected");
-        }
-
-        if (range == null) {
-            addProblem("no.range.found");
-        }
-
-        if (segments == null || segments.isEmpty()) {
-            addProblem("cannot.create.segments");
-        }
-
-        if (!hasProblems()) {
-            this.river    = river;
-            this.segments = segments;
-            this.from     = range[0];
-            this.to       = range[1];
-            this.step     = range[2];
-            this.isQ      = isQ;
-        }
-    }
-
-    public CalculationResult calculate() {
-        if (hasProblems()) {
-            return new CalculationResult(new WQKms[0], this);
-        }
-
-        WstValueTable table = null;
-        River r = RiverFactory.getRiver(river);
-        if (r == null) {
-            addProblem("no.river.found");
-        }
-        else {
-            table = WstValueTableFactory.getTable(r);
-            if (table == null) {
-                addProblem("no.wst.for.river");
-            }
-            else {
-                Segment.setReferencePointConvertQ(segments, r, isQ, this);
-            }
-        }
-
-        return hasProblems()
-            ? new CalculationResult(new WQKms[0], this)
-            : innerCalculate(table);
-    }
-
-    protected CalculationResult innerCalculate(WstValueTable table) {
-        boolean debug = logger.isDebugEnabled();
-
-        if (debug) {
-            logger.debug(
-                "calculate from " + from + " to " + to + " step " + step);
-            logger.debug("# segments: " + segments.size());
-            for (Segment segment: segments) {
-                logger.debug("  " + segment);
-            }
-        }
-
-        int numResults = segments.get(0).values.length;
-
-        if (numResults < 1) {
-            logger.debug("no values given");
-            addProblem("no.values.given");
-            return new CalculationResult(new WQKms[0], this);
-        }
-
-
-        WQKms [] results = new WQKms[numResults];
-        for (int i = 0; i < results.length; ++i) {
-            results[i] = new WQKms();
-        }
-
-        if (Math.abs(step) < MINIMAL_STEP_WIDTH) {
-            step = MINIMAL_STEP_WIDTH;
-        }
-
-        if (from > to) {
-            step = -step;
-        }
-
-        QPosition [] qPositions = new QPosition[numResults];
-
-        Function [] functions = new Function[numResults];
-
-        double [] out = new double[2];
-
-        Segment sentinel = new Segment(Double.MAX_VALUE);
-        Segment s1 = sentinel, s2 = sentinel;
-
-        for (double pos = from;
-             from < to ? pos <= to : pos >= to;
-             pos = DoubleUtil.round(pos + step)
-        ) {
-            if (pos < s1.referencePoint || pos > s2.referencePoint) {
-                if (debug) {
-                    logger.debug("need to find new interval for " + pos);
-                }
-                // find new interval
-                if (pos <= segments.get(0).referencePoint) {
-                    // before first segment -> "gleichwertig"
-                    if (debug) {
-                        logger.debug("before first segment -> gleichwertig");
-                    }
-                    Segment   first  = segments.get(0);
-                    double [] values = first.values;
-                    double    refPos = first.referencePoint;
-                    for (int i = 0; i < qPositions.length; ++i) {
-                        qPositions[i] = table.getQPosition(
-                            refPos, values[i]);
-                    }
-                    sentinel.setReferencePoint(-Double.MAX_VALUE);
-                    s1 = sentinel;
-                    s2 = segments.get(0);
-                    Arrays.fill(functions, Identity.IDENTITY);
-                }
-                else if (pos >= segments.get(segments.size()-1).referencePoint) {
-                    // after last segment -> "gleichwertig"
-                    if (debug) {
-                        logger.debug("after last segment -> gleichwertig");
-                    }
-                    Segment   last   = segments.get(segments.size()-1);
-                    double [] values = last.values;
-                    double    refPos = last.referencePoint;
-                    for (int i = 0; i < qPositions.length; ++i) {
-                        qPositions[i] = table.getQPosition(
-                            refPos, values[i]);
-                    }
-                    sentinel.setReferencePoint(Double.MAX_VALUE);
-                    s1 = last;
-                    s2 = sentinel;
-                    Arrays.fill(functions, Identity.IDENTITY);
-                }
-                else { // "ungleichwertig"
-                    // find matching interval
-                    if (debug) {
-                        logger.debug("in segments -> ungleichwertig");
-                    }
-                    s1 = s2 = null;
-                    for (int i = 1, N = segments.size(); i < N; ++i) {
-                        Segment si1 = segments.get(i-1);
-                        Segment si  = segments.get(i);
-                        if (debug) {
-                            logger.debug("check " + pos + " in " +
-                                si1.referencePoint + " - " + si.referencePoint);
-                        }
-                        if (pos >= si1.referencePoint
-                        &&  pos <= si. referencePoint) {
-                            s1 = si1;
-                            s2 = si;
-                            break;
-                        }
-                    }
-
-                    if (s1 == null) {
-                        throw new IllegalStateException("no interval found");
-                    }
-
-                    Segment anchor, free;
-
-                    if (from > to) { anchor = s1; free = s2; }
-                    else           { anchor = s2; free = s1; }
-
-                    // build transforms based on "gleichwertiger" phase
-                    for (int i = 0; i < qPositions.length; ++i) {
-                        QPosition qi = table.getQPosition(
-                            anchor.referencePoint,
-                            anchor.values[i]);
-
-                        if ((qPositions[i] = qi) == null) {
-                            addProblem(pos, "cannot.find.q", anchor.values[i]);
-                            functions[i] = Identity.IDENTITY;
-                        }
-                        else {
-                            double qA = table.getQ(qi, anchor.referencePoint);
-                            double qF = table.getQ(qi, free  .referencePoint);
-
-                            functions[i] = Double.isNaN(qA) || Double.isNaN(qF)
-                                ? Identity.IDENTITY
-                                : new Linear(
-                                    qA, qF,
-                                    anchor.values[i], free.values[i]);
-
-                            if (debug) {
-                                logger.debug(
-                                    anchor.referencePoint + ": " +
-                                    qA + " -> " + functions[i].value(qA) +
-                                    " / " + free.referencePoint + ": " +
-                                    qF + " -> " + functions[i].value(qF));
-                            }
-                        }
-                    } // build transforms
-                } // "ungleichwertiges" interval
-            } // find matching interval
-
-            for (int i = 0; i < qPositions.length; ++i) {
-                QPosition qPosition = qPositions[i];
-
-                if (qPosition == null) {
-                    continue;
-                }
-
-                if (table.interpolate(pos, out, qPosition, functions[i])) {
-                    results[i].add(out[0], out[1], pos);
-                }
-                else {
-                    addProblem(pos, "cannot.interpolate.w.q");
-                }
-            }
-        }
-
-        // Backjump correction
-        for (int i = 0; i < results.length; ++i) {
-            BackJumpCorrector bjc = new BackJumpCorrector();
-
-            double [] ws  = results[i].getWs();
-            double [] kms = results[i].getKms();
-
-            if (bjc.doCorrection(kms, ws, this)) {
-                results[i] = new WQCKms(results[i], bjc.getCorrected());
-            }
-        }
-
-        // Name the curves.
-        for (int i = 0; i < results.length; ++i) {
-            results[i].setName(createName(i));
-        }
-
-        // Generate the "Umhuellende".
-        ConstantWQKms [] infoldings =
-            generateInfolding(table, results, from, to, step);
-
-        // TODO: Use qkms in a new result type.
-        WQKms [] newResults = new WQKms[results.length + infoldings.length];
-        System.arraycopy(
-            results, 0, newResults, 0, results.length);
-        System.arraycopy(
-            infoldings, 0, newResults, results.length, infoldings.length);
-
-        return new CalculationResult(newResults, this);
-    }
-
-    protected ConstantWQKms [] generateInfolding(
-        WstValueTable wst,
-        WQKms []      results,
-        double        from,
-        double        to,
-        double        step
-    ) {
-        WstValueTable.Column [] columns = wst.getColumns();
-
-        InfoldingColumns ic = new InfoldingColumns(columns);
-        ic.markInfoldingColumns(results);
-
-        List<ConstantWQKms> infoldings = new ArrayList<ConstantWQKms>();
-
-        boolean [] infoldingColumns = ic.getInfoldingColumns();
-
-        double [] kms = null;
-        double [] ws  = null;
-
-        for (int i = 0; i < infoldingColumns.length; ++i) {
-            if (!infoldingColumns[i]) {
-                continue;
-            }
-
-            if (kms == null) {
-                kms = DoubleUtil.explode(from, to, step);
-                ws  = new double[kms.length];
-            }
-
-            QRangeTree.QuickQFinder qf =
-                columns[i].getQRangeTree().new QuickQFinder();
-
-            int numProblemsBefore = numProblems();
-            double [] qs = qf.findQs(kms, this);
-
-            String name = columns[i].getName();
-            ConstantWQKms infolding = new ConstantWQKms(kms, qs, ws, name);
-
-            if (numProblems() > numProblemsBefore) {
-                infolding.removeNaNs();
-            }
-
-            infoldings.add(infolding);
-        }
-
-        for (int i = 0; i < infoldings.size(); i++) {
-            String name = infoldings.get(i).getName();
-            // TODO: i18n
-            if (i == 0) {
-                infoldings.get(i).setName("untere Umh\u00fcllende " + name);
-            }
-            else if (i ==  infoldings.size() - 1) {
-                infoldings.get(i).setName("obere Umh\u00fcllende " + name);
-            }
-            else {
-                infoldings.get(i).setName("geschnitten " + name);
-            }
-        }
-        return infoldings.toArray(new ConstantWQKms[infoldings.size()]);
-    }
-
-    // TODO: issue1109/2, merge with FixRealizingCalculation
-    protected String createName(int index) {
-        // TODO: i18n
-        StringBuilder sb = new StringBuilder(isQ ? "Q" : "W");
-        sb.append(" benutzerdefiniert (");
-        for (int i = 0, N = segments.size(); i < N; ++i) {
-            if (i > 0) {
-                sb.append("; ");
-            }
-            Segment segment = segments.get(i);
-            sb.append((segment.backup != null
-                ? segment.backup
-                : segment.values)[index]);
-        }
-        sb.append(')');
-        return sb.toString();
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/Calculation5.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,77 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.flys.utils.Formatter;
-
-import java.text.NumberFormat;
-
-import java.util.ArrayList;
-import java.util.Map;
-
-import org.apache.log4j.Logger;
-
-public class Calculation5
-extends      Calculation
-{
-    private static Logger log = Logger.getLogger(Calculation5.class);
-
-    protected double    startKm;
-    protected double [] endKms;
-
-    public Calculation5() {
-    }
-
-    public Calculation5(double startKm, double [] endKms) {
-        this.startKm = startKm;
-        this.endKms  = endKms;
-    }
-
-    public CalculationResult calculate(
-        WstValueTable       wst,
-        Map<Double, Double> kms2gaugeDatums,
-        CallContext         context
-    ) {
-        ArrayList<WWQQ> results = new ArrayList<WWQQ>();
-
-        int numProblems = numProblems();
-
-        CallMeta meta = context.getMeta();
-
-        for (double endKm: endKms) {
-
-            double [][] wws = wst.relateWs(startKm, endKm, this);
-            int newNumProblems = numProblems();
-
-            if (wws.length == 4) {
-                WWQQ wwqq = new WWQQ(
-                    generateName(meta, startKm, endKm),
-                    startKm, kms2gaugeDatums.get(startKm), wws[0], wws[1],
-                    endKm,   kms2gaugeDatums.get(endKm),   wws[2], wws[3]);
-
-                if (newNumProblems > numProblems) {
-                    wwqq.removeNaNs();
-                }
-
-                results.add(wwqq);
-            }
-            numProblems = newNumProblems;
-        }
-
-        return new CalculationResult(
-            results.toArray(new WWQQ[results.size()]),
-            this);
-    }
-
-    protected static String generateName(
-        CallMeta meta,
-        double   startKm,
-        double   endKm
-    ) {
-        NumberFormat nf = Formatter.getCalculationKm(meta);
-        return "W(km " + nf.format(startKm) +
-               ") ~ W(km " + nf.format(endKm) + ")";
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/Calculation6.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,347 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.flys.model.DischargeTable;
-import de.intevation.flys.model.Gauge;
-import de.intevation.flys.model.TimeInterval;
-
-
-/**
- * Historical Discharge Calculation.
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class Calculation6 extends Calculation {
-
-    private static final Logger logger = Logger.getLogger(Calculation6.class);
-
-    private int mode;
-    private long[] timerange;
-    private double[] values;
-
-    public static final int MODE_W = 0;
-    public static final int MODE_Q = 1;
-
-    public static final double SCALE = 1d;
-
-    public Calculation6(int mode, long[] timerange, double[] values) {
-        this.mode = mode;
-        this.timerange = timerange;
-        this.values = values;
-    }
-
-    public CalculationResult calculate(Gauge gauge) {
-        if (!checkParameters() || gauge == null) {
-            logger.warn("Parameters not valid for calculation.");
-
-            return null;
-        }
-
-        if (logger.isDebugEnabled()) {
-            debug();
-        }
-
-        DischargeTable refTable = fetchReferenceTable(gauge);
-        List<DischargeTable> dts = fetchDischargeTables(gauge);
-
-        int numTables = dts.size();
-
-        logger.debug("Take " + numTables + " into account.");
-
-        if (numTables == 0) {
-            addProblem("cannot.find.hist.q.tables");
-        }
-
-        WQTimerange[] wqt = prepareTimerangeData(refTable, dts);
-        WQKms[] wqs = prepareWQData(dts);
-
-        logger.debug("Number of calculation results: " + wqt.length);
-
-        return new CalculationResult(new HistoricalDischargeData(wqt, wqs),
-            this);
-    }
-
-    protected boolean checkParameters() {
-        if (!(mode == MODE_W || mode == MODE_Q)) {
-            logger.warn("Invalid mode '" + mode + "' for calculation.");
-            return false;
-        }
-
-        if (timerange == null || timerange.length < 2) {
-            logger.warn("Invalid timerange for calculation.");
-            return false;
-        }
-
-        if (values == null || values.length == 0) {
-            logger.warn("No values for W or Q specified.");
-            return false;
-        }
-
-        return true;
-    }
-
-    protected DischargeTable fetchReferenceTable(Gauge gauge) {
-        return gauge.fetchMasterDischargeTable();
-    }
-
-    protected List<DischargeTable> fetchDischargeTables(Gauge gauge) {
-        List<DischargeTable> relevant = new ArrayList<DischargeTable>();
-        List<DischargeTable> all = gauge.getDischargeTables();
-
-        for (DischargeTable dt : all) {
-            if (isDischargeTableRelevant(dt)) {
-                relevant.add(dt);
-            }
-        }
-
-        return relevant;
-    }
-
-    /** True if timerange of given discharge table overlaps with timerange. */
-    protected boolean isDischargeTableRelevant(DischargeTable dt) {
-        TimeInterval ti = dt.getTimeInterval();
-
-        if (dt.getKind() == Gauge.MASTER_DISCHARGE_TABLE || ti == null) {
-            return false;
-        }
-
-        Date start = ti.getStartTime();
-        long startTime = start.getTime();
-
-        if (startTime >= timerange[0] && startTime <= timerange[1]) {
-            return true;
-        }
-
-        Date stop = ti.getStopTime();
-        long stopTime = stop != null ? stop.getTime() : -1l;
-
-        if (stopTime >= timerange[0] && stopTime <= timerange[1]) {
-            return true;
-        }
-
-        logger.debug("DischargeTable not in range: " + start + " -> " + stop);
-
-        return false;
-    }
-
-    protected WQTimerange[] prepareTimerangeData(DischargeTable refTable,
-        List<DischargeTable> dts) {
-        if (refTable == null) {
-            addProblem("cannot.find.hist.q.reftable");
-            return prepareSimpleData(dts);
-        }
-        else {
-            return prepareData(refTable, dts);
-        }
-    }
-
-    protected WQKms[] prepareWQData(List<DischargeTable> dts) {
-        WQKms[] wqs = new WQKms[dts.size()];
-
-        int idx = 0;
-
-        for (DischargeTable dt : dts) {
-            double[][] values = null;
-
-            if (dt.getKind() == DischargeTables.MASTER) {
-                values = DischargeTables.loadDischargeTableValues(dt,
-                    DischargeTables.MASTER_SCALE);
-
-            }
-            else {
-                values = DischargeTables.loadDischargeTableValues(dt,
-                    DischargeTables.HISTORICAL_SCALE);
-
-            }
-
-            wqs[idx++] = prepareWQ(dt, values);
-        }
-
-        return wqs;
-    }
-
-    protected WQKms prepareWQ(DischargeTable dt, double[][] values) {
-        double km = dt.getGauge().getStation().doubleValue();
-
-        double[] kms = new double[values[0].length];
-        Arrays.fill(kms, km);
-
-        return new HistoricalWQKms(kms, values[0], values[1],
-            String.valueOf(km), dt.getTimeInterval());
-    }
-
-    /** Without reference. */
-    protected WQTimerange[] prepareSimpleData(List<DischargeTable> dts) {
-        List<WQTimerange> wqts = new ArrayList<WQTimerange>(values.length);
-
-        for (double value : values) {
-            logger.debug("Prepare data for value: " + value);
-
-            String name = mode == MODE_W ? "W=" + value : "Q=" + value;
-            WQTimerange wqt = null;
-
-            for (DischargeTable dt : dts) {
-                Date[] ti = prepareTimeInterval(dt);
-                Timerange t = new Timerange(ti[0], ti[1]);
-                double w;
-                double q;
-
-                if (mode == MODE_W) {
-                    w = value;
-                    q = findValueForW(dt, w, DischargeTables.HISTORICAL_SCALE);
-
-                    if (Double.isNaN(q)) {
-                        logger.warn("Cannot find Q for W: " + w);
-                        addProblem("cannot.find.hist.q.for.w", w, ti[0], ti[1]);
-                        continue;
-                    }
-                }
-                else {
-                    q = value;
-                    w = findValueForQ(dt, q, DischargeTables.HISTORICAL_SCALE);
-                }
-
-                logger.debug("Q=" + q + " | W=" + w);
-
-                if (wqt == null) {
-                    wqt = new WQTimerange(name);
-                }
-
-                wqt.add(w, q, t);
-            }
-
-            if (wqt != null) {
-                wqts.add(wqt);
-            }
-        }
-
-        return wqts.toArray(new WQTimerange[wqts.size()]);
-    }
-
-    /** With reference. */
-    protected HistoricalWQTimerange[] prepareData(DischargeTable refTable,
-        List<DischargeTable> dts) {
-        List<HistoricalWQTimerange> wqts = new ArrayList<HistoricalWQTimerange>(
-            values.length);
-
-        for (double value : values) {
-            logger.debug("Prepare data plus diff for value: " + value);
-
-            String name = mode == MODE_W ? "W=" + value : "Q=" + value;
-            HistoricalWQTimerange wqt = null;
-
-            double ref;
-            double diff;
-
-            if (refTable != null && mode == MODE_W) {
-                ref = findValueForW(refTable, value,
-                    DischargeTables.MASTER_SCALE);
-            }
-            else if (refTable != null) {
-                ref = findValueForQ(refTable, value,
-                    DischargeTables.MASTER_SCALE);
-            }
-            else {
-                ref = Double.NaN;
-            }
-
-            for (DischargeTable dt : dts) {
-                Date[] ti = prepareTimeInterval(dt);
-
-                Timerange t = new Timerange(ti[0], ti[1]);
-                double w;
-                double q;
-
-                if (mode == MODE_W) {
-                    w = value;
-                    q = findValueForW(dt, w, DischargeTables.HISTORICAL_SCALE);
-
-                    if (Double.isNaN(q)) {
-                        logger.warn("Cannot find Q for W: " + w);
-                        addProblem("cannot.find.hist.q.for.w", w, ti[0], ti[1]);
-                        continue;
-                    }
-
-                    diff = ref - q;
-                }
-                else {
-                    q = value;
-                    w = findValueForQ(dt, q, DischargeTables.HISTORICAL_SCALE);
-
-                    if (Double.isNaN(w)) {
-                        logger.warn("Cannot find W for Q: " + q);
-                        addProblem("cannot.find.hist.w.for.q", q, ti[0], ti[1]);
-                        continue;
-                    }
-                    diff = ref - w;
-                }
-
-                logger.debug("Q=" + q + " | W=" + w + " | Ref = " + ref);
-
-                if (wqt == null) {
-                    wqt = new HistoricalWQTimerange(name);
-                }
-
-                wqt.add(w, q, diff, t);
-            }
-
-            if (wqt != null) {
-                wqts.add(wqt);
-            }
-        }
-
-        return (HistoricalWQTimerange[]) wqts
-            .toArray(new HistoricalWQTimerange[wqts.size()]);
-    }
-
-    /** Returns discharge table interval as Date[]. */
-    protected Date[] prepareTimeInterval(DischargeTable dt) {
-        TimeInterval ti = dt.getTimeInterval();
-
-        Date start = ti.getStartTime();
-        Date end = ti.getStopTime();
-
-        if (end == null) {
-            logger.warn("TimeInterval has no stop time set!");
-
-            end = new Date();
-        }
-
-        return new Date[] { start, end };
-    }
-
-    protected double findValueForW(DischargeTable dt, double w, double scale) {
-        double[][] vs = DischargeTables.loadDischargeTableValues(dt, scale);
-        double[] qs = DischargeTables.getQsForW(vs, w);
-        return qs.length == 0 ? Double.NaN : qs[0];
-    }
-
-    protected double findValueForQ(DischargeTable dt, double q, double scale) {
-        double[][] vs = DischargeTables.loadDischargeTableValues(dt, scale);
-        double[] ws = DischargeTables.getWsForQ(vs, q);
-
-        return ws.length == 0 ? Double.NaN : ws[0];
-    }
-
-    /**
-     * Writes the parameters used for this calculation to logger.
-     */
-    public void debug() {
-        StringBuilder sb = new StringBuilder();
-        for (double value : values) {
-            sb.append(String.valueOf(value) + " ");
-        }
-
-        logger.debug("========== Calculation6 ==========");
-        logger.debug("   Mode:         " + mode);
-        logger.debug("   Timerange:    " + timerange[0] + " - " + timerange[1]);
-        logger.debug("   Input values: " + sb.toString());
-        logger.debug("==================================");
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/CalculationMessage.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import de.intevation.artifacts.Message;
-
-
-public class CalculationMessage implements Message {
-
-    protected String message;
-    protected int    steps;
-    protected int    currentStep;
-
-
-    public CalculationMessage() {
-    }
-
-
-    public CalculationMessage(int steps, int currentStep, String message) {
-        this.steps       = steps;
-        this.currentStep = currentStep;
-        this.message     = message;
-    }
-
-
-    public int getSteps() {
-        return steps;
-    }
-
-
-    public int getCurrentStep() {
-        return currentStep;
-    }
-
-
-    public String getMessage() {
-        return message;
-    }
-
-
-    @Override
-    public String getText() {
-        return
-            String.valueOf(currentStep) + "/" + String.valueOf(steps) +
-            " - " + getMessage();
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/CalculationResult.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,45 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.io.Serializable;
-
-/**
- * Wraps result(s) of a Calculation and eventual error reports.
- */
-public class CalculationResult
-implements   Serializable
-{
-    protected Object      data;
-    protected Calculation report;
-
-    public CalculationResult() {
-    }
-
-    public CalculationResult(Calculation report) {
-        this(null, report);
-    }
-
-    /**
-     * @param report report (e.g. error messages).
-     */
-    public CalculationResult(Object data, Calculation report) {
-        this.data   = data;
-        this.report = report;
-    }
-
-    public Object getData() {
-        return data;
-    }
-
-    public void setData(Object data) {
-        this.data = data;
-    }
-
-    public Calculation getReport() {
-        return report;
-    }
-
-    public void setReport(Calculation report) {
-        this.report = report;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/ConstantWQKms.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,30 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-public class ConstantWQKms
-extends      WQKms
-{
-    public ConstantWQKms() {
-        this("");
-    }
-
-    public ConstantWQKms(String name) {
-        super(name);
-    }
-
-    public ConstantWQKms(int capacity) {
-        this(capacity, "");
-    }
-
-    public ConstantWQKms(int capacity, String name) {
-        super(capacity, name);
-    }
-
-    public ConstantWQKms(double [] kms, double [] qs, double [] ws) {
-        this(kms, qs, ws, "");
-    }
-
-    public ConstantWQKms(double [] kms, double [] qs, double [] ws, String name) {
-        super(kms, qs, ws, name);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/CrossSectionFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,122 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.artifactdatabase.state.Facet;
-
-import de.intevation.flys.artifacts.CrossSectionArtifact;
-
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-
-/**
- * Trival Facet for Cross Sections (profiles).
- */
-public class CrossSectionFacet
-extends      BlackboardDataFacet
-implements   FacetTypes {
-
-    public static String BLACKBOARD_CS_MASTER_DATA
-        = "crosssection.masterprofile.data";
-
-    public static String BLACKBOARD_CS_PREV_KM
-        = "crosssection.masterprofile.km.prev";
-
-    public static String BLACKBOARD_CS_NEXT_KM
-        = "crosssection.masterprofile.km.next";
-
-
-    private static Logger logger = Logger.getLogger(CrossSectionFacet.class);
-
-    protected ComputeType type;
-
-
-    /** Trivial constructor, set (maybe localized) description. */
-    public CrossSectionFacet(int idx, String description) {
-        super(idx, CROSS_SECTION, description);
-        type = ComputeType.ADVANCE;
-    }
-
-
-    /** Tell world we know about crosssection masters data and its index. */
-    public List getStaticDataProviderKeys(Artifact art) {
-        CrossSectionArtifact artifact = (CrossSectionArtifact) art;
-        List keys = new ArrayList();
-        if (artifact.isMaster()) {
-            keys.add(BLACKBOARD_CS_MASTER_DATA);
-            keys.add(BLACKBOARD_CS_PREV_KM);
-            keys.add(BLACKBOARD_CS_NEXT_KM);
-        }
-        keys.add(artifact.identifier() + getIndex());
-        keys.addAll(super.getStaticDataProviderKeys(art));
-        return keys;
-    }
-
-
-    /**
-     * Can provide the master cross section lines or its index.
-     * @param artifact crosssection-artifact
-     * @param key      will respond on BLACKBOARD_CS_MASTER_DATA
-     * @param param    ignored
-     * @param context  ignored
-     * @return data from artifact (cross section master track).
-     */
-    public Object provideBlackboardData(Artifact artifact,
-        Object key,
-        Object param,
-        CallContext context
-    ) {
-        CrossSectionArtifact crossSection = (CrossSectionArtifact) artifact;
-
-        if (key.equals(BLACKBOARD_CS_MASTER_DATA)) {
-            return crossSection.searchCrossSectionLine();
-        }
-        else if (key.equals(artifact.identifier() + getIndex())) {
-            return getData(artifact, context);
-        }
-        else if (key.equals(BLACKBOARD_CS_NEXT_KM)) {
-            return crossSection.getNextKm();
-        }
-        else if (key.equals(BLACKBOARD_CS_PREV_KM)) {
-            return crossSection.getPrevKm();
-        }
-        else {
-            Object obj = super.provideBlackboardData(artifact, key, param,
-                context);
-            if (obj == null) {
-                logger.warn("Cannot provide data for key: " + key);
-            }
-            return obj;
-        }
-    }
-
-
-    /**
-     * Gets Cross Section (profile).
-     * @param art artifact to get data from.
-     * @param context ignored
-     */
-    public Object getData(Artifact art, CallContext context) {
-        logger.debug("Get data for cross section");
-
-        CrossSectionArtifact artifact = (CrossSectionArtifact)art;
-
-        return artifact.getCrossSectionData();
-    }
-
-
-    /** Do a deep copy. */
-    @Override
-    public Facet deepCopy() {
-        CrossSectionFacet copy = new CrossSectionFacet(this.index, this.description);
-        copy.set(this);
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/CrossSectionFactory.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,112 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.util.List;
-
-import net.sf.ehcache.Cache;
-import net.sf.ehcache.Element;
-
-import de.intevation.flys.backend.SessionHolder;
-import de.intevation.flys.model.CrossSection;
-import de.intevation.flys.model.River;
-
-import de.intevation.flys.artifacts.cache.CacheFactory;
-
-import org.hibernate.Session;
-import org.hibernate.Query;
-
-
-/**
- * Get Cross Sections.
- */
-public class CrossSectionFactory {
-
-    protected final static String CACHE_NAME = "cross_sections";
-
-    // TODO use caching consistently, streamline acces.
-    /**
-     * Get CrossSections for an instantiated River.
-     *
-     * @param river river object.
-     *
-     * @return List of Cross Sections of river.
-     */
-    public static List<CrossSection> getCrossSections(River river) {
-        return getCrossSections(river.getName());
-    }
-
-
-    /**
-     * Get Cross Sections for a river by name.
-     *
-     * @param riverName name of the river of interest.
-     *
-     * @return List of Cross Sections of river.
-     */
-    public static List<CrossSection> getCrossSections(String riverName) {
-        Session session = SessionHolder.HOLDER.get();
-        Query query = session.createQuery(
-            "from CrossSection where river.name = :rivername");
-        query.setParameter("rivername", riverName);
-        return query.list();
-    }
-
-
-    /**
-     * True if the given section is the "newest" for that river.
-     * @param section Given section
-     * @return true if the section has the most advanced end of its validity interval
-     *         or the most advanced start of its validity interval.
-     */
-    public static boolean isNewest(CrossSection section) {
-        Session session = SessionHolder.HOLDER.get();
-        Query query = session.createQuery(
-            "from CrossSection where river.id = :riverid "
-            + " order by timeInterval.stopTime desc, timeInterval.startTime desc");
-        query.setParameter("riverid", section.getRiver().getId());
-
-        List result = query.list();
-
-        if (result == null || result.isEmpty()) {
-            return true;
-        }
-        else {
-            CrossSection cs = (CrossSection) result.get(0);
-            return section.getId().equals(cs.getId());
-        }
-    }
-
-
-    /**
-     *  Get a specific CrossSection from db.
-     *  @param id The dbid of the cross-section to load.
-     */
-    public static CrossSection getCrossSection(int id) {
-        Cache cache = CacheFactory.getCache(CACHE_NAME);
-        if (cache != null) {
-            Element element = cache.get(Integer.valueOf(id));
-            if (element != null) {
-                return (CrossSection) element.getValue();
-            }
-        }
-
-        CrossSection section = getCrossSectionUncached(id);
-        if (cache != null) {
-            Element element = new Element(Integer.valueOf(id), section);
-            cache.put(element);
-        }
-
-        return section;
-    }
-
-
-    /** Get specific CrossSection from database. */
-    protected static CrossSection getCrossSectionUncached(int id) {
-        Session session = SessionHolder.HOLDER.get();
-        Query query = session.createQuery(
-                "from CrossSection where id=:id");
-        query.setParameter("id", id);
-        List<CrossSection> list = query.list();
-        return list.isEmpty() ? null : list.get(0);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/CrossSectionWaterLineFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,101 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import org.apache.log4j.Logger;
-
-import java.util.List;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.DataProvider;
-
-import de.intevation.artifactdatabase.state.Facet;
-
-import de.intevation.flys.artifacts.WaterLineArtifact;
-
-import de.intevation.flys.model.FastCrossSectionLine;
-
-import de.intevation.flys.artifacts.geom.Lines;
-
-
-/**
- * Facet for Waterlines in Cross Sections.
- */
-public class CrossSectionWaterLineFacet
-extends      BlackboardDataFacet
-implements   FacetTypes {
-
-    /** Private logger to use. */
-    private static Logger logger =
-        Logger.getLogger(CrossSectionWaterLineFacet.class);
-
-
-    /** Trivial constructor, set (maybe localized) description. */
-    public CrossSectionWaterLineFacet(int idx, String description) {
-        super(idx, CROSS_SECTION_WATER_LINE, description);
-    }
-
-
-    /**
-     * Trivial constructor, set (maybe localized) description.
-     * @param idx Index of this facet.
-     * @param name 'type' of this facet.
-     * @param description (maybe) localized user-visible description.
-     */
-    public CrossSectionWaterLineFacet(int idx, String name, String description) {
-        super(idx, name, description);
-    }
-
-
-    /**
-     * Gets waterline (crossed with cross section) of waterlevel.
-     */
-    public Object getData(Artifact artifact, CallContext context) {
-        logger.debug("Get data for cross section water line");
-
-        List<DataProvider> providers = context.
-            getDataProvider(CrossSectionFacet.BLACKBOARD_CS_MASTER_DATA);
-        if (providers.size() < 1) {
-            logger.warn("Could not find Cross-Section data provider.");
-            return new Lines.LineData(new double[][] {}, 0d, 0d);
-        }
-
-        Object crossSection = providers.get(0)
-            .provideData(CrossSectionFacet.BLACKBOARD_CS_MASTER_DATA,
-                null, context);
-        Object nextKm = providers.get(0).
-            provideData(CrossSectionFacet.BLACKBOARD_CS_NEXT_KM, null, context);
-        Object prevKm = providers.get(0).
-            provideData(CrossSectionFacet.BLACKBOARD_CS_PREV_KM, null, context);
-        if (prevKm == null)
-            prevKm = new Double(-1d);
-        if (nextKm == null)
-            nextKm = new Double(-1d);
-
-        if (!(artifact instanceof WaterLineArtifact)) {
-            logger.error("CrossSectionWaterLineFacet needs WaterLineArtifact");
-            return new Lines.LineData(new double[][] {}, 0d,0d);
-        }
-        WaterLineArtifact lineArtifact = (WaterLineArtifact) artifact;
-
-        if (crossSection != null) {
-            return lineArtifact.getWaterLines(this.getIndex(),
-                (FastCrossSectionLine) crossSection, (Double) nextKm,
-                (Double) prevKm, context);
-        }
-        else {
-            return new Lines.LineData(new double[][] {}, 0d,0d);
-        }
-    }
-
-
-    /** Do a deep copy. */
-    @Override
-    public Facet deepCopy() {
-        CrossSectionWaterLineFacet copy = new CrossSectionWaterLineFacet(
-            this.getIndex(),
-            this.description);
-        copy.set(this);
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/DataFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,107 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.artifactdatabase.state.Facet;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-public class DataFacet
-extends      BlackboardDataFacet
-{
-    protected ComputeType type;
-    protected String      hash;
-    protected String      stateId;
-
-
-    /** Trivial constructor. */
-    public DataFacet() {
-    }
-
-    /**
-     * Defaults to ADVANCE Compute type.
-     * @param name Name of the facet.
-     * @param description maybe localized description of the facet.
-     */
-    public DataFacet(String name, String description) {
-        this(name, description, ComputeType.ADVANCE);
-    }
-
-
-    public DataFacet(String name, String description, ComputeType type) {
-        this(name, description, type, null);
-    }
-
-
-    public DataFacet(
-        String      name,
-        String      description,
-        ComputeType type,
-        String      hash
-    ) {
-        super(name, description);
-        this.type = type;
-        this.hash = hash;
-    }
-
-
-    public DataFacet(
-        String      name,
-        String      description,
-        ComputeType type,
-        String      hash,
-        String      stateId
-    ) {
-        super(name, description);
-        this.type    = type;
-        this.hash    = hash;
-        this.stateId = stateId;
-    }
-
-
-    public DataFacet(
-        int         index,
-        String      name,
-        String      description,
-        ComputeType type,
-        String      hash,
-        String      stateId
-    ) {
-        super(index, name, description);
-        this.type    = type;
-        this.hash    = hash;
-        this.stateId = stateId;
-    }
-
-
-    /**
-     * Return computation result.
-     */
-    @Override
-    public Object getData(Artifact artifact, CallContext context) {
-        FLYSArtifact flys = (FLYSArtifact)artifact;
-        String    theHash = (hash != null) ? hash : flys.hash();
-
-        return (stateId != null && stateId.length() > 0)
-            ? flys.compute(context, theHash, stateId, type, false)
-            : flys.compute(context, theHash, type, false);
-    }
-
-
-    /**
-     * Return a deep copy.
-     */
-    @Override
-    public Facet deepCopy() {
-        DataFacet copy = new DataFacet();
-        copy.set(this);
-        copy.type    = type;
-        copy.hash    = hash;
-        copy.stateId = stateId;
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/DateRange.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,51 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.io.Serializable;
-import java.util.Date;
-
-public class DateRange
-implements   Serializable
-{
-    private static final long serialVersionUID = -2553914795388094818L;
-
-    protected Date from;
-    protected Date to;
-
-    public DateRange(Date from, Date to) {
-        this.from = from;
-        this.to   = to;
-    }
-
-    public Date getFrom() {
-        return from;
-    }
-
-    public void setFrom(Date from) {
-        this.from = from;
-    }
-
-    public Date getTo() {
-        return to;
-    }
-
-    public void setTo(Date to) {
-        this.to = to;
-    }
-
-    private static final boolean equalDates(Date a, Date b) {
-        if (a == null && b != null) return false;
-        if (a != null && b == null) return false;
-        if (a == null) return true;
-        return a.equals(b);
-    }
-
-    @Override
-    public boolean equals(Object other) {
-        if (!(other instanceof DateRange)) {
-            return false;
-        }
-        DateRange o = (DateRange)other;
-        return equalDates(from, o.from) && equalDates(to, o.to);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/DifferenceCurveFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.flys.artifacts.WINFOArtifact;
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-
-/**
- * Facet with the curve of a subtraction of two waterlevel-lines.
- * TODO inherit directly from DataFacet? Check whether this Facet is obsolete.
- */
-public class DifferenceCurveFacet extends WaterlevelFacet {
-
-    private static Logger logger = Logger.getLogger(DifferenceCurveFacet.class);
-
-
-    public DifferenceCurveFacet() {
-    }
-
-    public DifferenceCurveFacet(
-        int         index,
-        String      name,
-        String      description,
-        ComputeType type,
-        String      stateID,
-        String      hash
-
-    ) {
-        super(index, name, description, type, stateID, hash);
-    }
-
-    /**
-     * Get difference curve data.
-     * @return a WKms at given index.
-     */
-    @Override
-    public Object getData(Artifact artifact, CallContext context) {
-        WINFOArtifact winfo = (WINFOArtifact)artifact;
-
-        CalculationResult res = (CalculationResult)
-            winfo.compute(context, hash, stateId, type, false);
-
-        WKms [] wkms = (WKms [])res.getData();
-
-        WKms result = wkms[index];
-        logger.debug("Got difference curve data (" + result.getName()
-            + ") at index: " + index);
-
-        return result;
-    }
-
-
-    /** Copy deeply. */
-    @Override
-    public Facet deepCopy() {
-        WaterlevelFacet copy = new DifferenceCurveFacet();
-        copy.set(this);
-        copy.type    = type;
-        copy.stateId = stateId;
-        copy.hash    = hash;
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,312 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.util.List;
-import java.util.Map;
-import java.util.HashMap;
-import java.util.Arrays;
-
-import java.io.Serializable;
-
-import org.hibernate.Session;
-import org.hibernate.Query;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.flys.backend.SessionHolder;
-import de.intevation.flys.model.Gauge;
-import de.intevation.flys.model.DischargeTable;
-import de.intevation.flys.model.DischargeTableValue;
-
-import gnu.trove.TDoubleArrayList;
-
-/** Documentation goes here. */
-public class DischargeTables
-implements   Serializable
-{
-    /** Private logger. */
-    private static Logger log = Logger.getLogger(DischargeTables.class);
-
-    /** Scale to convert discharge table values of master table into [cm]. */
-    public static final double MASTER_SCALE = 100d;
-
-    /** Scale to convert discharge table values of historical tables into [cm]. */
-    public static final double HISTORICAL_SCALE = 1d;
-
-    public static final int MASTER = 0;
-
-    protected List<String> gaugeNames;
-
-    protected String riverName;
-
-    protected double scale;
-
-    protected int    kind;
-
-    protected Map<String, double [][]> values;
-
-    public DischargeTables() {
-    }
-
-    public DischargeTables(String riverName, String gaugeName) {
-        this(riverName, gaugeName, MASTER);
-    }
-
-    public DischargeTables(String riverName, String gaugeName, int kind) {
-        this(riverName, new String [] { gaugeName }, kind);
-    }
-
-    public DischargeTables(String riverName, String [] gaugeNames) {
-        this(riverName, gaugeNames, MASTER);
-    }
-
-    public DischargeTables(String riverName, String [] gaugeNames, int kind) {
-        this(riverName, Arrays.asList(gaugeNames), kind);
-    }
-
-    public DischargeTables(
-        String       riverName,
-        List<String> gaugeNames,
-        int          kind
-    ) {
-        scale           = Double.NaN;
-        this.kind       = kind;
-        this.riverName  = riverName;
-        this.gaugeNames = gaugeNames;
-    }
-
-    public double [][] getFirstTable() {
-        return getFirstTable(MASTER_SCALE);
-    }
-
-    public double [][] getFirstTable(double scale) {
-        Map<String, double [][]> values = getValues(scale);
-        for (double [][] table: values.values()) {
-            return table;
-        }
-        return null;
-    }
-
-    public Map<String, double [][]> getValues() {
-        return getValues(MASTER_SCALE);
-    }
-
-    public Map<String, double [][]> getValues(double scale) {
-        if (values == null || scale != this.scale) {
-            values = loadValues(scale);
-            this.scale = scale;
-        }
-        return values;
-    }
-
-    /**
-     * Returns mapping of gauge name to values.
-     */
-    protected Map<String, double [][]> loadValues(double scale) {
-        Map<String, double [][]> values = new HashMap<String, double [][]>();
-
-        Session session = SessionHolder.HOLDER.get();
-
-        Query gaugeQuery = session.createQuery(
-            "from Gauge where name=:gauge and river.name=:river");
-        gaugeQuery.setParameter("river", riverName);
-
-        for (String gaugeName: gaugeNames) {
-            gaugeQuery.setParameter("gauge", gaugeName);
-            List<Gauge> gauges = gaugeQuery.list();
-            if (gauges.isEmpty()) {
-                log.warn(
-                    "no gauge '"+gaugeName+"' at river '"+riverName+"'");
-                continue;
-            }
-            Gauge gauge = gauges.get(0);
-
-            List<DischargeTable> tables = gauge.getDischargeTables();
-
-            if (tables.isEmpty()) {
-                log.warn(
-                    "no discharge table for gauge '" + gaugeName + "'");
-                continue;
-            }
-
-            // TODO: Filter by time interval
-            DischargeTable table = null;
-            for (DischargeTable dt : tables) {
-                if (dt.getKind() == 0) {
-                    table = dt;
-                    break;
-                }
-            }
-            if (table == null) {
-                table = tables.get(0);
-            }
-            double [][] vs = loadDischargeTableValues(table, scale);
-
-            values.put(gaugeName, vs);
-        }
-
-        return values;
-    }
-
-
-    /**
-     * @param table The discharge table
-     * @param scale The scale factor to adjust W and Q values.
-     *
-     * @return the values of a discharge table.
-     */
-    public static double[][] loadDischargeTableValues(
-        DischargeTable table,
-        double         scale
-    ) {
-        List<DischargeTableValue> dtvs = table.getDischargeTableValues();
-
-        final double [][] vs = new double[2][dtvs.size()];
-
-        int idx = 0;
-        for (DischargeTableValue dtv: dtvs) {
-            double q = dtv.getQ().doubleValue();
-            vs[0][idx] = q * scale;
-            vs[1][idx] = dtv.getW().doubleValue() * scale;
-            ++idx;
-        }
-
-        return vs;
-    }
-
-    private static final double EPSILON = 1e-5;
-
-    private static final boolean epsEquals(double a, double b) {
-        return Math.abs(a - b) < EPSILON;
-    }
-
-    private static final boolean between(double a, double b, double x) {
-        if (a > b) { double t = a; a = b; b = t; }
-        return x > a && x < b;
-    }
-
-    /**
-     * Find or interpolate Qs from q/w array.
-     * @param values [[q0,q1,q2],[w0,w1,w2]]
-     * @param w      W value to look for in values.
-     */
-    public static double [] getQsForW(double [][] values, double w) {
-
-        boolean debug = log.isDebugEnabled();
-
-        if (debug) {
-            log.debug("getQsForW: W = " + w);
-        }
-
-        double [] qs = values[0];
-        double [] ws = values[1];
-
-        int N = Math.min(qs.length, ws.length);
-
-        if (N == 0) {
-            if (debug) {
-                log.debug("Q(" + w + ") = []");
-            }
-            return new double [0];
-        }
-
-        TDoubleArrayList outQs = new TDoubleArrayList();
-
-        if (epsEquals(ws[0], w)) {
-            outQs.add(qs[0]);
-        }
-
-        for (int i = 1; i < N; ++i) {
-            if (epsEquals(ws[i], w)) {
-                outQs.add(qs[i]);
-            }
-            else if (between(ws[i-1], ws[i], w)) {
-                double w1 = ws[i-1];
-                double w2 = ws[i];
-                double q1 = qs[i-1];
-                double q2 = qs[i];
-
-                // q1 = m*w1 + b
-                // q2 = m*w2 + b
-                // q2 - q1 = m*(w2 - w1)
-                // m = (q2 - q1)/(w2 - w1) # w2 != w1
-                // b = q1 - m*w1
-                // w1 != w2
-
-                double m = (q2 - q1)/(w2 - w1);
-                double b = q1 - m*w1;
-                double q = w*m + b;
-
-                outQs.add(q);
-            }
-        }
-
-        double [] result = outQs.toNativeArray();
-
-        if (debug) {
-            log.debug("Q(" + w + ") = " + Arrays.toString(result));
-        }
-
-        return result;
-    }
-
-    public static double [] getWsForQ(double [][] values, double q) {
-
-        boolean debug = log.isDebugEnabled();
-
-        if (debug) {
-            log.debug("getWsForQ: W = " + q);
-        }
-
-        double [] qs = values[0];
-        double [] ws = values[1];
-
-        int N = Math.min(qs.length, ws.length);
-
-        if (N == 0) {
-            if (debug) {
-                log.debug("W(" + q + ") = []");
-            }
-            return new double [0];
-        }
-
-        TDoubleArrayList outWs = new TDoubleArrayList();
-
-        if (epsEquals(qs[0], q)) {
-            outWs.add(ws[0]);
-        }
-
-        for (int i = 1; i < N; ++i) {
-            if (epsEquals(qs[i], q)) {
-                outWs.add(ws[i]);
-            }
-            else if (between(qs[i-1], qs[i], q)) {
-                double w1 = ws[i-1];
-                double w2 = ws[i];
-                double q1 = qs[i-1];
-                double q2 = qs[i];
-
-                // w1 = m*q1 + b
-                // w2 = m*q2 + b
-                // w2 - w1 = m*(q2 - q1)
-                // m = (w2 - w1)/(q2 - q1) # q2 != q1
-                // b = w1 - m*q1
-                // q1 != q2
-
-                double m = (w2 - w1)/(q2 - q1);
-                double b = w1 - m*q1;
-                double w = q*m + b;
-
-                outWs.add(w);
-            }
-        }
-
-        double [] result = outWs.toNativeArray();
-
-        if (debug) {
-            log.debug("W(" + q + ") = " + Arrays.toString(result));
-        }
-
-        return result;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/DurationCurveFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,98 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.util.List;
-import java.util.ArrayList;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.artifactdatabase.state.DefaultFacet;
-import de.intevation.artifactdatabase.state.Facet;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.WINFOArtifact;
-
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-
-/**
- * Data of a duration curve.
- */
-public class DurationCurveFacet extends DefaultFacet {
-
-    private static Logger logger = Logger.getLogger(DurationCurveFacet.class);
-
-    /** Blackboard data provider key for durationcurve (wqday) data. */
-    public static String BB_DURATIONCURVE = "durationcurve";
-
-    /** Blackboard data provider key for km of durationcurve. */
-    public static String BB_DURATIONCURVE_KM = "durationcurve.km";
-
-    public DurationCurveFacet() {
-    }
-
-    public DurationCurveFacet(String name, String description) {
-        super(0, name, description);
-    }
-
-
-    /**
-     * Expose state computation from WINFOArtifact.
-     */
-    public Object getData(Artifact artifact, CallContext context) {
-        logger.debug("Get data for duration curve data");
-
-        WINFOArtifact winfo = (WINFOArtifact)artifact;
-
-        CalculationResult cr = (CalculationResult)winfo.compute(
-            context, ComputeType.ADVANCE, false);
-
-        return cr.getData();
-    }
-
-
-    @Override
-    public List getStaticDataProviderKeys(Artifact art) {
-        List list = new ArrayList();
-        list.add(BB_DURATIONCURVE);
-        list.add(BB_DURATIONCURVE_KM);
-        return list;
-    }
-
-
-    /**
-     * Can provide whatever getData returns and additionally the location.
-     * @param key      will respond on BB_DURATIONCURVE +KM
-     * @param param    ignored
-     * @param context  ignored
-     * @return whatever getData delivers or location.
-     */
-    @Override
-    public Object provideBlackboardData(Artifact artifact,
-        Object key,
-        Object param,
-        CallContext context
-    ) {
-        if (key.equals(BB_DURATIONCURVE)) {
-            return getData(artifact, context);
-        }
-        else if (key.equals(BB_DURATIONCURVE_KM)) {
-            return ((FLYSArtifact)artifact).getDataAsString("ld_locations");
-        }
-        else {
-            return null;
-        }
-    }
-
-
-    /** Create a deep copy. */
-    @Override
-    public Facet deepCopy() {
-        DurationCurveFacet copy = new DurationCurveFacet();
-        copy.set(this);
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/EmptyFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.artifactdatabase.state.DefaultFacet;
-import de.intevation.artifactdatabase.state.Facet;
-
-public class EmptyFacet
-extends      DefaultFacet
-{
-    /** Trivial constructor. */
-    public EmptyFacet() {
-        super(0, "empty.facet", "empty.facet");
-    }
-
-    @Override
-    public Object getData(Artifact artifact, CallContext context) {
-        return null;
-    }
-
-
-    /**
-     * Return a deep copy.
-     */
-    @Override
-    public Facet deepCopy() {
-        EmptyFacet copy = new EmptyFacet();
-        copy.set(this);
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/FacetTypes.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,380 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-/** 'Types' of facets. */
-public interface FacetTypes {
-
-    /** Static helper class to determine if a Facet name is of a generalized
-     * 'type'. */
-    public static class IS {
-        public static boolean WQ_KM(String type) {
-           return type.equals(DISCHARGE_LONGITUDINAL_W)
-               || type.equals(LONGITUDINAL_W);
-        }
-        public static boolean W_KM(String type) {
-            return type.equals(STATIC_WKMS)
-               || type.equals(HEIGHTMARKS_POINTS)
-               || WQ_KM(type);
-        }
-        public static boolean AREA(String type) {
-            return type.equals(AREA)
-                || type.equals(CROSS_SECTION_AREA)
-                || type.equals(LONGITUDINAL_SECTION_AREA);
-        }
-        public static boolean W(String type) {
-            return type.equals(LONGITUDINAL_W)
-                || type.equals(DISCHARGE_LONGITUDINAL_W)
-                || type.equals(DURATION_W)
-                || type.equals(STATIC_WKMS)
-                || type.equals(STATIC_WQKMS_W);
-        }
-        public static boolean Q(String type) {
-            return type.equals(LONGITUDINAL_Q)
-                || type.equals(DISCHARGE_LONGITUDINAL_Q)
-                || type.equals(DURATION_Q)
-                || type.equals(STATIC_WQKMS_Q);
-        }
-        public static boolean V(String type) {
-            return type.equals(FLOW_VELOCITY_MAINCHANNEL)
-                || type.equals(FLOW_VELOCITY_TOTALCHANNEL);
-        }
-        public static boolean T(String type) {
-            return type.equals(FLOW_VELOCITY_TAU);
-        }
-        public static boolean H(String type) {
-            return type.equals(MIDDLE_BED_HEIGHT_SINGLE)
-                || type.equals(MIDDLE_BED_HEIGHT_EPOCH);
-        }
-        public static boolean MANUALPOINTS(String type) {
-            return type.endsWith("manualpoints");
-        }
-        public static boolean MANUALLINE(String type) {
-            return type.endsWith("manualline");
-        }
-        public static boolean SQ_CURVE(String type) {
-            if (type.equals(SQ_A_CURVE)
-                || type.equals(SQ_B_CURVE)
-                || type.equals(SQ_C_CURVE)
-                || type.equals(SQ_D_CURVE)
-                || type.equals(SQ_E_CURVE)
-                || type.equals(SQ_F_CURVE)
-                || type.equals(SQ_A_OUTLIER_CURVE)
-                || type.equals(SQ_B_OUTLIER_CURVE)
-                || type.equals(SQ_C_OUTLIER_CURVE)
-                || type.equals(SQ_D_OUTLIER_CURVE)
-                || type.equals(SQ_E_OUTLIER_CURVE)
-                || type.equals(SQ_F_OUTLIER_CURVE)
-                )
-            {
-                return true;
-            }
-
-            return false;
-        }
-        public static boolean SQ_MEASUREMENT(String type) {
-            if (type.equals(SQ_A_MEASUREMENT)
-                || type.equals(SQ_B_MEASUREMENT)
-                || type.equals(SQ_C_MEASUREMENT)
-                || type.equals(SQ_D_MEASUREMENT)
-                || type.equals(SQ_E_MEASUREMENT)
-                || type.equals(SQ_F_MEASUREMENT)
-                || type.equals(SQ_A_OUTLIER_MEASUREMENT)
-                || type.equals(SQ_B_OUTLIER_MEASUREMENT)
-                || type.equals(SQ_C_OUTLIER_MEASUREMENT)
-                || type.equals(SQ_D_OUTLIER_MEASUREMENT)
-                || type.equals(SQ_E_OUTLIER_MEASUREMENT)
-                || type.equals(SQ_F_OUTLIER_MEASUREMENT)
-                )
-            {
-                return true;
-            }
-
-            return false;
-        }
-        public static boolean SQ_OUTLIER(String type) {
-            if (type.equals(SQ_A_OUTLIER)
-                || type.equals(SQ_B_OUTLIER)
-                || type.equals(SQ_C_OUTLIER)
-                || type.equals(SQ_D_OUTLIER)
-                || type.equals(SQ_E_OUTLIER)
-                || type.equals(SQ_F_OUTLIER))
-            {
-                return true;
-            }
-
-            return false;
-        }
-        public static boolean SEDIMENT_LOAD(String type) {
-            return type.startsWith("sedimentload");
-        }
-        public static boolean SEDIMENT_LOAD_NO_FLOAT(String type) {
-            return type.startsWith("sedimentload") && !type.contains("susp");
-        }
-    };
-
-    /** Available diagram/chart-types.  This enum is evaluated at certain
-     * places to iterate over chart-types to e.g. dynamically generate
-     * Facet-names (ManualPoints for example). */
-    public enum ChartType {
-        FD("fix_derivate_curve"),
-        LS("longitudinal_section"),
-        CS("cross_section"),
-        DLS("discharge_longitudinal_section"),
-        CDC("computed_discharge_curve"),
-        DUC("duration_curve"),
-        DIC("discharge_curve"),
-        RC("reference_curve"),
-        RCN("reference_curve_normalized"),
-        WD("wdifferences"),
-        FWQC("fix_wq_curve"),
-        FDWC("fix_deltawt_curve"),
-        FLSC("fix_longitudinal_section_curve"),
-        FDC("fix_derivate_curve"),
-        EWQ("extreme_wq_curve"),
-        BHM("bedheight_middle"),
-        BLS("bed_longitudinal_section"),
-        SLS("sedimentload_ls"),
-        FV("flow_velocity"),
-        SQA("sq_relation_a"),
-        SQB("sq_relation_b"),
-        W_D("w_differences"),
-        SQC("sq_relation_c"),
-        SQD("sq_relation_d"),
-        SQE("sq_relation_e"),
-        SQF("sq_relation_f"),
-        HD("historical_discharge"),
-        HDWQ("historical_discharge_wq");
-
-        private String chartTypeString;
-
-        ChartType(String description) {
-            this.chartTypeString = description;
-        }
-
-        @Override
-        public String toString() {
-            return chartTypeString;
-        }
-    }
-
-    String AREA                        = "area";
-    String CROSS_SECTION_AREA          = "cross_section.area";
-    String LONGITUDINAL_SECTION_AREA   = "longitudinal_section.area";
-
-    String FLOODMAP_WSPLGEN            = "floodmap.wsplgen";
-    String FLOODMAP_BARRIERS           = "floodmap.barriers";
-    String FLOODMAP_USERSHAPE          = "floodmap.usershape";
-    String FLOODMAP_RIVERAXIS          = "floodmap.riveraxis";
-    @Deprecated
-    String FLOODMAP_WMSBACKGROUND      = "floodmap.wmsbackground";
-    String FLOODMAP_KMS                = "floodmap.kms";
-    String FLOODMAP_QPS                = "floodmap.qps";
-    String FLOODMAP_HWS_LINES          = "floodmap.hws_lines";
-    String FLOODMAP_HWS_POINTS         = "floodmap.hws_points";
-    String FLOODMAP_HYDR_BOUNDARY      = "floodmap.hydr_boundaries";
-    String FLOODMAP_HYDR_BOUNDARY_POLY = "floodmap.hydr_boundaries_poly";
-    String FLOODMAP_CATCHMENT          = "floodmap.catchment";
-    String FLOODMAP_FLOODPLAIN         = "floodmap.floodplain";
-    String FLOODMAP_LINES              = "floodmap.lines";
-    String FLOODMAP_BUILDINGS          = "floodmap.buildings";
-    String FLOODMAP_FIXPOINTS          = "floodmap.fixpoints";
-    String FLOODMAP_FLOODMARKS         = "floodmap.floodmarks";
-    String FLOODMAP_FLOODMAPS          = "floodmap.floodmaps";
-    String FLOODMAP_GAUGE_LOCATION     = "floodmap.gauge_location";
-    String FLOODMAP_EXTERNAL_WMS       = "floodmap.externalwms";
-    String FLOODMAP_JETTIES            = "floodmap.jetties";
-
-    String DISCHARGE_LONGITUDINAL_W = "discharge_longitudinal_section.w";
-    String DISCHARGE_LONGITUDINAL_Q = "discharge_longitudinal_section.q";
-    String DISCHARGE_LONGITUDINAL_Q_INFOLD = "discharge_longitudinal_section.q.infolding";
-    String DISCHARGE_LONGITUDINAL_Q_INFOLD_CUT = "discharge_longitudinal_section.q.cutting";
-    String DISCHARGE_LONGITUDINAL_C = "discharge_longitudinal_section.c";
-
-    String LONGITUDINAL_W = "longitudinal_section.w";
-    String LONGITUDINAL_Q = "longitudinal_section.q";
-    String LONGITUDINAL_ANNOTATION   = "longitudinal_section.annotations";
-    String LONGITUDINAL_MANUALPOINTS = "longitudinal_section.manualpoints";
-
-    String W_DIFFERENCES = "w_differences";
-
-    String COMPUTED_DISCHARGE_Q = "computed_discharge_curve.q";
-    String COMPUTED_DISCHARGE_MAINVALUES_Q = "computed_discharge_curve.mainvalues.q";
-    String COMPUTED_DISCHARGE_MAINVALUES_W = "computed_discharge_curve.mainvalues.w";
-
-    String MAINVALUES_Q = "mainvalues.q";
-    String MAINVALUES_W = "mainvalues.w";
-
-    String CROSS_SECTION = "cross_section";
-    String CROSS_SECTION_WATER_LINE = "cross_section_water_line";
-
-    String HYK = "hyk";
-
-    String DISCHARGE_CURVE = "discharge_curve.curve";
-    String GAUGE_DISCHARGE_CURVE = "gauge_discharge_curve";
-    String GAUGE_DISCHARGE_CURVE_AT_EXPORT = "gauge_discharge_curve_at_export";
-
-    String DURATION_W = "duration_curve.w";
-    String DURATION_Q = "duration_curve.q";
-    String DURATION_MAINVALUES_Q = "duration_curve.mainvalues.q";
-
-    String MANUALPOINTS = "manualpoints";
-    String MANUALLINE = "manualline";
-
-    String QSECTOR        = "qsectors";
-
-    String STATIC_WQ      = "other.wq";
-    String STATIC_WQ_ANNOTATIONS = "other.wq.annotations";
-    String STATIC_WKMS    = "other.wkms";
-    String STATIC_WKMS_MARKS = "other.wkms.marks";
-    String STATIC_WQKMS   = "other.wqkms";
-    String STATIC_WQKMS_W = "other.wqkms.w";
-    String STATIC_WQKMS_Q = "other.wqkms.q";
-    String STATIC_WKMS_INTERPOL = "other.wkms.interpol";
-
-    String HEIGHTMARKS_POINTS = "heightmarks_points";
-
-    String CSV = "csv";
-    String WST = "wst";
-    String AT  = "at";
-    String PDF = "pdf";
-
-    String REPORT = "report";
-
-    String HISTORICAL_DISCHARGE_Q      = "historical_discharge.historicalq";
-    String HISTORICAL_DISCHARGE_Q_DIFF = "historical_discharge.historicalq.diff";
-    String HISTORICAL_DISCHARGE_W      = "historical_discharge.historicalw";
-    String HISTORICAL_DISCHARGE_W_DIFF = "historical_discharge.historicalw.diff";
-    String HISTORICAL_DISCHARGE_WQ_Q   = "historical_discharge.wq.q";
-    String HISTORICAL_DISCHARGE_WQ_W   = "historical_discharge.wq.w";
-    String HISTORICAL_DISCHARGE_WQ_CURVE = "historical_discharge.wq.curve";
-    String HISTORICAL_DISCHARGE_MAINVALUES_Q = "historical_discharge.mainvalues.q";
-    String HISTORICAL_DISCHARGE_MAINVALUES_W = "historical_discharge.mainvalues.w";
-
-    String REFERENCE_CURVE = "reference_curve";
-    String REFERENCE_CURVE_NORMALIZED = "reference_curve_normalized";
-
-    String FLOW_VELOCITY_MAINCHANNEL           = "flow_velocity.mainchannel";
-    String FLOW_VELOCITY_TOTALCHANNEL          = "flow_velocity.totalchannel";
-    String FLOW_VELOCITY_TAU                   = "flow_velocity.tau";
-    String FLOW_VELOCITY_MAINCHANNEL_FILTERED  = "flow_velocity.mainchannel.filtered";
-    String FLOW_VELOCITY_TOTALCHANNEL_FILTERED = "flow_velocity.totalchannel.filtered";
-    String FLOW_VELOCITY_TAU_FILTERED          = "flow_velocity.tau.filtered";
-    String FLOW_VELOCITY_ANNOTATION            = "flow_velocity.annotation";
-    String FLOW_VELOCITY_MEASUREMENT           = "flow_velocity.measurement";
-    String FLOW_VELOCITY_DISCHARGE             = "flow_velocity.discharge";
-
-    String MIDDLE_BED_HEIGHT_SINGLE     = "bedheight_middle.single";
-    String MIDDLE_BED_HEIGHT_EPOCH      = "bedheight_middle.epoch";
-    String MIDDLE_BED_HEIGHT_ANNOTATION = "bedheight_middle.annotation";
-
-    String BED_QUALITY_POROSITY_TOPLAYER         = "bed_longitudinal_section.porosity_toplayer";
-    String BED_QUALITY_POROSITY_SUBLAYER         = "bed_longitudinal_section.porosity_sublayer";
-    String BED_QUALITY_BED_DIAMETER_TOPLAYER     = "bed_longitudinal_section.bed_diameter_toplayer";
-    String BED_QUALITY_BED_DIAMETER_SUBLAYER     = "bed_longitudinal_section.bed_diameter_sublayer";
-    String BED_QUALITY_SEDIMENT_DENSITY_TOPLAYER = "bed_longitudinal_section.sediment_density_toplayer";
-    String BED_QUALITY_SEDIMENT_DENSITY_SUBLAYER = "bed_longitudinal_section.sediment_density_sublayer";
-    String BED_QUALITY_BEDLOAD_DIAMETER          = "bed_longitudinal_section.bedload_diameter";
-
-    String BED_DIFFERENCE_YEAR          = "bedheight_difference.year";
-    String BED_DIFFERENCE_YEAR_FILTERED = "bedheight_difference.year.filtered";
-    String BED_DIFFERENCE_HEIGHT_YEAR   = "bedheight_difference.height_year";
-    String BED_DIFFERENCE_HEIGHT_YEAR_FILTERED   = "bedheight_difference.height_year.filtered";
-    String BED_DIFFERENCE_EPOCH         = "bedheight_difference.epoch";
-    String BED_DIFFERENCE_EPOCH_FILTERED = "bedheight_difference.epoch.filtered";
-    String BED_DIFFERENCE_MORPH_WIDTH   = "bedheight_difference.morph_width";
-    String BED_DIFFERENCE_YEAR_HEIGHT1  = "bedheight_difference.year.height1";
-    String BED_DIFFERENCE_YEAR_HEIGHT2  = "bedheight_difference.year.height2";
-    String BED_DIFFERENCE_YEAR_HEIGHT1_FILTERED = "bedheight_difference.year.height1.filtered";
-    String BED_DIFFERENCE_YEAR_HEIGHT2_FILTERED = "bedheight_difference.year.height2.filtered";
-    String BED_DIFFERENCE_EPOCH_HEIGHT1 = "bedheight_difference.epoch.height1";
-    String BED_DIFFERENCE_EPOCH_HEIGHT2 = "bedheight_difference.epoch.height2";
-    String BED_DIFFERENCE_EPOCH_HEIGHT1_FILTERED = "bedheight_difference.epoch.height1.filtered";
-    String BED_DIFFERENCE_EPOCH_HEIGHT2_FILTERED = "bedheight_difference.epoch.height2.filtered";
-    String MORPHOLOGIC_WIDTH = "morph-width";
-
-    String SEDIMENT_LOAD_COARSE        = "sedimentload.coarse";
-    String SEDIMENT_LOAD_SAND          = "sedimentload.sand";
-    String SEDIMENT_LOAD_FINEMIDDLE    = "sedimentload.finemiddle";
-    String SEDIMENT_LOAD_SUSP_SAND     = "sedimentload.susp_sand";
-    String SEDIMENT_LOAD_SUSP_SAND_BED = "sedimentload.susp_sand_bed";
-    String SEDIMENT_LOAD_SUSP_SEDIMENT = "sedimentload.susp_sediment";
-    String SEDIMENT_LOAD_TOTAL         = "sedimentload.total";
-    String SEDIMENT_LOAD_TOTAL_LOAD    = "sedimentload.total_load";
-
-    String SQ_OVERVIEW       = "sq_overview";
-
-    String SQ_A_CURVE       = "sq_a_curve";
-    String SQ_A_MEASUREMENT = "sq_a_measurement";
-    String SQ_A_OUTLIER     = "sq_a_outlier";
-    String SQ_A_OUTLIER_CURVE = "sq_a_outlier_curve";
-    String SQ_A_OUTLIER_MEASUREMENT = "sq_a_outlier_measurement";
-
-    String SQ_B_CURVE       = "sq_b_curve";
-    String SQ_B_MEASUREMENT = "sq_b_measurement";
-    String SQ_B_OUTLIER     = "sq_b_outlier";
-    String SQ_B_OUTLIER_CURVE  = "sq_b_outlier_curve";
-    String SQ_B_OUTLIER_MEASUREMENT  = "sq_b_outlier_measurement";
-
-    String SQ_C_CURVE       = "sq_c_curve";
-    String SQ_C_MEASUREMENT = "sq_c_measurement";
-    String SQ_C_OUTLIER     = "sq_c_outlier";
-    String SQ_C_OUTLIER_CURVE = "sq_c_outlier_curve";
-    String SQ_C_OUTLIER_MEASUREMENT = "sq_c_outlier_measurement";
-
-    String SQ_D_CURVE       = "sq_d_curve";
-    String SQ_D_MEASUREMENT = "sq_d_measurement";
-    String SQ_D_OUTLIER     = "sq_d_outlier";
-    String SQ_D_OUTLIER_CURVE = "sq_d_outlier_curve";
-    String SQ_D_OUTLIER_MEASUREMENT = "sq_d_outlier_measurement";
-
-    String SQ_E_CURVE       = "sq_e_curve";
-    String SQ_E_MEASUREMENT = "sq_e_measurement";
-    String SQ_E_OUTLIER     = "sq_e_outlier";
-    String SQ_E_OUTLIER_CURVE = "sq_e_outlier_curve";
-    String SQ_E_OUTLIER_MEASUREMENT = "sq_e_outlier_curve_measurement";
-
-    String SQ_F_CURVE       = "sq_f_curve";
-    String SQ_F_MEASUREMENT = "sq_f_measurement";
-    String SQ_F_OUTLIER     = "sq_f_outlier";
-    String SQ_F_OUTLIER_CURVE = "sq_f_outlier_curve";
-    String SQ_F_OUTLIER_MEASUREMENT = "sq_f_outlier_measurement";
-
-    String RELATIVE_POINT = "relativepoint";
-
-    String FIX_ANALYSIS_EVENTS_DWT = "fix_analysis_events_dwt";
-    String FIX_ANALYSIS_EVENTS_LS = "fix_analysis_events_ls";
-    String FIX_ANALYSIS_EVENTS_WQ = "fix_analysis_events_wq";
-
-    String FIX_EVENTS = "fix_events_wqkms";
-
-    String FIX_REFERENCE_EVENTS_DWT = "fix_reference_events_dwt";
-    String FIX_REFERENCE_EVENTS_LS = "fix_reference_events_ls";
-    String FIX_REFERENCE_EVENTS_WQ = "fix_reference_events_wq";
-    String FIX_REFERENCE_PERIOD_DWT = "fix_reference_period_dwt";
-
-    // Note that AVERAGE_DWT will get a postfix (e.g. ..._dwt_1)
-    String FIX_SECTOR_AVERAGE_DWT = "fix_sector_average_dwt";
-    String FIX_SECTOR_AVERAGE_LS = "fix_sector_average_ls";
-    String FIX_SECTOR_AVERAGE_WQ = "fix_sector_average_wq";
-    String FIX_SECTOR_AVERAGE_LS_DEVIATION = "fix_sector_average_ls_deviation";
-
-    String FIX_WQ_CURVE = "fix_wq_curve";
-    String FIX_OUTLIER = "fix_outlier";
-
-    String FIX_ANALYSIS_PERIODS_DWT = "fix_analysis_periods_dwt";
-    String FIX_ANALYSIS_PERIODS_LS = "fix_analysis_periods_ls";
-    String FIX_ANALYSIS_PERIODS_WQ = "fix_analysis_periods_wq";
-
-    String FIX_DERIVATE_CURVE = "fix_derivate_curve";
-
-    String FIX_DEVIATION_DWT = "fix_deviation_dwt";
-    String FIX_DEVIATION_LS = "fix_deviation_ls";
-
-    String FIX_PARAMETERS = "fix_parameters";
-
-    String STATIC_BEDHEIGHT = "static_bedheight";
-
-    String EXTREME_WQ_CURVE = "extreme_wq_curve";
-
-    String EXTREME_WQ_CURVE_BASE = "extreme_wq_curve_base";
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/FastCrossSectionChunk.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,83 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.util.List;
-import java.util.Collections;
-
-import java.io.Serializable;
-
-import de.intevation.flys.model.CrossSection;
-
-import de.intevation.flys.model.FastCrossSectionLine;
-
-import org.apache.log4j.Logger;
-
-public class FastCrossSectionChunk
-implements   Serializable
-{
-    private static Logger log = Logger.getLogger(FastCrossSectionChunk.class);
-
-    public static final String PREFIX = "FCSC:";
-    public static final double KM_RANGE = 1.0;
-
-    protected double startKm;
-    protected int    crossSectionId;
-
-    protected List<FastCrossSectionLine> crossSectionLines;
-
-    public FastCrossSectionChunk() {
-    }
-
-    public FastCrossSectionChunk(CrossSection cs, double km) {
-
-        crossSectionId = cs.getId();
-        startKm = Math.floor(km);
-        double stopKm = startKm + KM_RANGE;
-
-        long startTime = System.currentTimeMillis();
-
-        crossSectionLines = cs.getFastLines(startKm, stopKm);
-
-        long stopTime = System.currentTimeMillis();
-
-        if (log.isDebugEnabled()) {
-            log.debug("Fetching cross section lines took " +
-                (float)(stopTime-startTime)/1000f + " secs.");
-        }
-    }
-
-    public FastCrossSectionLine getCrossSectionLine(double km) {
-        FastCrossSectionLine key = new FastCrossSectionLine(km);
-        int pos = Collections.binarySearch(
-            crossSectionLines, key, FastCrossSectionLine.KM_CMP);
-        return pos < 0 ? null : crossSectionLines.get(pos);
-    }
-
-    public static String createHashKey(CrossSection cs, double km) {
-        return PREFIX + cs.getId() + ":" + (int)Math.floor(km);
-    }
-
-    public String getHashKey() {
-        return PREFIX + crossSectionId + ":" + (int)Math.floor(startKm);
-    }
-
-    public double getStartKm() {
-        return startKm;
-    }
-
-    public void setStartKm(double startKm) {
-        this.startKm = startKm;
-    }
-
-    public double getStopKm() {
-        return startKm + KM_RANGE;
-    }
-
-    public int getCrossSectionId() {
-        return crossSectionId;
-    }
-
-    public void setCrossSectionId(int crossSectionId) {
-        this.crossSectionId = crossSectionId;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/FastCrossSectionLineFactory.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import de.intevation.flys.artifacts.cache.CacheFactory;
-
-import de.intevation.flys.model.CrossSection;
-
-import de.intevation.flys.model.FastCrossSectionLine;
-
-import net.sf.ehcache.Cache;
-import net.sf.ehcache.Element;
-
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-public class FastCrossSectionLineFactory
-{
-    private static Logger log =
-        Logger.getLogger(FastCrossSectionLineFactory.class);
-
-    public static final String CACHE_NAME = "fast-cross-section-lines";
-
-    private FastCrossSectionLineFactory() {
-    }
-
-    public static FastCrossSectionLine getCrossSectionLine(
-        CrossSection cs,
-        double       km
-    ) {
-        Cache cache = CacheFactory.getCache(CACHE_NAME);
-
-        boolean debug = log.isDebugEnabled();
-
-        if (cache == null) {
-            if (debug) {
-                log.debug("No cross section chunk cache configured.");
-            }
-            List<FastCrossSectionLine> lines = cs.getFastLines(km, km);
-            return lines.isEmpty() ? null : lines.get(0);
-        }
-
-        String cacheKey = FastCrossSectionChunk.createHashKey(cs, km);
-
-        Element element = cache.get(cacheKey);
-
-        FastCrossSectionChunk fcsc;
-
-        if (element != null) {
-            if (debug) {
-                log.debug("Found cross section chunk in cache id: " +
-                    cs.getId() + " km: " + km);
-            }
-
-            fcsc = (FastCrossSectionChunk)element.getValue();
-        }
-        else {
-            if (debug) {
-                log.debug("Not found cross section chunk in cache id: " +
-                    cs.getId() + " km: " + km + " -> loading");
-            }
-            fcsc = new FastCrossSectionChunk(cs, km);
-            element = new Element(cacheKey, fcsc);
-            cache.put(element);
-        }
-
-        return fcsc.getCrossSectionLine(km);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/FixingsColumn.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,62 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import de.intevation.flys.artifacts.math.Linear;
-
-import java.util.Arrays;
-
-import java.io.Serializable;
-
-public class FixingsColumn
-implements   Serializable
-{
-    protected double [] kms;
-    protected double [] ws;
-
-    protected QRangeTree qs;
-
-    public FixingsColumn() {
-    }
-
-    public FixingsColumn(
-        double []  kms,
-        double []  ws,
-        QRangeTree qs
-    ) {
-        this.kms = kms;
-        this.ws  = ws;
-        this.qs  = qs;
-    }
-
-    public boolean getW(double km, double [] w) {
-        return getW(km, w, 0);
-    }
-
-    public boolean getW(double km, double [] w, int index) {
-
-        if (kms.length == 0 || km < kms[0] || km > kms[kms.length-1]) {
-            w[index] = Double.NaN;
-            return true;
-        }
-
-        int idx = Arrays.binarySearch(kms, km);
-
-        if (idx >= 0) {
-            w[index] = ws[idx];
-            return true;
-        }
-
-        idx = -idx - 1;
-
-        w[index] = Linear.linear(km, kms[idx-1], kms[idx], ws[idx-1], ws[idx]);
-        return false;
-    }
-
-    public double getQ(double km) {
-        return qs.findQ(km);
-    }
-
-    public QRangeTree getQRanges() {
-        return qs;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/FixingsColumnFactory.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,131 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import de.intevation.flys.artifacts.model.FixingsOverview.Fixing;
-
-import de.intevation.flys.artifacts.cache.CacheFactory;
-
-import de.intevation.flys.backend.SessionHolder;
-
-import java.util.List;
-
-import net.sf.ehcache.Cache;
-import net.sf.ehcache.Element;
-
-import org.hibernate.Session;
-import org.hibernate.SQLQuery;
-
-import org.hibernate.type.StandardBasicTypes;
-
-import org.apache.log4j.Logger;
-
-public class FixingsColumnFactory
-{
-    private static Logger log = Logger.getLogger(FixingsColumnFactory.class);
-
-    public static final String CACHE_NAME = "fixings-columns";
-
-    public static final String SQL_COLUMN_WS =
-        "SELECT wcv.position AS km, wcv.w AS w " +
-        "FROM wst_column_values wcv " +
-        "WHERE wst_column_id = :column_id " +
-        "ORDER by wcv.position";
-
-    public static final String SQL_COLUMN_QS =
-        "SELECT wqr.q AS q, r.a AS a, r.b AS b " +
-        "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 FixingsColumnFactory INSTANCE =
-        new FixingsColumnFactory();
-
-    private FixingsColumnFactory() {
-    }
-
-    public static FixingsColumnFactory getInstance() {
-        return INSTANCE;
-    }
-
-    public FixingsColumn getColumnData(Fixing.Column column) {
-
-        boolean debug = log.isDebugEnabled();
-
-        if (debug) {
-            log.debug("FixingsColumnFactory.getColumnData");
-        }
-
-        Cache cache = CacheFactory.getCache(CACHE_NAME);
-
-        if (cache == null) {
-            if (debug) {
-                log.debug("Cache unconfigured.");
-            }
-            return getUncached(column);
-        }
-
-        Integer cacheKey = Integer.valueOf(column.getId());
-        Element element  = cache.get(cacheKey);
-
-        if (element != null) {
-            if (debug) {
-                log.debug("Column " + cacheKey + " found in cache.");
-            }
-            return (FixingsColumn)element.getValue();
-        }
-        else {
-            FixingsColumn result = getUncached(column);
-            if (result != null) {
-                if (debug) {
-                    log.debug("Store column " + cacheKey + " into cache.");
-                }
-                cache.put(new Element(cacheKey, result));
-            }
-            return result;
-        }
-    }
-
-    protected FixingsColumn getUncached(Fixing.Column column) {
-        Session session = SessionHolder.HOLDER.get();
-
-        SQLQuery sqlQuery = session.createSQLQuery(SQL_COLUMN_WS)
-            .addScalar("km", StandardBasicTypes.DOUBLE)
-            .addScalar("w",  StandardBasicTypes.DOUBLE);
-
-        sqlQuery.setInteger("column_id", column.getId());
-
-        List<Object []> results = sqlQuery.list();
-
-        if (results.isEmpty()) {
-            return null;
-        }
-
-        double [] kms = new double[results.size()];
-        double [] ws  = new double[kms.length];
-
-        for (int i = 0; i < kms.length; ++i) {
-            Object [] row = results.get(i);
-            kms[i] = ((Double)row[0]).doubleValue();
-            ws [i] = ((Double)row[1]).doubleValue();
-        }
-
-        sqlQuery = session.createSQLQuery(SQL_COLUMN_QS)
-            .addScalar("q", StandardBasicTypes.DOUBLE)
-            .addScalar("a", StandardBasicTypes.DOUBLE)
-            .addScalar("b", StandardBasicTypes.DOUBLE);
-
-        sqlQuery.setInteger("column_id", column.getId());
-
-        results = sqlQuery.list();
-
-        if (results.isEmpty()) {
-            return null;
-        }
-
-        QRangeTree qs = new QRangeTree(
-            results, QRangeTree.WITHOUT_COLUMN, 0, results.size());
-
-        return new FixingsColumn(kms, ws, qs);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/FixingsFilterBuilder.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,247 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import de.intevation.flys.artifacts.model.FixingsOverview.AndFilter;
-import de.intevation.flys.artifacts.model.FixingsOverview.DateFilter;
-import de.intevation.flys.artifacts.model.FixingsOverview.DateRangeFilter;
-
-import de.intevation.flys.artifacts.model.FixingsOverview.Fixing.Filter;
-
-import de.intevation.flys.artifacts.model.FixingsOverview.IdFilter;
-import de.intevation.flys.artifacts.model.FixingsOverview.IdsFilter;
-import de.intevation.flys.artifacts.model.FixingsOverview.KmFilter;
-import de.intevation.flys.artifacts.model.FixingsOverview.NotFilter;
-import de.intevation.flys.artifacts.model.FixingsOverview.OrFilter;
-import de.intevation.flys.artifacts.model.FixingsOverview.SectorFilter;
-import de.intevation.flys.artifacts.model.FixingsOverview.SectorRangeFilter;
-
-import java.text.ParsePosition;
-import java.text.SimpleDateFormat;
-
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-
-public class FixingsFilterBuilder
-{
-    private static Logger log = Logger.getLogger(FixingsFilterBuilder.class);
-
-    protected Filter   filter;
-    protected Range    range;
-
-    protected Document document;
-
-    public FixingsFilterBuilder() {
-    }
-
-    public FixingsFilterBuilder(Document document) {
-        this.document = document;
-    }
-
-    public Filter getFilter() {
-        if (filter == null) {
-            filter = buildFilter();
-        }
-        return filter;
-    }
-
-    public Range getRange() {
-        if (range == null) {
-            range = buildRange();
-        }
-        return range;
-    }
-
-    public Document getDocument() {
-        return document;
-    }
-
-    protected Range buildRange() {
-
-        NodeList ranges = document.getElementsByTagName("range");
-
-        if (ranges.getLength() < 1) {
-            return FixingsOverview.FULL_EXTENT;
-        }
-
-        Element range = (Element)ranges.item(0);
-
-        String from = range.getAttribute("from").trim();
-        String to   = range.getAttribute("to"  ).trim();
-
-        double start = -Double.MAX_VALUE;
-        double end   =  Double.MAX_VALUE;
-
-        if (from.length() > 0) {
-            try {
-                start = Double.parseDouble(from);
-            }
-            catch (NumberFormatException nfe) {
-                log.warn("Invalid from value: " + from);
-            }
-        }
-
-        if (to.length() > 0) {
-            try {
-                end = Double.parseDouble(to);
-            }
-            catch (NumberFormatException nfe) {
-                log.warn("Invalid to value: " + to);
-            }
-        }
-
-        if (start > end) {
-            double t = start;
-            start = end;
-            end = t;
-        }
-
-        return new Range(start, end);
-    }
-
-    protected Filter buildFilter() {
-        NodeList filters = document.getElementsByTagName("filter");
-
-        return filters.getLength() < 1
-            ? FixingsOverview.ACCEPT
-            : buildFilter((Element)filters.item(0));
-    }
-
-    protected static Filter buildFilter(Element root) {
-        List<Filter> filters = buildRecursiveFilter(root);
-        switch (filters.size()) {
-            case  0: return FixingsOverview.ACCEPT;
-            case  1: return filters.get(0);
-            default: return new AndFilter(filters);
-        }
-    }
-
-    protected static final Date parseDate(String text) {
-        SimpleDateFormat format =
-            new SimpleDateFormat(FixingsOverview.DATE_FORMAT);
-        return format.parse(text, new ParsePosition(0));
-    }
-
-    protected static List<Filter> buildRecursiveFilter(Element root) {
-        List<Filter> filters = new ArrayList<Filter>();
-
-        NodeList children = root.getChildNodes();
-
-        for (int i = 0, N = children.getLength(); i < N; ++i) {
-            Node child = children.item(i);
-            if (child.getNodeType() != Node.ELEMENT_NODE) {
-                continue;
-            }
-
-            Element element = (Element)child;
-            String name = element.getLocalName();
-
-            if ("and".equals(name)) {
-                filters.add(new AndFilter(buildRecursiveFilter(element)));
-            }
-            else if ("or".equals(name)) {
-                filters.add(new OrFilter(buildRecursiveFilter(element)));
-            }
-            else if ("not".equals(name)) {
-                List<Filter> childrenFilters = buildRecursiveFilter(element);
-                if (!childrenFilters.isEmpty()) {
-                    filters.add(new NotFilter(childrenFilters.get(0)));
-                }
-            }
-            else if ("column".equals(name)) {
-                String cid = element.getAttribute("cid").trim();
-                if (cid.length() > 0) {
-                    try {
-                        filters.add(new IdFilter(Integer.parseInt(cid)));
-                    }
-                    catch (NumberFormatException nfe) {
-                        log.warn(nfe);
-                    }
-                }
-            }
-            else if ("columns".equals(name)) {
-                String cidsS = element.getAttribute("cids").trim();
-                String [] parts = cidsS.split("\\s+");
-                List<Integer> ids = new ArrayList<Integer>();
-                for (String part: parts) {
-                    try {
-                        ids.add(Integer.valueOf(part));
-                    }
-                    catch (NumberFormatException nfe) {
-                        log.warn(nfe);
-                    }
-                }
-                int [] cids = new int[ids.size()];
-                for (int j = 0; j < cids.length; ++j) {
-                    cids[j] = ids.get(j);
-                }
-                filters.add(new IdsFilter(cids));
-            }
-            else if ("date".equals(name)) {
-                String when = element.getAttribute("when").trim();
-                if (when.length() > 0) {
-                    Date date = parseDate(when);
-                    if (date != null) {
-                        filters.add(new DateFilter(date));
-                    }
-                }
-            }
-            else if ("date-range".equals(name)) {
-                String from = element.getAttribute("from").trim();
-                String to   = element.getAttribute("to"  ).trim();
-                if (from.length() > 0 && to.length() > 0) {
-                    Date start = parseDate(from);
-                    Date end   = parseDate(to);
-                    if (start != null && end != null) {
-                        filters.add(new DateRangeFilter(start, end));
-                    }
-                }
-            }
-            else if ("sector-range".equals(name)) {
-                String from = element.getAttribute("from").trim();
-                String to   = element.getAttribute("to"  ).trim();
-                if (from.length() > 0 && to.length() > 0) {
-                    try {
-                        filters.add(new SectorRangeFilter(
-                            Integer.parseInt(from),
-                            Integer.parseInt(to)));
-                    }
-                    catch (NumberFormatException nfe) {
-                        log.warn(nfe);
-                    }
-                }
-            }
-            else if ("sector".equals(name)) {
-                String value = element.getAttribute("value").trim();
-                if (value.length() > 0) {
-                    try {
-                        filters.add(new SectorFilter(Integer.parseInt(value)));
-                    }
-                    catch (NumberFormatException nfe) {
-                        log.warn(nfe);
-                    }
-                }
-            }
-            else if ("position".equals(name)) {
-                String km = element.getAttribute("km").trim();
-                if (km.length() > 0) {
-                    try {
-                        filters.add(new KmFilter(Double.parseDouble(km)));
-                    }
-                    catch (NumberFormatException nfe) {
-                        log.warn(nfe);
-                    }
-                }
-            }
-        }
-
-        return filters;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/FixingsOverview.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,808 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.io.Serializable;
-
-import java.text.SimpleDateFormat;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-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.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-
-/** Generate Fixings Table chart. */
-public class FixingsOverview
-implements   Serializable
-{
-    private static Logger log = Logger.getLogger(FixingsOverview.class);
-
-    public static final double EPSILON = 1e-2;
-
-    public static final String DATE_FORMAT = "dd.MM.yyyy";
-
-    public static final String SQL_RIVER_ID =
-        "SELECT" +
-        "    id AS river_id," +
-        "    km_up " +
-        "FROM rivers " +
-        "WHERE" +
-        "    name = :name";
-
-    /** All kind-2 wsts from given river. */
-    public static final String SQL_FIXINGS =
-        "SELECT" +
-        "    id AS wst_id," +
-        "    description " +
-        "FROM wsts " +
-        "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_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_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 class QRange extends Range {
-
-        protected double q;
-
-        public QRange() {
-        }
-
-        public QRange(double start, double end, double q) {
-            super(start, end);
-            this.q = q;
-        }
-    } // class QRange
-
-    public static class SectorRange extends Range {
-
-        protected int sector;
-
-        public SectorRange() {
-        }
-
-        public SectorRange(SectorRange other) {
-            start  = other.start;
-            end    = other.end;
-            sector = other.sector;
-        }
-
-        public SectorRange(Range range) {
-            super(range);
-        }
-
-        public SectorRange(double start, double end, int sector) {
-            super(start, end);
-            this.sector = sector;
-        }
-
-        public int getSector() {
-            return sector;
-        }
-
-        public void setSector(int sector) {
-            this.sector = sector;
-        }
-
-        public boolean enlarge(SectorRange other) {
-            if (sector == other.sector
-            && Math.abs(end-other.start) < FixingsOverview.EPSILON) {
-                end = other.end;
-                return true;
-            }
-            return false;
-        }
-    } // class SectorRange
-
-    public static class Fixing implements Serializable {
-
-        public static final Comparator<Column> DATE_CMP =
-            new Comparator<Column>() {
-                @Override
-                public int compare(Column a, Column b) {
-                    return a.startTime.compareTo(b.startTime);
-                }
-            };
-
-        public interface Filter {
-
-            boolean accept(Column column);
-
-        } // interface Filter
-
-        public class Column extends Range {
-
-            protected int    columnId;
-            protected Date   startTime;
-            protected String name;
-
-            protected List<SectorRange> sectors;
-
-            public Column() {
-            }
-
-            public Column(int columnId, Date startTime, String name) {
-                this.columnId  = columnId;
-                this.startTime = startTime;
-                this.name      = name;
-
-                sectors = new ArrayList<SectorRange>();
-            }
-
-            public int getId() {
-                return columnId;
-            }
-
-            public Fixing getFixing() {
-                return Fixing.this;
-            }
-
-            public Date getStartTime() {
-                return startTime;
-            }
-
-            public String getName() {
-                return name;
-            }
-
-            public String getDescription() {
-                return Fixing.this.description + "/" + name;
-            }
-
-            public List<SectorRange> getSectors() {
-                return sectors;
-            }
-
-            public boolean hasSectorsInRange(Range range) {
-                for (SectorRange sector: sectors) {
-                    if (sector.intersects(range)) {
-                        return true;
-                    }
-                }
-                return false;
-            }
-
-            public List<SectorRange> getSectors(Range range) {
-
-                List<SectorRange> result =
-                    new ArrayList<SectorRange>(sectors.size());
-
-                for (SectorRange src: sectors) {
-                    SectorRange dst = new SectorRange(src);
-                    if (range == null || dst.clip(range)) {
-                        result.add(dst);
-                    }
-                }
-
-                return result;
-            }
-
-            public int findQSector(double km) {
-                for (SectorRange sector: sectors) {
-                    if (sector.inside(km)) {
-                        return sector.getSector();
-                    }
-                }
-                return -1;
-            }
-
-            public void buildSectors(
-                GaugeFinder  gaugeFinder,
-                List<QRange> qRanges
-            ) {
-                for (QRange qRange: qRanges) {
-                    for (GaugeRange gRange: gaugeFinder.getGauges()) {
-                        SectorRange sector = new SectorRange(qRange);
-                        if (!sector.clip(gRange)) {
-                            continue;
-                        }
-                        sector.setSector(gRange.classify(qRange.q));
-
-                        if (sectors.isEmpty()
-                        || !sectors.get(sectors.size()-1).enlarge(sector)) {
-                            sectors.add(sector);
-                        }
-                    } // for all gauges
-                } // for all Q ranges
-            }
-
-            public void loadKmRange(SQLQuery query) {
-                query.setInteger("column_id", columnId);
-
-                List<Object []> kms = query.list();
-
-                if (kms.isEmpty()) {
-                    log.warn("No km range for column " + columnId + ".");
-                }
-                else {
-                    Object [] obj = kms.get(0);
-                    start = (Double)obj[0];
-                    end   = (Double)obj[1];
-                }
-            }
-
-            public void loadQRanges(
-                SQLQuery    query,
-                GaugeFinder gaugeFinder
-            ) {
-                query.setInteger("column_id", columnId);
-                List<Object []> list = query.list();
-
-                List<QRange> qRanges = new ArrayList<QRange>(list.size());
-
-                for (Object [] row: list) {
-                    double q     = (Double)row[0];
-                    double start = (Double)row[1];
-                    double end   = (Double)row[2];
-                    QRange qRange = new QRange(start, end, q);
-                    if (qRange.clip(this)) {
-                        qRanges.add(qRange);
-                    }
-                }
-
-                buildSectors(gaugeFinder, qRanges);
-            }
-        } // class Column
-
-        protected int          wstId;
-        protected String       description;
-        protected List<Column> columns;
-
-        public Fixing() {
-        }
-
-        public int getId() {
-            return wstId;
-        }
-
-        public String getDescription() {
-            return description;
-        }
-
-        public Fixing(int wstId, String description) {
-            this.wstId       = wstId;
-            this.description = description;
-            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 loadColumnsKmRange(SQLQuery query) {
-            for (Column column: columns) {
-                column.loadKmRange(query);
-            }
-        }
-
-        public void adjustExtent(Range extent) {
-            for (Column column: columns) {
-                extent.extend(column);
-            }
-        }
-
-        public void loadColumnsQRanges(
-            SQLQuery    query,
-            GaugeFinder gaugeFinder
-        ) {
-            for (Column column: columns) {
-                column.loadQRanges(query, gaugeFinder);
-            }
-        }
-
-        /**
-         * @param allColumns[out] Columns will be put here.
-         * @param range can be null.
-         * @param filter filter to apply.
-         */
-        public void addAllColumns(
-            List<Column> allColumns,
-            Range        range,
-            Filter       filter
-        ) {
-            for (Column column: columns) {
-                if ((range == null || column.hasSectorsInRange(range))
-                && (filter == null || filter.accept(column))) {
-                    allColumns.add(column);
-                }
-            }
-        }
-    } // class Fixing
-
-
-    protected String       riverName;
-    protected int          riverId;
-    protected boolean      isKmUp;
-    protected List<Fixing> fixings;
-    protected Range        extent;
-
-    public FixingsOverview() {
-        fixings = new ArrayList<Fixing>();
-        extent  = new Range(Double.MAX_VALUE, -Double.MAX_VALUE);
-    }
-
-    public FixingsOverview(String riverName) {
-        this();
-        this.riverName = riverName;
-    }
-
-    protected boolean loadRiver(Session session) {
-        SQLQuery query = session.createSQLQuery(SQL_RIVER_ID)
-            .addScalar("river_id", StandardBasicTypes.INTEGER)
-            .addScalar("km_up",    StandardBasicTypes.BOOLEAN);
-
-        query.setString("name", riverName);
-
-        List<Object []> list = query.list();
-
-        if (list.isEmpty()) {
-            log.warn("No river '" + riverName + "' found.");
-            return false;
-        }
-
-        Object [] row = list.get(0);
-
-        riverId = (Integer)row[0];
-        isKmUp  = (Boolean)row[1];
-
-        return true;
-    }
-
-    protected void loadFixings(Session session) {
-        SQLQuery query = session.createSQLQuery(SQL_FIXINGS)
-            .addScalar("wst_id",      StandardBasicTypes.INTEGER)
-            .addScalar("description", StandardBasicTypes.STRING);
-
-        query.setInteger("river_id", riverId);
-
-        List<Object []> list = query.list();
-
-        if (list.isEmpty()) {
-            log.warn("River " + riverId + " has no fixings.");
-            // Its pretty fine to have no fixings.
-        }
-
-        for (Object [] row: list) {
-            int    wstId       = (Integer)row[0];
-            String description = (String) row[1];
-            Fixing fixing = new Fixing(wstId, description);
-            fixings.add(fixing);
-        }
-    }
-
-    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);
-
-        for (Fixing fixing: fixings) {
-            fixing.loadColumns(query);
-        }
-    }
-
-    protected void loadFixingsColumnsKmRange(Session session) {
-        SQLQuery query = session.createSQLQuery(SQL_FIXING_COLUMN_KM_RANGE)
-            .addScalar("start_km", StandardBasicTypes.DOUBLE)
-            .addScalar("stop_km",  StandardBasicTypes.DOUBLE);
-
-        for (Fixing fixing: fixings) {
-            fixing.loadColumnsKmRange(query);
-        }
-    }
-
-    protected void loadFixingsColumnsQRanges(
-        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);
-
-        for (Fixing fixing: fixings) {
-            fixing.loadColumnsQRanges(query, gaugeFinder);
-        }
-    }
-
-    protected void adjustExtent() {
-        for (Fixing fixing: fixings) {
-            fixing.adjustExtent(extent);
-        }
-    }
-
-    public boolean load(Session session) {
-
-        if (!loadRiver(session)) {
-            return false;
-        }
-
-        GaugeFinderFactory gff = GaugeFinderFactory.getInstance();
-
-        GaugeFinder gaugeFinder = gff.getGaugeFinder(riverId, isKmUp);
-
-        if (gaugeFinder == null) {
-            return false;
-        }
-
-        loadFixings(session);
-        loadFixingsColumns(session);
-        loadFixingsColumnsKmRange(session);
-
-        adjustExtent();
-
-        loadFixingsColumnsQRanges(session, gaugeFinder);
-
-        return true;
-    }
-
-    public static final Range FULL_EXTENT =
-        new Range(-Double.MAX_VALUE, Double.MAX_VALUE);
-
-    public static final Fixing.Filter ACCEPT = new Fixing.Filter() {
-        @Override
-        public boolean accept(Fixing.Column column) {
-            return true;
-        }
-    };
-
-    public static class NotFilter implements Fixing.Filter {
-        protected Fixing.Filter child;
-
-        public NotFilter(Fixing.Filter child) {
-            this.child = child;
-        }
-
-        @Override
-        public boolean accept(Fixing.Column column) {
-            return !child.accept(column);
-        }
-    } // class NotFilter
-
-    public static abstract class ComponentFilter implements Fixing.Filter {
-        protected List<Fixing.Filter> children;
-
-        public ComponentFilter() {
-            children = new ArrayList<Fixing.Filter>();
-        }
-
-        public ComponentFilter(List<Fixing.Filter> children) {
-            this.children = children;
-        }
-
-        public ComponentFilter add(Fixing.Filter filter) {
-            children.add(filter);
-            return this;
-        }
-    } // class ComponentFilter
-
-    public static class OrFilter extends ComponentFilter {
-
-        public OrFilter() {
-        }
-
-        public OrFilter(List<Fixing.Filter> children) {
-            super(children);
-        }
-
-        @Override
-        public boolean accept(Fixing.Column column) {
-            for (Fixing.Filter child: children) {
-                if (child.accept(column)) {
-                    return true;
-                }
-            }
-            return false;
-        }
-    } // class OrFilter
-
-    public static class AndFilter extends ComponentFilter {
-
-        public AndFilter() {
-        }
-
-        public AndFilter(List<Fixing.Filter> children) {
-            super(children);
-        }
-
-        @Override
-        public boolean accept(Fixing.Column column) {
-            for (Fixing.Filter child: children) {
-                if (!child.accept(column)) {
-                    return false;
-                }
-            }
-            return true;
-        }
-    } // class AndFilter
-
-    public static class IdFilter implements Fixing.Filter {
-
-        protected int columnId;
-
-        public IdFilter(int columnId) {
-            this.columnId = columnId;
-        }
-
-        @Override
-        public boolean accept(Fixing.Column column) {
-            return column.getId() == columnId;
-        }
-    } // class IdFilter
-
-    /** Accept Fixing columns whose id is in id list. */
-    public static class IdsFilter implements Fixing.Filter {
-
-        protected int [] columnIds;
-
-        public IdsFilter(int [] columnIds) {
-            this.columnIds = columnIds;
-        }
-
-        @Override
-        public boolean accept(Fixing.Column column) {
-            int cid = column.getId();
-            for (int i = columnIds.length-1; i >= 0; --i) {
-                if (columnIds[i] == cid) {
-                    return true;
-                }
-            }
-            return false;
-        }
-    } // class IdFilter
-
-    public static class DateFilter implements Fixing.Filter {
-
-        protected Date date;
-
-        public DateFilter(Date date) {
-            this.date = date;
-        }
-
-        @Override
-        public boolean accept(Fixing.Column column) {
-            return date.equals(column.getStartTime());
-        }
-    } // class DateFilter
-
-    public static class DateRangeFilter implements Fixing.Filter {
-
-        protected Date start;
-        protected Date end;
-
-        public DateRangeFilter(Date start, Date end) {
-            this.start = start;
-            this.end   = end;
-        }
-
-        @Override
-        public boolean accept(Fixing.Column column) {
-            Date date = column.getStartTime();
-            return start.compareTo(date) <= 0 && end.compareTo(date) >= 0;
-        }
-    } // class DateRangeFilter
-
-    public static class SectorFilter implements Fixing.Filter {
-
-        protected int sector;
-
-        public SectorFilter(int sector) {
-            this.sector = sector;
-        }
-
-        @Override
-        public boolean accept(Fixing.Column column) {
-            for (SectorRange s: column.getSectors()) {
-                if (s.getSector() == sector) {
-                    return true;
-                }
-            }
-            return false;
-        }
-    } // class SectorFilter
-
-    public static class SectorRangeFilter implements Fixing.Filter {
-
-        protected int min;
-        protected int max;
-
-        public SectorRangeFilter(int min, int max) {
-            this.min = Math.min(min, max);
-            this.max = Math.max(min, max);
-        }
-
-        @Override
-        public boolean accept(Fixing.Column column) {
-            for (SectorRange s: column.getSectors()) {
-                int v = s.getSector();
-                if (v < min || v > max) {
-                    return false;
-                }
-            }
-            return true;
-        }
-    } // class SectorRangeFilter
-
-    public static class KmFilter implements Fixing.Filter {
-
-        protected double km;
-
-        public KmFilter(double km) {
-            this.km = km;
-        }
-
-        @Override
-        public boolean accept(Fixing.Column column) {
-            for (SectorRange s: column.getSectors()) {
-                if (s.inside(km)) {
-                    return true;
-                }
-            }
-            return false;
-        }
-    } // class KmFilter
-
-    public void generateOverview(Document document) {
-        generateOverview(document, FULL_EXTENT, ACCEPT);
-    }
-
-    public List<Fixing.Column> filter(Range range, Fixing.Filter filter) {
-        List<Fixing.Column> allColumns = new ArrayList<Fixing.Column>();
-
-        for (Fixing fixing: fixings) {
-            fixing.addAllColumns(allColumns, range, filter);
-        }
-
-        Collections.sort(allColumns, Fixing.DATE_CMP);
-
-        return allColumns;
-    }
-
-    protected static Range realRange(List<Fixing.Column> columns) {
-        Range range = null;
-        for (Fixing.Column column: columns) {
-            if (range == null) {
-                range = new Range(column);
-            }
-            else {
-                range.extend(column);
-            }
-        }
-        return range;
-    }
-
-    protected Element intersectingGauges(Document document, Range range) {
-        Element gauges = document.createElement("gauges");
-
-        if (range == null) {
-            return gauges;
-        }
-
-        GaugeFinderFactory gff = GaugeFinderFactory.getInstance();
-
-        GaugeFinder gf = gff.getGaugeFinder(riverId, isKmUp);
-
-        if (gf == null) {
-            return gauges;
-        }
-
-        for (GaugeRange gr: gf.getGauges()) {
-            if (gr.intersects(range)) {
-                Element gauge = document.createElement("gauge");
-                gauge.setAttribute("from", String.valueOf(gr.getStart()));
-                gauge.setAttribute("to",   String.valueOf(gr.getEnd()));
-                gauge.setAttribute("name", gr.getName());
-                gauges.appendChild(gauge);
-            }
-        }
-
-        return gauges;
-    }
-
-    /** Populate document with fixings, filtered by range and filter. */
-    public void generateOverview(
-        Document      document,
-        Range         range,
-        Fixing.Filter filter
-    ) {
-        List<Fixing.Column> allColumns = filter(range, filter);
-
-        Element fixingsElement = document.createElement("fixings");
-
-        Element riverElement = document.createElement("river");
-
-        riverElement.setAttribute("from", String.valueOf(extent.start));
-        riverElement.setAttribute("to",   String.valueOf(extent.end));
-        riverElement.setAttribute("rid",  String.valueOf(riverId));
-        riverElement.setAttribute("name", riverName);
-
-        fixingsElement.appendChild(riverElement);
-
-        fixingsElement.appendChild(
-            intersectingGauges(
-                document,
-                realRange(allColumns)));
-
-        SimpleDateFormat df = new SimpleDateFormat(DATE_FORMAT);
-
-        Element esE = document.createElement("events");
-
-        for (Fixing.Column column: allColumns) {
-
-            List<SectorRange> sectors = column.getSectors(range);
-
-            if (!sectors.isEmpty()) {
-                Element eE = document.createElement("event");
-                eE.setAttribute("description",
-                    String.valueOf(column.getDescription()));
-                eE.setAttribute("cid", String.valueOf(column.columnId));
-                eE.setAttribute("date", df.format(column.startTime));
-
-                for (SectorRange sector: sectors) {
-                    Element sE = document.createElement("sector");
-
-                    sE.setAttribute("from",  String.valueOf(sector.start));
-                    sE.setAttribute("to",    String.valueOf(sector.end));
-                    sE.setAttribute("class", String.valueOf(sector.sector));
-
-                    eE.appendChild(sE);
-                }
-
-                esE.appendChild(eE);
-            }
-        }
-
-        fixingsElement.appendChild(esE);
-
-        document.appendChild(fixingsElement);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/FixingsOverviewFactory.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,73 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import de.intevation.flys.artifacts.cache.CacheFactory;
-
-import de.intevation.flys.backend.SessionHolder;
-
-import net.sf.ehcache.Cache;
-import net.sf.ehcache.Element;
-
-import org.apache.log4j.Logger;
-
-import org.hibernate.Session;
-
-public class FixingsOverviewFactory
-{
-    private static Logger log = Logger.getLogger(FixingsOverviewFactory.class);
-
-    public static final String CACHE_NAME = "fixings-overviews";
-
-    private FixingsOverviewFactory() {
-    }
-
-
-    public static FixingsOverview getOverview(String river) {
-
-        boolean debug = log.isDebugEnabled();
-
-        if (debug) {
-            log.debug(
-                "Looking for fixings overview for river '" + river + "'");
-        }
-
-        Cache cache = CacheFactory.getCache(CACHE_NAME);
-
-        if (cache == null) {
-            if (debug) {
-                log.debug("Cache not configured.");
-            }
-            return getUncached(river);
-        }
-
-        String key = "fix-over-" + river;
-
-        Element element = cache.get(key);
-
-        if (element != null) {
-            if (debug) {
-                log.debug("Overview found in cache");
-            }
-            return (FixingsOverview)element.getValue();
-        }
-
-        FixingsOverview overview = getUncached(river);
-
-        if (overview != null) {
-            if (debug) {
-                log.debug("Store overview in cache.");
-            }
-            cache.put(new Element(key, overview));
-        }
-
-        return overview;
-    }
-
-    public static FixingsOverview getUncached(String river) {
-        FixingsOverview overview = new FixingsOverview(river);
-
-        Session session = SessionHolder.HOLDER.get();
-
-        return overview.load(session) ? overview : null;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/FlowVelocityCalculation.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,166 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import de.intevation.artifacts.Artifact;
-
-import de.intevation.flys.artifacts.access.FlowVelocityAccess;
-
-import de.intevation.flys.model.DischargeZone;
-import de.intevation.flys.model.FlowVelocityModel;
-import de.intevation.flys.model.FlowVelocityModelValue;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-
-/** Calculate flow velocity. */
-public class FlowVelocityCalculation extends Calculation {
-
-    /** Own logger. */
-    private static final Logger logger =
-        Logger.getLogger(FlowVelocityCalculation.class);
-
-
-    public CalculationResult calculate(FlowVelocityAccess access) {
-        logger.info("FlowVelocityCalculation.calculate");
-
-        int[] mainIds  = access.getMainChannels();
-        int[] totalIds = access.getTotalChannels();
-
-        if (logger.isDebugEnabled()) {
-            Artifact a = access.getArtifact();
-            logger.debug("Artifact '" + a.identifier() + "' contains:");
-            if (mainIds != null) {
-                logger.debug("   " + mainIds.length + " main channel ids");
-            }
-
-            if (totalIds != null) {
-                logger.debug("   " + totalIds.length + " total channel ids");
-            }
-        }
-
-        List<DischargeZone>     zones  = getDischargeZones(mainIds, totalIds);
-        List<FlowVelocityModel> models = getFlowVelocityModels(access, zones);
-
-        return buildCalculationResult(access, models);
-    }
-
-
-    protected List<DischargeZone> getDischargeZones(
-        int[] mainIds,
-        int[] totalIds
-    ) {
-        List<DischargeZone> zones = new ArrayList<DischargeZone>();
-
-        if (mainIds != null) {
-            for (int id: mainIds) {
-                DischargeZone zone = DischargeZone.getDischargeZoneById(id);
-                zone.putType("main");
-
-                if (zone != null) {
-                    zones.add(zone);
-                }
-            }
-        }
-
-        if (totalIds != null) {
-            for (int id: totalIds) {
-                DischargeZone zone = DischargeZone.getDischargeZoneById(id);
-                if (zone != null) {
-                    int ndx = zones.indexOf(zone);
-                    if (zones.contains(zone) &&
-                        zones.get(ndx).fetchType().equals("main")) {
-                        zone.putType("main_total");
-                    }
-                    else {
-                        zone.putType("total");
-                        zones.add(zone);
-                    }
-                }
-            }
-        }
-
-        return zones;
-    }
-
-
-    protected List<FlowVelocityModel> getFlowVelocityModels(
-        FlowVelocityAccess  access,
-        List<DischargeZone> zones
-    ) {
-        String riverName = access.getRiver();
-        if (riverName == null) {
-            logger.warn("No river name found");
-            return Collections.<FlowVelocityModel>emptyList();
-        }
-
-        List<FlowVelocityModel> models = new ArrayList<FlowVelocityModel>();
-
-        for (DischargeZone zone: zones) {
-            List<FlowVelocityModel> model = FlowVelocityModel.getModels(zone);
-            models.addAll(model);
-        }
-
-        return models;
-    }
-
-
-    protected void prepareData(
-        FlowVelocityData  data,
-        FlowVelocityModel model,
-        double kmLo,
-        double kmHi
-    ) {
-        List<FlowVelocityModelValue> values =
-            FlowVelocityModelValue.getValues(model, kmLo, kmHi);
-
-        logger.debug("Found " + values.size() + " values for model.");
-
-        for (FlowVelocityModelValue value: values) {
-            data.addKM(value.getStation().doubleValue());
-            data.addQ(value.getQ().doubleValue());
-            data.addVTotal(value.getTotalChannel().doubleValue());
-            data.addVMain(value.getMainChannel().doubleValue());
-            data.addTauMain(value.getShearStress().doubleValue());
-        }
-
-        DischargeZone zone = model.getDischargeZone();
-        String        lo   = zone.getLowerDischarge();
-        String        hi   = zone.getUpperDischarge();
-
-        data.setType(zone.fetchType());
-        if (lo.equals(hi)) {
-            data.setZone(lo);
-        }
-        else {
-            data.setZone(lo + " - " + hi);
-        }
-    }
-
-
-    protected CalculationResult buildCalculationResult(
-        FlowVelocityAccess         access,
-        List<FlowVelocityModel> models
-    ) {
-        double kmLo = access.getLowerKM();
-        double kmHi = access.getUpperKM();
-
-        logger.debug("Prepare data for km range: " + kmLo + " - " + kmHi);
-
-        FlowVelocityData[] data = new FlowVelocityData[models.size()];
-        for (int i = 0, n = models.size(); i < n; i++) {
-            FlowVelocityData d = new FlowVelocityData();
-
-            prepareData(d, models.get(i), kmLo, kmHi);
-
-            data[i] = d;
-        }
-
-        logger.debug("Calculation contains " + data.length + " data items.");
-
-        return new CalculationResult(data, this);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/FlowVelocityData.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,135 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.io.Serializable;
-
-import gnu.trove.TDoubleArrayList;
-
-
-public class FlowVelocityData implements Serializable {
-
-    private TDoubleArrayList km;
-    private TDoubleArrayList vMain;
-    private TDoubleArrayList vTotal;
-    private TDoubleArrayList tauMain;
-    private TDoubleArrayList q;
-    private String zone;
-    private String type;
-
-    protected FlowVelocityData() {
-        this.km      = new TDoubleArrayList();
-        this.vMain   = new TDoubleArrayList();
-        this.vTotal  = new TDoubleArrayList();
-        this.tauMain = new TDoubleArrayList();
-        this.q       = new TDoubleArrayList();
-    }
-
-
-    public void addKM(double km) {
-        this.km.add(km);
-    }
-
-    public double getKM(int idx) {
-        return km.get(idx);
-    }
-
-    public void addVMain(double vMain) {
-        this.vMain.add(vMain);
-    }
-
-    public double getVMain(int idx) {
-        return vMain.get(idx);
-    }
-
-    public void addVTotal(double vTotal) {
-        this.vTotal.add(vTotal);
-    }
-
-    public double getVTotal(int idx) {
-        return vTotal.get(idx);
-    }
-
-    public void addTauMain(double tauMain) {
-        this.tauMain.add(tauMain);
-    }
-
-    public double getTauMain(int idx) {
-        return tauMain.get(idx);
-    }
-
-    public void addQ(double q) {
-        this.q.add(q);
-    }
-
-    public double getQ(int idx) {
-        return q.get(idx);
-    }
-
-    public void setZone(String zone) {
-        this.zone = zone;
-    }
-
-    public String getZone() {
-        return zone;
-    }
-
-    public void setType(String type) {
-        this.type = type;
-    }
-
-    public String getType() {
-        return this.type;
-    }
-
-    public int size() {
-        return km.size();
-    }
-
-
-    public double[][] getMainChannelPoints() {
-        double[][] points = new double[2][size()];
-
-        for (int i = 0, n = size(); i < n; i++) {
-            points[0][i] = getKM(i);
-            points[1][i] = getVMain(i);
-        }
-
-        return points;
-    }
-
-
-    public double[][] getTotalChannelPoints() {
-        double[][] points = new double[2][size()];
-
-        for (int i = 0, n = size(); i < n; i++) {
-            points[0][i] = getKM(i);
-            points[1][i] = getVTotal(i);
-        }
-
-        return points;
-    }
-
-
-    public double[][] getQPoints() {
-        double[][] points = new double[2][size()];
-
-        for (int i = 0, n = size(); i < n; i++) {
-            points[0][i] = getKM(i);
-            points[1][i] = getQ(i);
-        }
-
-        return points;
-    }
-
-
-    public double[][] getTauPoints() {
-        double[][] points = new double[2][size()];
-
-        for (int i = 0, n = size(); i < n; i++) {
-            points[0][i] = getKM(i);
-            points[1][i] = getTauMain(i);
-        }
-
-        return points;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/FlowVelocityFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,63 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import de.intevation.artifactdatabase.state.Facet;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-import org.apache.log4j.Logger;
-
-/**
- * Facet of a FlowVelocity curve.
- */
-public class FlowVelocityFacet extends DataFacet {
-
-    private static Logger logger = Logger.getLogger(FlowVelocityFacet.class);
-
-    public FlowVelocityFacet() {
-        // required for clone operation deepCopy()
-    }
-
-
-    public FlowVelocityFacet(
-        int         idx,
-        String      name,
-        String      description,
-        ComputeType type,
-        String      stateId,
-        String      hash
-    ) {
-        super(idx, name, description, type, hash, stateId);
-    }
-
-
-    public Object getData(Artifact artifact, CallContext context) {
-        logger.debug("Get data for flow velocity at index: " + index);
-
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-
-        CalculationResult res = (CalculationResult)
-            flys.compute(context, hash, stateId, type, false);
-
-        FlowVelocityData[] data = (FlowVelocityData[]) res.getData();
-
-        return data[index];
-    }
-
-
-    /** Copy deeply. */
-    @Override
-    public Facet deepCopy() {
-        FlowVelocityFacet copy = new FlowVelocityFacet();
-        copy.set(this);
-        copy.type    = type;
-        copy.hash    = hash;
-        copy.stateId = stateId;
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/FlowVelocityFilterFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,90 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import de.intevation.artifactdatabase.state.Facet;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.flys.artifacts.access.RiverAccess;
-import de.intevation.flys.artifacts.context.FLYSContext;
-
-import de.intevation.flys.artifacts.math.MovingAverage;
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-import org.apache.log4j.Logger;
-
-/**
- * Facet of a FlowVelocity curve.
- */
-public class FlowVelocityFilterFacet extends DataFacet {
-
-    private static Logger logger = Logger.getLogger(FlowVelocityFilterFacet.class);
-
-    public FlowVelocityFilterFacet() {
-        // required for clone operation deepCopy()
-    }
-
-
-    public FlowVelocityFilterFacet(
-        int         idx,
-        String      name,
-        String      description,
-        ComputeType type,
-        String      stateId,
-        String      hash
-    ) {
-        super(idx, name, description, type, hash, stateId);
-    }
-
-
-    public Object getData(Artifact artifact, CallContext context) {
-        logger.debug("Get data for flow velocity at index: " + index);
-
-        Double start = (Double)context.getContextValue("startkm");
-        Double end = (Double)context.getContextValue("endkm");
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-
-        CalculationResult res = (CalculationResult)
-            flys.compute(context, hash, stateId, type, false);
-
-        FlowVelocityData[] data = (FlowVelocityData[]) res.getData();
-        if(start != null && end != null) {
-            FLYSContext fc = (FLYSContext)context.globalContext();
-            ZoomScale scales = (ZoomScale)fc.get("zoomscale");
-            RiverAccess access = new RiverAccess((FLYSArtifact)artifact);
-            String river = access.getRiver();
-
-            double radius = scales.getRadius(river, start, end);
-            FlowVelocityData oldData = data[index];
-            FlowVelocityData newData = new FlowVelocityData();
-            double[][] q = oldData.getQPoints();
-            double[][] totalV = MovingAverage.weighted(oldData.getTotalChannelPoints(), radius);
-            double[][] mainV = MovingAverage.weighted(oldData.getMainChannelPoints(), radius);
-            double[][] tau = MovingAverage.weighted(oldData.getTauPoints(), radius);
-            for(int j = 0; j < q[0].length; j++) {
-                newData.addKM(q[0][j]);
-                newData.addQ(q[1][j]);
-                newData.addTauMain(tau[1][j]);
-                newData.addVMain(mainV[1][j]);
-                newData.addVTotal(totalV[1][j]);
-            }
-            return newData;
-        }
-        return data[index];
-    }
-
-
-    /** Copy deeply. */
-    @Override
-    public Facet deepCopy() {
-        FlowVelocityFilterFacet copy = new FlowVelocityFilterFacet();
-        copy.set(this);
-        copy.type    = type;
-        copy.hash    = hash;
-        copy.stateId = stateId;
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/GaugeDischargeCurveFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,82 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.util.Arrays;
-import java.util.Map;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.artifactdatabase.state.DefaultFacet;
-import de.intevation.artifactdatabase.state.Facet;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.flys.model.Gauge;
-
-import de.intevation.flys.utils.FLYSUtils;
-
-import org.apache.log4j.Logger;
-
-/**
- * A Facet that returns discharge curve data at a gauge
- *
- * @author <a href="mailto:bjoern.ricks@intevation.de">Björn Ricks</a>
- */
-public class GaugeDischargeCurveFacet
-extends      DefaultFacet
-implements FacetTypes
-{
-    private static final Logger log =
-        Logger.getLogger(GaugeDischargeCurveFacet.class);
-
-    public GaugeDischargeCurveFacet(String name, String description) {
-        super(0, name, description);
-    }
-
-    @Override
-    public Object getData(Artifact art, CallContext context) {
-        return getWQKms(art, context);
-    }
-
-    protected WQKms getWQKms(Artifact art, CallContext context) {
-        if (!(art instanceof FLYSArtifact)) {
-            log.warn("Invalid artifact type");
-            return null;
-        }
-
-        FLYSArtifact flys = (FLYSArtifact)art;
-
-        String river = flys.getDataAsString("river");
-
-        Gauge gauge = FLYSUtils.getReferenceGauge(flys);
-
-        if (river == null || gauge == null) {
-            log.warn("Unknown river or gauge");
-            return null;
-        }
-
-        String name = gauge.getName();
-
-        DischargeTables dt = new DischargeTables(river, name);
-
-        Map<String, double [][]> map = dt.getValues(
-                DischargeTables.MASTER_SCALE);
-
-        double [][] values = map.get(name);
-        if (values == null) {
-            return null;
-        }
-        double [] kms = new double[values[0].length];
-        Arrays.fill(kms, gauge.getStation().doubleValue());
-        return new WQKms(kms, values[0], values[1], name);
-    }
-
-    @Override
-    public Facet deepCopy() {
-        GaugeDischargeCurveFacet copy = new GaugeDischargeCurveFacet(
-                this.name,
-                this.description);
-        copy.set(this);
-        return copy;
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/GaugeDischargeFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,86 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.artifactdatabase.state.Facet;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-
-/**
- * Access Discharge Curve of a gauge.
- */
-public class GaugeDischargeFacet extends DataFacet {
-
-    /** Private logger. */
-    private static final Logger logger =
-        Logger.getLogger(GaugeDischargeFacet.class);
-
-
-    public GaugeDischargeFacet() {
-    }
-
-
-    /**
-     * @param index Index translates to index of WQ-array.
-     * @param name Name of the facet.
-     * @param desc Description of the facet (visible in client).
-     */
-    public GaugeDischargeFacet(int index, String name, String desc) {
-        super(index, name, desc, ComputeType.ADVANCE, null,
-            "state.gaugedischarge.init");
-    }
-
-
-    public GaugeDischargeFacet(
-        int         index,
-        String      name,
-        String      description,
-        ComputeType type,
-        String      stateID,
-        String      hash
-
-    ) {
-        super(index, name, description, type, hash, stateID);
-    }
-
-
-    @Override
-    public Facet deepCopy() {
-        GaugeDischargeFacet copy = new GaugeDischargeFacet();
-        copy.set(this);
-        copy.type    = type;
-        copy.hash    = hash;
-        copy.stateId = stateId;
-        return copy;
-    }
-
-
-    /**
-     * @return wqkms corresponding to gauge of artifact and index of facet.
-     */
-    @Override
-    public Object getData(Artifact artifact, CallContext context) {
-        if (logger.isDebugEnabled()) {
-            logger.debug("Get data for discharge curves at index: " +
-                index + " / stateId: " + stateId);
-        }
-
-        if (stateId == null) {
-            logger.error("GaugeDischargeFacet.getData: stateId is null.");
-        }
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-
-        CalculationResult res = (CalculationResult)
-            flys.compute(context, hash, stateId, type, true);
-
-        WQKms[] discharge = (WQKms[]) res.getData();
-
-        return discharge[index];
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/GaugeFinder.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,134 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.io.Serializable;
-
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import org.hibernate.SQLQuery;
-import org.hibernate.Session;
-
-import org.hibernate.type.StandardBasicTypes;
-
-/** Find Gauges and respective Q main values. */
-public class GaugeFinder
-implements   Serializable
-{
-    private static Logger log = Logger.getLogger(GaugeFinder.class);
-
-    public static final String SQL_DISCHARGE_SECTORS =
-        "SELECT" +
-        "    g.id                            AS gauge_id," +
-        "    nmv.name                        AS name," +
-        "    CAST(mv.value AS NUMERIC(38,2)) AS value " +
-        "FROM gauges g" +
-        "    JOIN main_values       mv  ON g.id = mv.gauge_id" +
-        "    JOIN named_main_values nmv ON nmv.id = mv.named_value_id" +
-        "    JOIN main_value_types  mvt ON nmv.type_id = mvt.id " +
-        "WHERE" +
-        "    mvt.name = 'Q' AND (" +
-        "        nmv.name = 'MNQ'      OR" +
-        "        nmv.name LIKE 'MNQ(%' OR" +
-        "        nmv.name = 'MQ'       OR" +
-        "        nmv.name LIKE 'MQ(%'  OR" +
-        "        nmv.name = 'MHQ'      OR" +
-        "        nmv.name LIKE 'MHQ(%' OR" +
-        "        nmv.name = 'HQ5'      OR" +
-        "        nmv.name LIKE 'HQ5(%') AND" +
-        "    g.river_id = :river_id " +
-        "ORDER BY" +
-        "    g.id";
-
-    protected List<GaugeRange> gauges;
-    protected boolean          isKmUp;
-
-    public GaugeFinder(List<GaugeRange> gauges) {
-        this(gauges, true);
-    }
-
-    public GaugeFinder(
-        List<GaugeRange> gauges,
-        boolean          isKmUp
-    ) {
-        this.gauges = gauges;
-        this.isKmUp = isKmUp;
-    }
-
-    public boolean getIsKmUp() {
-        return isKmUp;
-    }
-
-    public void setIsKmUp(boolean isKmUp) {
-        this.isKmUp = isKmUp;
-    }
-
-
-    /** Find GaugeRange at kilometer. */
-    public GaugeRange find(double km) {
-        for (GaugeRange gauge: gauges) {
-            if (gauge.inside(km)) {
-                return gauge;
-            }
-        }
-        return null;
-    }
-
-    public GaugeRange find(Range range) {
-        return find(isKmUp ? range.start : range.end);
-    }
-
-    public GaugeRange find(int gaugeId) {
-        for (GaugeRange gauge: gauges) {
-            if (gauge.gaugeId == gaugeId) {
-                return gauge;
-            }
-        }
-        return null;
-    }
-
-    public List<GaugeRange> getGauges() {
-        return gauges;
-    }
-
-    public boolean loadDischargeSectors(Session session, int riverId) {
-
-        SQLQuery query = session.createSQLQuery(SQL_DISCHARGE_SECTORS)
-            .addScalar("gauge_id", StandardBasicTypes.INTEGER)
-            .addScalar("name",     StandardBasicTypes.STRING)
-            .addScalar("value",    StandardBasicTypes.DOUBLE);
-
-        query.setInteger("river_id", riverId);
-
-        List<Object []> list = query.list();
-
-        if (list.isEmpty()) {
-            log.warn("River " + riverId + " has no discharge sectors.");
-            return false;
-        }
-
-        GaugeRange gauge = null;
-
-        for (Object [] row: list) {
-            int    gaugeId = (Integer)row[0];
-            String label   = (String) row[1];
-            Double value   = (Double) row[2];
-
-            if (gauge == null || gauge.gaugeId != gaugeId) {
-                if ((gauge = find(gaugeId)) == null) {
-                    log.warn("Cannot find gauge for id " + gaugeId + ".");
-                    continue;
-                }
-            }
-
-            gauge.addMainValue(label, value);
-        }
-
-        for (GaugeRange g: gauges) {
-            g.buildClasses();
-        }
-
-        return true;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/GaugeFinderFactory.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,134 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import de.intevation.flys.artifacts.cache.CacheFactory;
-
-import de.intevation.flys.backend.SessionHolder;
-
-import de.intevation.flys.model.River;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import net.sf.ehcache.Cache;
-import net.sf.ehcache.Element;
-
-import org.apache.log4j.Logger;
-
-import org.hibernate.SQLQuery;
-import org.hibernate.Session;
-
-import org.hibernate.type.StandardBasicTypes;
-
-/** Get GaugeFinders. */
-public class GaugeFinderFactory
-{
-    private static Logger log = Logger.getLogger(GaugeFinderFactory.class);
-
-    public static final String CACHE_NAME = "gauge-finders";
-
-    public static final String SQL_GAUGES =
-        "SELECT" +
-        "    g.id AS gauge_id," +
-        "    g.name AS name," +
-        "    r.a  AS a," +
-        "    r.b  AS b " +
-        "FROM gauges g" +
-        "    JOIN ranges r ON g.range_id = r.id " +
-        "WHERE" +
-        "    g.river_id = :river_id " +
-        "ORDER BY r.a";
-
-    private static GaugeFinderFactory INSTANCE;
-
-    protected GaugeFinderFactory() {
-    }
-
-    public static synchronized GaugeFinderFactory getInstance() {
-        if (INSTANCE == null) {
-            INSTANCE = new GaugeFinderFactory();
-        }
-
-        return INSTANCE;
-    }
-
-    public GaugeFinder getGaugeFinder(String riverName) {
-        River river = RiverFactory.getRiver(riverName);
-        return river != null
-            ? getGaugeFinder(river.getId(), river.getKmUp())
-            : null;
-    }
-
-    public synchronized GaugeFinder getGaugeFinder(
-        int     riverId,
-        boolean isKmUp
-    ) {
-        Cache cache = CacheFactory.getCache(CACHE_NAME);
-
-        if (cache == null) {
-            return getUncached(riverId, isKmUp);
-        }
-
-        String cacheKey = riverId + "-" + isKmUp;
-        Element element = cache.get(cacheKey);
-
-        if (element != null) {
-            return (GaugeFinder)element.getValue();
-        }
-
-        GaugeFinder finder = getUncached(riverId, isKmUp);
-
-        if (finder != null) {
-            cache.put(new Element(cacheKey, finder));
-        }
-
-        return finder;
-    }
-
-    protected GaugeFinder loadGauges(
-        Session session,
-        int     riverId,
-        boolean isKmUp
-    ) {
-        SQLQuery query = session.createSQLQuery(SQL_GAUGES)
-            .addScalar("gauge_id", StandardBasicTypes.INTEGER)
-            .addScalar("name",     StandardBasicTypes.STRING)
-            .addScalar("a",        StandardBasicTypes.DOUBLE)
-            .addScalar("b",        StandardBasicTypes.DOUBLE);
-
-        query.setInteger("river_id", riverId);
-
-        List<Object []> list = query.list();
-
-        if (list.isEmpty()) {
-            log.warn("River " + riverId + " has no gauges.");
-            return null;
-        }
-
-        List<GaugeRange> gauges = new ArrayList<GaugeRange>();
-
-        for (Object [] row: list) {
-            int    gaugeId = (Integer)row[0];
-            String name    = (String) row[1];
-            double start   = (Double) row[2];
-            double end     = (Double) row[3];
-            GaugeRange gauge = new GaugeRange(start, end, name, gaugeId);
-            gauges.add(gauge);
-        }
-
-        return new GaugeFinder(gauges, isKmUp);
-    }
-
-    protected GaugeFinder getUncached(int riverId, boolean isKmUp) {
-        Session session = SessionHolder.HOLDER.get();
-
-        GaugeFinder finder = loadGauges(session, riverId, isKmUp);
-
-        if (finder == null
-        || !finder.loadDischargeSectors(session, riverId)) {
-            return null;
-        }
-
-        return finder;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/GaugeRange.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,153 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.io.Serializable;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.log4j.Logger;
-
-/**
- * Gauge, km-range, main values.
- */
-public class GaugeRange
-extends      Range
-{
-    private static Logger log = Logger.getLogger(GaugeRange.class);
-
-    private static final class Sector implements Serializable {
-
-        int    sector;
-        double value;
-
-        Sector(int sector, double value) {
-            this.sector = sector;
-            this.value  = value;
-        }
-    } // class Sector
-
-    protected String name;
-
-    protected int gaugeId;
-
-    /** Certain main value. */
-    protected Map<String, Double> mainValues;
-
-    protected List<Sector> sectors;
-
-
-    public GaugeRange() {
-    }
-
-
-    public GaugeRange(double start, double end, int gaugeId) {
-        this(start, end, null, gaugeId);
-    }
-
-
-    public GaugeRange(
-        double start,
-        double end,
-        String name,
-        int    gaugeId
-    ) {
-        super(start, end);
-        this.name = name;
-        this.gaugeId = gaugeId;
-        mainValues = new HashMap<String, Double>();
-        sectors = new ArrayList<Sector>(3);
-    }
-
-
-    public void addMainValue(String label, Double value) {
-        int idx = label.indexOf('(');
-        if (idx >= 0) {
-            label = label.substring(0, idx);
-        }
-        mainValues.put(label, value);
-    }
-
-
-    protected Double getMainValue(String label) {
-        Double v = mainValues.get(label);
-        if (v == null) {
-            log.warn("Missing main value '"
-                + label + "' for gauge " + gaugeId);
-        }
-        return v;
-    }
-
-
-    public Map<String, Double> getMainValues() {
-        return mainValues;
-    }
-
-
-    public void buildClasses() {
-        Double mnq = getMainValue("MNQ");
-        Double mq  = getMainValue("MQ");
-        Double mhq = getMainValue("MHQ");
-        Double hq5 = getMainValue("HQ5");
-
-        Double [][] pairs = {
-            { mnq,  mq },
-            {  mq, mhq },
-            { hq5, hq5 } };
-
-        for (int c = 0; c < pairs.length; ++c) {
-            Double [] pair = pairs[c];
-            if (pair[0] != null && pair[1] != null) {
-                double value = 0.5*(pair[0] + pair[1]);
-                sectors.add(new Sector(c, value));
-            }
-        }
-    }
-
-
-    public double getSectorBorder(int sector) {
-        for (Sector s: sectors) {
-            if (s.sector == sector) {
-                return s.value;
-            }
-        }
-        return Double.NaN;
-    }
-
-
-    public int classify(double value) {
-        for (Sector sector: sectors) {
-            if (value < sector.value) {
-                return sector.sector;
-            }
-        }
-        return sectors.size();
-    }
-
-
-    public String getName() {
-        return name;
-    }
-
-
-    public void setName(String name) {
-        this.name = name;
-    }
-
-
-    public String toString() {
-        StringBuilder sb = new StringBuilder("sectors: [");
-
-        for (int i = 0, S = sectors.size(); i < S; ++i) {
-            if (i > 0) sb.append(", ");
-            Sector s = sectors.get(i);
-            sb.append(s.sector).append(": ").append(s.value);;
-        }
-
-        sb.append("] mainvalues: ").append(mainValues);
-
-        return sb.toString();
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/GaugesFactory.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,70 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.util.List;
-import java.util.ArrayList;
-
-import de.intevation.flys.backend.SessionHolder;
-import de.intevation.flys.model.River;
-import de.intevation.flys.model.Gauge;
-import de.intevation.flys.model.Range;
-
-import org.hibernate.Session;
-import org.hibernate.Query;
-
-public class GaugesFactory
-{
-    public static List<Gauge> getGauges(River river) {
-        return getGauges(river.getName());
-    }
-
-
-    public static Gauge getGauge(String gaugeName) {
-        Session session = SessionHolder.HOLDER.get();
-        Query query = session.createQuery(
-            "from Gauge where name=:name");
-        query.setParameter("name", gaugeName);
-
-        List<Gauge> res = query.list();
-
-        return res.isEmpty() ? null : res.get(0);
-    }
-
-
-    public static List<Gauge> getGauges(String river) {
-        Session session = SessionHolder.HOLDER.get();
-        Query query = session.createQuery(
-            "from Gauge where river.name=:name");
-        query.setParameter("name", river);
-        return query.list();
-    }
-
-    public static List<Gauge> filterRanges(
-        List<Gauge>     gauges,
-        List<double []> ranges
-    ) {
-        // XXX: Inefficent!
-        ArrayList<Range> rs = new ArrayList<Range>();
-        for (double [] range: ranges) {
-            double a = range[0];
-            double b = range[1];
-            rs.add(new Range(Math.min(a, b), Math.max(a, b), null));
-        }
-        return filter(gauges, rs);
-    }
-
-    public static List<Gauge> filter(List<Gauge> gauges, List<Range> ranges) {
-        // TODO: Make it an HQL filter!
-        ArrayList<Gauge> out = new ArrayList<Gauge>();
-        for (Gauge gauge: gauges) {
-            Range range = gauge.getRange();
-            for (Range cmp: ranges) {
-                if (range.intersects(cmp)) {
-                    out.add(gauge);
-                    break;
-                }
-            }
-        }
-        return out;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/HYKFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,78 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.DataProvider;
-import de.intevation.flys.artifacts.HYKArtifact;
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-import de.intevation.flys.model.FastCrossSectionLine;
-
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-
-/**
- * Trival Facet for HYKs
- */
-public class HYKFacet
-extends      DataFacet
-implements   FacetTypes {
-
-    /** House logger. */
-    private static Logger logger = Logger.getLogger(HYKFacet.class);
-
-    /** Trivial constructor, set (maybe localized) description. */
-    public HYKFacet(int idx, String description) {
-        super(idx, HYK, description, ComputeType.FEED, null, null);
-    }
-
-
-    /**
-     * Set km from cross section- master to HYKArtifact, then fire up
-     * computation.
-     *
-     * @param art artifact to get data from.
-     * @param context ignored
-     */
-     @Override
-    public Object getData(Artifact art, CallContext context) {
-        logger.debug("HYKFacet.getData");
-
-        String dataKey = CrossSectionFacet.BLACKBOARD_CS_MASTER_DATA;
-
-        List<DataProvider> providers = context.getDataProvider(dataKey);
-        if (providers.size() < 1) {
-            logger.warn("Could not find Cross-Section data provider to get master cs km.");
-            return null;
-        }
-
-        FastCrossSectionLine crossSection = (FastCrossSectionLine) providers.get(0)
-            .provideData(dataKey, null, context);
-
-        if(crossSection == null) {
-            logger.debug("getData: crossSection is null");
-            return null;
-        }
-
-        double km = crossSection.getKm();
-        logger.debug("HYKFacet.getData: Master Cross Section is at km: " + km);
-
-        // Set this km at hyk artifact to be evaluated.
-        HYKArtifact hyk = (HYKArtifact) art;
-        hyk.setKm(km);
-
-        return hyk.compute(context, hash, stateId, type, false);
-    }
-
-
-    /** Do a deep copy. */
-    @Override
-    public Facet deepCopy() {
-        HYKFacet copy = new HYKFacet(this.index, this.description);
-        copy.set(this);
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/HYKFactory.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,211 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import de.intevation.flys.artifacts.cache.CacheFactory;
-import de.intevation.flys.backend.SessionHolder;
-import de.intevation.flys.model.HYK;
-import de.intevation.flys.model.HYKFlowZone;
-import de.intevation.flys.model.HYKFormation;
-
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.List;
-
-import net.sf.ehcache.Cache;
-import net.sf.ehcache.Element;
-
-import org.apache.log4j.Logger;
-import org.hibernate.Query;
-import org.hibernate.SQLQuery;
-import org.hibernate.Session;
-import org.hibernate.type.StandardBasicTypes;
-
-
-/**
- * Factory to access HYKs (hydrographic values).
- */
-public class HYKFactory
-{
-    private static Logger logger = Logger.getLogger(HYKFactory.class);
-
-    public static String HYK_CACHE_NAME = "hykache";
-
-
-    /** Do not instantiate a HYKFactory. */
-    private HYKFactory() {
-    }
-
-
-    /**
-     * Get List of Zones for given river and km.
-     */
-    public static Object getHYKs(int hykid, double km) {
-        logger.debug("HYKFactory.getHYKs");
-
-        Cache cache = CacheFactory.getCache(HYK_CACHE_NAME);
-
-        String cacheKey;
-
-        if (cache != null) {
-            cacheKey = "" + hykid + "_" + km;
-            Element element = cache.get(cacheKey);
-            if (element != null) {
-                logger.debug("Got hyk from cache");
-                return element.getValue();
-            }
-        }
-        else {
-            cacheKey = null;
-        }
-
-        List<Zone> zones = getZonesUncached(hykid, km);
-
-        if (zones != null && cacheKey != null) {
-            logger.debug("Store hykzones in cache.");
-            Element element = new Element(cacheKey, zones);
-            cache.put(element);
-        }
-
-        return zones;
-    }
-
-
-    /** Return name for hyk with given id. */
-    public static String getHykName(int hykid) {
-        logger.debug("HYKFactory.getHykName " + hykid);
-
-        Session session = SessionHolder.HOLDER.get();
-
-        Query query = session.createQuery(
-            "select description from HYK where id = :hykid ");
-        query.setParameter("hykid", hykid);
-
-        return (String) query.uniqueResult();
-    }
-
-
-    /**
-     * Ask DB for hyk zones.
-     * @param hykid ID of the 'main' HYK to query.
-     * @param km for which to get the hyk-zones.
-     * @return according zones.
-     */
-    public static List<Zone> getZonesUncached(int hykid, double km) {
-        if (logger.isDebugEnabled()) {
-            logger.debug("HYKFactory.getZoneUncached " + hykid + " km " + km);
-        }
-
-        Session session = SessionHolder.HOLDER.get();
-
-        // Find out flow-direction of river.
-        // OPTIMIZE: 1) query kmUp directly 2) merge queries.
-        Query rQuery = session.createQuery("from HYK where id = :hykid");
-        rQuery.setParameter("hykid", hykid);
-        rQuery.setMaxResults(1);
-        HYK hyk = (HYK) rQuery.uniqueResult();
-
-        double flowDir = hyk.getRiver().getKmUp() ? 1 : -1;
-
-        List<HYKFormation> forms = getHYKFormations(hykid, km, flowDir);
-        List<Zone>         zones = new ArrayList<Zone>();
-
-        // Take the first one.
-        if (forms.size() >= 1) {
-            HYKFormation form = forms.get(0);
-            // Create respective zones.
-            for (HYKFlowZone flowZone: form.getZones()) {
-                Zone z = new Zone(flowZone.getA().doubleValue(),
-                    flowZone.getB().doubleValue(),
-                    flowZone.getType().getName());
-                zones.add(z);
-            }
-        }
-
-        return zones;
-    }
-
-
-    protected static List<HYKFormation> getHYKFormations(
-        int        hykid,
-        double     km,
-        double     flowDir
-    ) {
-        Session session = SessionHolder.HOLDER.get();
-
-        String SQL = "SELECT " +
-            "   f.id          AS FID, " +
-            "   f.distance_vl AS DIST, " +
-            "   e.hyk_id      AS HID, " +
-            "   e.km          AS KM " +
-            " FROM hyk_formations f INNER JOIN hyk_entries e " +
-            "   ON e.id = f.hyk_entry_id " +
-            " WHERE e.hyk_id = :hykid " +
-            "   AND :km between " +
-            "     LEAST(e.km, e.km + :flowDir*(f.distance_vl/1000.0+0.001)) " +
-            "   AND " +
-            "     GREATEST(e.km, e.km + :flowDir*(f.distance_vl/1000.0+0.001))";
-
-        SQLQuery sqlQuery = session.createSQLQuery(SQL)
-            .addScalar("FID", StandardBasicTypes.INTEGER)
-            .addScalar("DIST", StandardBasicTypes.DOUBLE)
-            .addScalar("HID", StandardBasicTypes.INTEGER)
-            .addScalar("KM", StandardBasicTypes.DOUBLE);
-
-        sqlQuery.setInteger("hykid", hykid);
-        sqlQuery.setDouble("flowDir", flowDir);
-        sqlQuery.setDouble("km", km);
-
-        logger.debug("HYK SQL: " + sqlQuery.getQueryString());
-
-        List<Object[]> results = sqlQuery.list();
-
-        logger.debug("Found " + results.size() + " HYKFormation IDs in DB.");
-
-        if (results == null || results.isEmpty()) {
-            logger.debug("No HYK found for ID " + hykid + " at km " + km);
-            return new ArrayList<HYKFormation>();
-        }
-
-        Object[] resultSet      = results.get(0);
-        Integer  hykFormationId = (Integer) resultSet[0];
-
-        Query query = session.createQuery("from HYKFormation where id = :id");
-        query.setParameter("id", hykFormationId);
-        query.setMaxResults(1);
-
-        return query.list();
-    }
-
-
-    /** Labeled section. */
-    public static class Zone implements Serializable {
-        /** Lower end of segment. */
-        protected double  from;
-        /** Upper end of segment. */
-        protected double  to;
-        /** The label. */
-        protected String name;
-
-        /** Constructor for labelled section. */
-        public Zone (double from, double to, String name) {
-            this.from = from;
-            this.to   = to;
-            this.name = name;
-        }
-
-        /** Get upper value. */
-        public double getTo() {
-            return to;
-        }
-
-        /** Get lower value. */
-        public double getFrom() {
-            return from;
-        }
-
-        /** Get name (type). */
-        public String getName() {
-            return name;
-        }
-    } // public static class Zone
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/HistoricalDischargeCurveFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,48 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class HistoricalDischargeCurveFacet extends DataFacet {
-
-    public HistoricalDischargeCurveFacet() {
-    }
-
-    public HistoricalDischargeCurveFacet(int index, String name,
-        String description, ComputeType type, String stateID, String hash
-
-    ) {
-        super(index, name, description, type, hash, stateID);
-    }
-
-    @Override
-    public Facet deepCopy() {
-        WaterlevelFacet copy = new WaterlevelFacet();
-        copy.set(this);
-        copy.type = type;
-        copy.hash = hash;
-        copy.stateId = stateId;
-        return copy;
-    }
-
-    @Override
-    public Object getData(Artifact artifact, CallContext context) {
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-
-        CalculationResult res = (CalculationResult) flys.compute(context, hash,
-            stateId, type, false);
-
-        HistoricalDischargeData data = (HistoricalDischargeData) res.getData();
-        WQKms[] wqkms = (WQKms[]) data.getWQs();
-
-        return wqkms[index];
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/HistoricalDischargeData.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.io.Serializable;
-
-
-public class HistoricalDischargeData implements Serializable {
-
-    private WQKms[] wqs;
-    private WQTimerange[] wqTimeranges;
-
-    public HistoricalDischargeData() {
-    }
-
-    public HistoricalDischargeData(WQTimerange[] wqTimeranges, WQKms[] wqs) {
-        this.wqTimeranges = wqTimeranges;
-        this.wqs = wqs;
-    }
-
-    public WQTimerange[] getWQTimeranges() {
-        return wqTimeranges;
-    }
-
-    public WQ[] getWQs() {
-        return wqs;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/HistoricalDischargeDifferenceFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,63 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-
-/**
- * Difference of historical discharge curve to ...
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class HistoricalDischargeDifferenceFacet
-extends      HistoricalDischargeFacet
-{
-    private static final Logger logger =
-        Logger.getLogger(HistoricalDischargeDifferenceFacet.class);
-
-
-    public HistoricalDischargeDifferenceFacet(
-        int    index,
-        String name,
-        String desc
-    ) {
-        super(index, name, desc, ComputeType.ADVANCE, null, null);
-    }
-
-
-    public HistoricalDischargeDifferenceFacet(
-        int         index,
-        String      name,
-        String      description,
-        ComputeType type,
-        String      stateID,
-        String      hash
-
-    ) {
-        super(index, name, description, type, hash, stateID);
-    }
-
-
-    @Override
-    public Object getData(Artifact artifact, CallContext context) {
-        if (logger.isDebugEnabled()) {
-            logger.debug("Get data for historical discharge difference curves" +
-                " at index: " + index + " / stateId: " + stateId);
-        }
-
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-
-        CalculationResult res = (CalculationResult)
-            flys.compute(context, hash, stateId, type,  false);
-
-        HistoricalDischargeData data = (HistoricalDischargeData) res.getData();
-        WQTimerange[] wqts = (WQTimerange[]) data.getWQTimeranges();
-
-        return wqts[index];
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/HistoricalDischargeFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,74 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.artifactdatabase.state.Facet;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class HistoricalDischargeFacet extends DataFacet {
-
-    private static final Logger logger =
-        Logger.getLogger(HistoricalDischargeFacet.class);
-
-
-    public HistoricalDischargeFacet() {
-    }
-
-
-    public HistoricalDischargeFacet(int index, String name, String desc) {
-        super(index, name, desc, ComputeType.ADVANCE, null, null);
-    }
-
-
-    public HistoricalDischargeFacet(
-        int         index,
-        String      name,
-        String      description,
-        ComputeType type,
-        String      stateID,
-        String      hash
-
-    ) {
-        super(index, name, description, type, hash, stateID);
-    }
-
-
-    @Override
-    public Facet deepCopy() {
-        WaterlevelFacet copy = new WaterlevelFacet();
-        copy.set(this);
-        copy.type    = type;
-        copy.hash    = hash;
-        copy.stateId = stateId;
-        return copy;
-    }
-
-
-    @Override
-    public Object getData(Artifact artifact, CallContext context) {
-        if (logger.isDebugEnabled()) {
-            logger.debug("Get data for historical discharge curves at index: " +
-                index + " / stateId: " + stateId);
-        }
-
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-
-        CalculationResult res = (CalculationResult)
-            flys.compute(context, hash, stateId, type,  false);
-
-        HistoricalDischargeData data = (HistoricalDischargeData) res.getData();
-        WQTimerange[] wqts = (WQTimerange[]) data.getWQTimeranges();
-
-        return wqts[index];
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/HistoricalDischargeWQFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class HistoricalDischargeWQFacet extends DataFacet {
-
-    private double value;
-
-    public HistoricalDischargeWQFacet() {
-    }
-
-    public HistoricalDischargeWQFacet(int index, String name,
-        String description, ComputeType type, String hash, String stateId,
-        double value) {
-
-        super(index, name, description, type, hash, stateId);
-        this.value = value;
-    }
-
-    @Override
-    public Facet deepCopy() {
-        HistoricalDischargeWQFacet copy = new HistoricalDischargeWQFacet();
-        copy.set(this);
-        copy.value = value;
-        return copy;
-    }
-
-    @Override
-    public Object getData(Artifact artifact, CallContext context) {
-        return value;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/HistoricalWQKms.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,23 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import de.intevation.flys.model.TimeInterval;
-
-
-public class HistoricalWQKms extends WQKms {
-
-    private TimeInterval timeInterval;
-
-    public HistoricalWQKms() {
-    }
-
-    public HistoricalWQKms(double[] kms, double[] qs, double[] ws, String name,
-        TimeInterval timeInterval) {
-        super(kms, qs, ws, name);
-        this.timeInterval = timeInterval;
-    }
-
-    public TimeInterval getTimeInterval() {
-        return timeInterval;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/HistoricalWQTimerange.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,86 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import gnu.trove.TDoubleArrayList;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-
-/**
- * A subclass of WQTimerange that stores besides W, Q and Timerange values
- * another double value (difference to something).
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class HistoricalWQTimerange extends WQTimerange {
-
-    public static class HistoricalTimerangeItem extends TimerangeItem {
-        public double diff;
-
-        public HistoricalTimerangeItem (Timerange timerange, double q, double w, double diff) {
-            super(timerange, q, w);
-            this.diff = diff;
-        }
-
-        public double[] get(double[] wq) {
-            if (wq.length >= 3) {
-                wq[0] = w;
-                wq[1] = q;
-                wq[2] = diff;
-            }
-            else if (wq.length >= 2) {
-                return super.get(wq);
-            }
-
-            return wq;
-        }
-    }
-
-    protected TDoubleArrayList diffs;
-
-
-    public HistoricalWQTimerange(String name) {
-        super(name);
-
-        diffs = new TDoubleArrayList();
-    }
-
-
-    public void add(double w, double q, double diff, Timerange t) {
-        ws.add(w);
-        qs.add(q);
-        timeranges.add(t);
-        diffs.add(diff);
-    }
-
-
-    /**
-     * This method requires a 3dim double array for <i>res</i>!
-     */
-    @Override
-    public double[] get(int idx, double[] res) {
-        res[0] = ws.getQuick(idx);
-        res[1] = qs.getQuick(idx);
-        res[2] = diffs.getQuick(idx);
-
-        return res;
-    }
-
-
-    public double[] getDiffs() {
-        return diffs.toNativeArray();
-    }
-
-    @Override
-    public List<TimerangeItem> sort() {
-        ArrayList<TimerangeItem> items = new ArrayList<TimerangeItem>(timeranges.size());
-        for (int i = 0, n = size(); i < n; i++) {
-            items.add(new HistoricalTimerangeItem(getTimerange(i), getQ(i), getW(i), diffs.get(i)));
-        }
-
-        Collections.sort(items);
-        return items;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/InfoldingColumns.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,78 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-/** A pretty naive pointwise algorithm to find out the columns
- *  of a WSTValueTable which imfold ("umhuellen") a set of WQKMs
- *  in terms of Q.
- *  A better implemention would exploit the fact that the
- *  Qs normally are constant for a while along km. This would
- *  reduce the runtime complexity to only a few Q spans instead
- *  of the pointwise evaluation.
- */
-public class InfoldingColumns
-{
-    private QRangeTree.QuickQFinder [] qFinders;
-
-    private boolean [] infoldingColumns;
-
-    public InfoldingColumns() {
-    }
-
-    public InfoldingColumns(WstValueTable.Column [] columns) {
-
-        qFinders = new QRangeTree.QuickQFinder[columns.length];
-        for (int i = 0; i < qFinders.length; ++i) {
-            qFinders[i] = columns[i].getQRangeTree().new QuickQFinder();
-        }
-
-        infoldingColumns = new boolean[columns.length];
-    }
-
-    public boolean [] getInfoldingColumns() {
-        return infoldingColumns;
-    }
-
-    public void markInfoldingColumns(QKms [] qkms) {
-        for (QKms qk: qkms) {
-            markInfoldingColumns(qk);
-        }
-    }
-
-    public void markInfoldingColumns(QKms qkms) {
-        int N = qkms.size();
-        int C = qFinders.length-1;
-        for (int i = 0; i < N; ++i) {
-            double km       = qkms.getKm(i);
-            double q        = qkms.getQ(i);
-            double above    =  Double.MAX_VALUE;
-            double below    = -Double.MAX_VALUE;
-            int    aboveIdx = -1;
-            int    belowIdx = -1;
-
-            for (int j = C; j >= 0; --j) {
-                double qc = qFinders[j].findQ(km);
-                if (Double.isNaN(qc)) {
-                    continue;
-                }
-                if (qc <= q) {
-                    if (qc > below) {
-                        below    = qc;
-                        belowIdx = j;
-                    }
-                }
-                else if (qc < above) { // qc > q
-                    above    = qc;
-                    aboveIdx = j;
-                }
-            }
-
-            if (aboveIdx != -1) {
-                infoldingColumns[aboveIdx] = true;
-            }
-
-            if (belowIdx != -1) {
-                infoldingColumns[belowIdx] = true;
-            }
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/LayerInfo.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,163 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-
-public class LayerInfo {
-
-    protected String name;
-    protected String type;
-    protected String directory;
-    protected String data;
-    protected String connection;
-    protected String connectionType;
-    protected String extent;
-    protected String srid;
-    protected String group;
-    protected String groupTitle;
-    protected String title;
-    protected String style;
-    protected String filter;
-    protected String labelItem;
-
-
-    public LayerInfo() {
-    }
-
-
-    public void setName(String name) {
-        this.name = name;
-    }
-
-
-    public String getName() {
-        return name;
-    }
-
-
-    public void setType(String type) {
-        this.type = type;
-    }
-
-
-    public String getType() {
-        return type;
-    }
-
-
-    public void setDirectory(String directory) {
-        this.directory = directory;
-    }
-
-
-    public String getDirectory() {
-        return directory;
-    }
-
-
-    public void setData(String data) {
-        this.data = data;
-    }
-
-
-    public String getData() {
-        return data;
-    }
-
-
-    public void setConnection(String connection) {
-        this.connection = connection;
-    }
-
-
-    public String getConnection() {
-        return connection;
-    }
-
-
-    public void setConnectionType(String connectionType) {
-        this.connectionType = connectionType;
-    }
-
-
-    public String getConnectionType() {
-        return connectionType;
-    }
-
-
-    public void setGroup(String group) {
-        this.group = group;
-    }
-
-
-    public String getGroup() {
-        return group;
-    }
-
-
-    public void setGroupTitle(String groupTitle) {
-        this.groupTitle = groupTitle;
-    }
-
-
-    public String getGroupTitle() {
-        return groupTitle;
-    }
-
-
-    public void setTitle(String title) {
-        this.title = title;
-    }
-
-
-    public String getTitle() {
-        return title;
-    }
-
-
-    public void setExtent(String extent) {
-        this.extent = extent;
-    }
-
-
-    public String getExtent() {
-        return extent;
-    }
-
-
-    public void setSrid(String srid) {
-        this.srid = srid;
-    }
-
-
-    public String getSrid() {
-        return srid;
-    }
-
-
-    public void setStyle(String style) {
-        this.style = style;
-    }
-
-
-    public String getStyle() {
-        return style;
-    }
-
-
-    public void setFilter(String filter) {
-        this.filter = filter;
-    }
-
-
-    public String getFilter() {
-        return filter;
-    }
-
-    public void setLabelItem(String labelItem) {
-        this.labelItem = labelItem;
-    }
-
-    public String getLabelItem() {
-        return labelItem;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/LocationProvider.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,103 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import net.sf.ehcache.Cache;
-import net.sf.ehcache.Element;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.flys.model.Annotation;
-import de.intevation.flys.model.FastAnnotations;
-
-import de.intevation.flys.artifacts.cache.CacheFactory;
-
-
-/** Make FastAnnotations (db unbound) available. */
-public class LocationProvider {
-
-    private static final Logger log =
-        Logger.getLogger(LocationProvider.class);
-
-
-    public static final String CACHE_KEY = "location-provider";
-
-    public static final String PREFIX = "lp-";
-
-
-    private LocationProvider() {
-    }
-
-    public static String getLocation(String river, double km) {
-
-        FastAnnotations fas = getAnnotations(river, km);
-
-        FastAnnotations.Annotation an = fas.findByKm(km);
-
-        return an != null ? an.getPosition() : null;
-    }
-
-    public static FastAnnotations getAnnotations(String river) {
-        return getAnnotations(river, Double.MAX_VALUE);
-    }
-
-    protected static FastAnnotations getAnnotations(String river, double km) {
-        // TODO issue880: Make annotations available _per type_
-
-        Cache cache = CacheFactory.getCache(CACHE_KEY);
-
-        if (cache == null) {
-            return uncachedAnnotations(river, km);
-        }
-
-        String key = PREFIX + river;
-
-        Element element = cache.get(key);
-
-        if (element != null) {
-            return (FastAnnotations)element.getValue();
-        }
-
-        FastAnnotations fas = uncachedAnnotations(river, Double.MAX_VALUE);
-
-        cache.put(new Element(key, fas));
-
-        return fas;
-    }
-
-    protected static FastAnnotations uncachedAnnotations(
-        String river,
-        double km
-    ) {
-        if (km != Double.MAX_VALUE) {
-            // XXX Fake it by using a standard Annotation.
-
-            Annotation annotation =
-                AnnotationsFactory.getAnnotation(river, km);
-
-            if (annotation != null) {
-                FastAnnotations.Annotation fa =
-                    new FastAnnotations.Annotation(
-                        km, Double.NaN,
-                        annotation.getPosition().getValue(), null, null,
-                        Double.NaN, Double.NaN);
-                return new FastAnnotations(
-                    new FastAnnotations.Annotation [] { fa });
-            }
-
-            return new FastAnnotations(new FastAnnotations.Annotation[0]);
-        }
-
-        long startTime = System.currentTimeMillis();
-
-        FastAnnotations fas = new FastAnnotations(river);
-
-        long stopTime = System.currentTimeMillis();
-
-        if (log.isDebugEnabled()) {
-            log.debug("Loading locations took " +
-                (stopTime-startTime)/1000f + " secs.");
-        }
-
-        return fas;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/MainValuesQFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,154 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.DataProvider;
-
-import de.intevation.artifactdatabase.state.DefaultFacet;
-
-import de.intevation.flys.artifacts.MainValuesArtifact;
-import de.intevation.flys.artifacts.math.Linear;
-import de.intevation.flys.jfree.FLYSAnnotation;
-import de.intevation.flys.jfree.StickyAxisAnnotation;
-
-import de.intevation.flys.exports.DurationCurveGenerator;
-
-
-/**
- * Facet to show Main Q Values.
- * TODO Join with W implementation.
- */
-public class MainValuesQFacet
-extends      DefaultFacet
-implements   FacetTypes {
-
-    /** Own logger. */
-    private static Logger logger = Logger.getLogger(MainValuesQFacet.class);
-
-    /** Do we want MainValues at Gauge (not interpolated)? */
-    protected boolean isAtGauge;
-
-
-    /** Trivial Constructor. */
-    public MainValuesQFacet(String name, String description, boolean atGauge) {
-        this.description = description;
-        this.name        = name;
-        this.index       = 0;
-        this.isAtGauge   = atGauge;
-    }
-
-
-    /**
-     * Set the hit-point in Q where a line drawn from the axis would hit the
-     * curve in WQDay (if hit).
-     * Employ linear interpolation.
-     */
-    protected static void setHitPoint(WQDay wqday, StickyAxisAnnotation annotation) {
-        int idx = 0;
-        float q = annotation.getPos();
-        boolean qIncreases = wqday.getQ(0) < wqday.getQ(wqday.size()-1);
-        if (qIncreases) {
-            while (idx < wqday.size() && wqday.getQ(idx) < q) {
-                idx++;
-            }
-        }
-        else {
-            idx = wqday.size() -1;
-            while (idx > 0 && wqday.getQ(idx) > q) {
-                idx--;
-            }
-        }
-
-        double day = 0d;
-        int mod = (qIncreases) ? -1 : +1;
-        if (idx != 0 && idx <= wqday.size()-1) {
-            day = Linear.linear(q, wqday.getQ(idx +mod), wqday.getQ(idx),
-                wqday.getDay(idx+mod), wqday.getDay(idx));
-            annotation.setHitPoint((float)day);
-        }
-        else {
-            logger.debug("StickyAnnotation does not hit wqday curve");
-        }
-    }
-
-
-    /**
-     * Returns the data this facet requires.
-     *
-     * @param artifact the owner artifact.
-     * @param context  the CallContext (ignored).
-     *
-     * @return the data.
-     */
-    @Override
-    public Object getData(Artifact artifact, CallContext context) {
-        MainValuesArtifact mvArtifact = (MainValuesArtifact) artifact;
-
-        List<NamedDouble>          qs = mvArtifact.getMainValuesQ(isAtGauge);
-        List<StickyAxisAnnotation> xy = new ArrayList<StickyAxisAnnotation>();
-
-        WQDay wqdays = null;
-        List<DataProvider> providers = context.
-            getDataProvider(DurationCurveFacet.BB_DURATIONCURVE);
-        if (providers.size() < 1) {
-            logger.warn("Could not find durationcurve data provider.");
-        }
-        else {
-            wqdays = (WQDay) providers.get(0).provideData(
-                DurationCurveFacet.BB_DURATIONCURVE,
-                null,
-                context);
-        }
-
-        // Rather specific case, Q-Annotations at a maybe second yaxis.
-        StickyAxisAnnotation annotation = null;
-        if (this.name.equals(DURATION_MAINVALUES_Q)) {
-            for (NamedDouble q: qs) {
-                annotation =
-                    new StickyAxisAnnotation(
-                        q.getName(),
-                        (float) q.getValue(),
-                        StickyAxisAnnotation.SimpleAxis.Y_AXIS,
-                        DurationCurveGenerator.YAXIS.Q.idx);
-                xy.add(annotation);
-                if (wqdays != null) {
-                    setHitPoint(wqdays, annotation);
-                }
-            }
-        }
-        else {
-            for (NamedDouble q: qs) {
-                annotation =
-                    new StickyAxisAnnotation(
-                        q.getName(),
-                        (float) q.getValue(),
-                        StickyAxisAnnotation.SimpleAxis.X_AXIS);
-                xy.add(annotation);
-                if (wqdays != null) {
-                    setHitPoint(wqdays, annotation);
-                }
-            }
-        }
-
-        return new FLYSAnnotation(description, xy);
-    }
-
-
-    /**
-     * Create a deep copy of this Facet.
-     * @return a deep copy.
-     */
-    @Override
-    public MainValuesQFacet deepCopy() {
-        MainValuesQFacet copy = new MainValuesQFacet(this.name,
-            description, this.isAtGauge);
-        copy.set(this);
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/MainValuesWFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,133 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.DataProvider;
-
-import de.intevation.artifactdatabase.state.DefaultFacet;
-
-import de.intevation.flys.artifacts.MainValuesArtifact;
-import de.intevation.flys.artifacts.math.Linear;
-import de.intevation.flys.jfree.FLYSAnnotation;
-import de.intevation.flys.jfree.StickyAxisAnnotation;
-
-
-/**
- * Facet to show Main W Values.
- */
-public class MainValuesWFacet
-extends      DefaultFacet
-implements   FacetTypes {
-
-    /** Own logger. */
-    private static Logger logger = Logger.getLogger(MainValuesWFacet.class);
-
-    /** Do we want MainValues at Gauge (not interpolated)? */
-    protected boolean isAtGauge;
-
-    /** Trivial Constructor. */
-    public MainValuesWFacet(String name, String description, boolean atGauge) {
-        this.description = description;
-        this.name = name;
-        this.index = 0;
-        this.isAtGauge = atGauge;
-    }
-
-
-    /**
-     * Set the hit-point in W where a line drawn from the axis would hit the
-     * curve in WQDay (if hit).
-     * Employ linear interpolation.
-     */
-    protected static void setHitPoint(WQDay wqday, StickyAxisAnnotation annotation) {
-        int idx = 0;
-        float w = annotation.getPos();
-        boolean wIncreases = wqday.getW(0) < wqday.getW(wqday.size()-1);
-        if (wIncreases) {
-            while (idx < wqday.size() && wqday.getW(idx) < w) {
-                idx++;
-            }
-        }
-        else {
-            idx = wqday.size() -1;
-            while (idx > 0 && wqday.getW(idx) > w) {
-                idx--;
-            }
-        }
-
-        double day = 0d;
-        int mod = (wIncreases) ? -1 : +1;
-        if (idx != 0 && idx < wqday.size()-1-mod) {
-            day = Linear.linear(w, wqday.getW(idx +mod), wqday.getW(idx),
-                wqday.getDay(idx+mod), wqday.getDay(idx));
-            annotation.setHitPoint((float)day);
-        }
-        else {
-            logger.debug("StickyAnnotation does not hit wqday curve");
-        }
-    }
-
-
-    /**
-     * Returns the data this facet provides.
-     *
-     * @param artifact the owner artifact.
-     * @param context  the CallContext (ignored).
-     *
-     * @return the data.
-     */
-    @Override
-    public Object getData(Artifact artifact, CallContext context) {
-        MainValuesArtifact mvArtifact = (MainValuesArtifact) artifact;
-
-        List<NamedDouble> ws = mvArtifact.getMainValuesW(isAtGauge);
-        List<StickyAxisAnnotation> xy = new ArrayList<StickyAxisAnnotation>();
-
-        // Find whether a duration curve is on the blackboard.
-        WQDay wqdays = null;
-        List<DataProvider> providers = context.
-            getDataProvider(DurationCurveFacet.BB_DURATIONCURVE);
-        if (providers.size() < 1) {
-            logger.warn("Could not find durationcurve data provider.");
-        }
-        else {
-            wqdays = (WQDay) providers.get(0).provideData(
-                DurationCurveFacet.BB_DURATIONCURVE,
-                null,
-                context);
-        }
-
-        for (NamedDouble w: ws) {
-            StickyAxisAnnotation annotation =
-                new StickyAxisAnnotation(
-                    w.getName(),
-                    (float) w.getValue(),
-                    StickyAxisAnnotation.SimpleAxis.Y_AXIS);
-            xy.add(annotation);
-            if (wqdays != null) {
-                setHitPoint(wqdays, annotation);
-            }
-        }
-
-        return new FLYSAnnotation(description, xy);
-    }
-
-
-    /**
-     * Create a deep copy of this Facet.
-     * @return a deep copy.
-     */
-    @Override
-    public MainValuesWFacet deepCopy() {
-        MainValuesWFacet copy = new MainValuesWFacet(this.name,
-            description, this.isAtGauge);
-        copy.set(this);
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/ManagedDomFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,174 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-
-import de.intevation.artifacts.ArtifactNamespaceContext;
-
-
-/**
- * Use an Element (DOM) to store the information about a facet.
- * The intent of this facet type is to represent a facet
- * stored in an Collection attribute. Different facets can have different
- * attributes that we need to parse, but the only thing ManagedFacets need
- * to do, is to adjust the attributes "active" and "position". So, those
- * values are set directly on the Element, the other attributes aren't
- * touched.
- */
-public class ManagedDomFacet extends ManagedFacet {
-
-    protected Element facet;
-
-    public ManagedDomFacet(Element facet) {
-        super(null, -1, null, null, -1, -1, -1);
-
-        this.facet = facet;
-    }
-
-
-    @Override
-    public int getIndex() {
-        if (this.index < 0) {
-            String index = facet.getAttributeNS(
-                ArtifactNamespaceContext.NAMESPACE_URI, "index");
-
-            if (index != null && index.length() > 0) {
-                this.index = Integer.parseInt(index);
-            }
-        }
-
-        return this.index;
-    }
-
-
-    @Override
-    public String getName() {
-        if (this.name == null || this.name.length() == 0) {
-            String name = facet.getAttributeNS(
-                ArtifactNamespaceContext.NAMESPACE_URI, "facet");
-
-            this.name = name;
-        }
-
-        return this.name;
-    }
-
-
-    @Override
-    public String getDescription() {
-        if (this.description == null || this.description.length() == 0) {
-            String description = facet.getAttributeNS(
-                ArtifactNamespaceContext.NAMESPACE_URI, "description");
-
-            this.description = description;
-        }
-
-        return this.description;
-    }
-
-
-    @Override
-    public int getPosition() {
-        if (this.position < 0) {
-            String position = facet.getAttributeNS(
-                ArtifactNamespaceContext.NAMESPACE_URI,
-                "pos");
-
-            if (position != null && position.length() > 0) {
-                this.position = Integer.parseInt(position);
-            }
-        }
-
-        return this.position;
-    }
-
-
-    @Override
-    public void setPosition(int position) {
-        this.position = position;
-
-        // TODO Evaluate whether other set/getAttributes also need
-        // to use the NAMESPACE_PREFIX.
-        facet.setAttributeNS(
-            ArtifactNamespaceContext.NAMESPACE_URI,
-            ArtifactNamespaceContext.NAMESPACE_PREFIX + ":" + "pos",
-            String.valueOf(position));
-    }
-
-
-    @Override
-    public int getActive() {
-        if (this.active < 0) {
-            String active = facet.getAttributeNS(
-                ArtifactNamespaceContext.NAMESPACE_URI, "active");
-
-            if (active != null && active.length() > 0) {
-                this.active = Integer.parseInt(active);
-            }
-        }
-
-        return this.active;
-    }
-
-
-    @Override
-    public void setActive(int active) {
-        this.active = active;
-
-        facet.setAttributeNS(
-            ArtifactNamespaceContext.NAMESPACE_URI,
-            "art:active",
-            String.valueOf(active));
-    }
-
-
-    @Override
-    public int getVisible() {
-        if (this.visible < 0) {
-            String visible = facet.getAttributeNS(
-                ArtifactNamespaceContext.NAMESPACE_URI, "visible");
-
-            if (visible != null && visible.length() > 0) {
-                this.visible = Integer.parseInt(visible);
-            }
-        }
-
-        return this.visible;
-    }
-
-
-    @Override
-    public void setVisible(int visible) {
-        this.visible = visible;
-
-        facet.setAttributeNS(
-            ArtifactNamespaceContext.NAMESPACE_URI,
-            "visible",
-            String.valueOf(getVisible()));
-    }
-
-
-    @Override
-    public String getArtifact() {
-        if (this.uuid == null || this.uuid.length() == 0) {
-            String uuid = facet.getAttributeNS(
-                ArtifactNamespaceContext.NAMESPACE_URI, "artifact");
-
-            this.uuid = uuid;
-        }
-
-        return this.uuid;
-    }
-
-
-    /**
-     * Import into document.
-     * @param doc Document to be imported to.
-     */
-    @Override
-    public Node toXML(Document doc) {
-        return doc.importNode(facet, true);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/ManagedFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,130 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-
-import de.intevation.artifactdatabase.state.DefaultFacet;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifacts.ArtifactNamespaceContext;
-import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
-
-
-/**
- * Facet with user-supplied theme-control-information (pos in list,
- * active/disabled etc) attached.
- */
-public class ManagedFacet extends DefaultFacet implements Comparable {
-
-    /** The uuid of the owner artifact. */
-    protected String uuid;
-
-    /** A property that determines the position of this facet. */
-    protected int position;
-
-    /** A property that determines if this facet is active or not. */
-    protected int active;
-
-    /** A property that determines if this facet is visible or not. */
-    protected int visible;
-
-    public ManagedFacet() {
-    }
-
-    public ManagedFacet(String name, int index, String desc, String uuid,
-        int pos, int active, int visible) {
-        super(index, name, desc);
-
-        this.uuid = uuid;
-        this.position = pos;
-        this.active = active;
-        this.visible = visible;
-    }
-
-    /**
-     * Sets position (will be merged to position in ThemeList).
-     */
-    public void setPosition(int pos) {
-        this.position = pos;
-    }
-
-    public int getPosition() {
-        return position;
-    }
-
-    public void setActive(int active) {
-        this.active = active;
-    }
-
-    public int getActive() {
-        return active;
-    }
-
-    public void setVisible(int visible) {
-        this.visible = visible;
-    }
-
-    public int getVisible() {
-        return visible;
-    }
-
-    /**
-     * Get uuid of related artifact.
-     *
-     * @return uuid of related artifact.
-     */
-    public String getArtifact() {
-        return uuid;
-    }
-
-    public Node toXML(Document doc) {
-        ElementCreator ec = new ElementCreator(doc,
-            ArtifactNamespaceContext.NAMESPACE_URI,
-            ArtifactNamespaceContext.NAMESPACE_PREFIX);
-
-        Element facet = ec.create("theme");
-        ec.addAttr(facet, "artifact", getArtifact(), true);
-        ec.addAttr(facet, "facet", getName(), true);
-        ec.addAttr(facet, "pos", String.valueOf(getPosition()), true);
-        ec.addAttr(facet, "active", String.valueOf(getActive()), true);
-        ec.addAttr(facet, "index", String.valueOf(getIndex()), true);
-        ec.addAttr(facet, "description", getDescription(), true);
-        ec.addAttr(facet, "visible", String.valueOf(getVisible()), true);
-
-        return facet;
-    }
-
-    public void set(ManagedFacet other) {
-        uuid = other.uuid;
-        position = other.position;
-        active = other.active;
-    }
-
-    @Override
-    public Facet deepCopy() {
-        ManagedFacet copy = new ManagedFacet();
-        copy.set((DefaultFacet) this);
-        copy.set((ManagedFacet) this);
-        return copy;
-    }
-
-    @Override
-    public int compareTo(Object o) {
-        if (!(o instanceof ManagedFacet)) {
-            return -1;
-        }
-
-        ManagedFacet other = (ManagedFacet) o;
-
-        if (position < other.position) {
-            return -1;
-        }
-        else if (position > other.position) {
-            return 1;
-        }
-        else {
-            return 0;
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/ManagedFacetAdapter.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,73 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-
-import de.intevation.artifacts.ArtifactNamespaceContext;
-
-import de.intevation.artifactdatabase.state.DefaultFacet;
-import de.intevation.artifactdatabase.state.Facet;
-
-import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
-
-
-public class ManagedFacetAdapter extends ManagedFacet {
-
-    protected Facet facet;
-
-    public ManagedFacetAdapter() {
-    }
-
-
-    protected Logger logger = Logger.getLogger(ManagedFacetAdapter.class);
-
-    public ManagedFacetAdapter(
-        Facet   facet,
-        String  uuid,
-        int     pos,
-        int     active,
-        int     visible
-    ) {
-        super(
-            facet.getName(),
-            facet.getIndex(),
-            facet.getDescription(),
-            uuid,
-            pos,
-            active,
-            visible);
-
-        this.facet = facet;
-    }
-
-
-    @Override
-    public Node toXML(Document doc) {
-        ElementCreator ec = new ElementCreator(
-            doc,
-            ArtifactNamespaceContext.NAMESPACE_URI,
-            ArtifactNamespaceContext.NAMESPACE_PREFIX);
-
-        Element e = (Element) facet.toXML(doc);
-        ec.addAttr(e, "artifact", getArtifact(), true);
-        ec.addAttr(e, "facet", getName(), true);
-        ec.addAttr(e, "pos", String.valueOf(getPosition()), true);
-        ec.addAttr(e, "active", String.valueOf(getActive()), true);
-        ec.addAttr(e, "visible", String.valueOf(getVisible()), true);
-
-        return e;
-    }
-
-    @Override
-    public Facet deepCopy() {
-        ManagedFacetAdapter copy = new ManagedFacetAdapter();
-        copy.set((DefaultFacet)this);
-        copy.set((ManagedFacet)this);
-        copy.facet = facet.deepCopy();
-        return facet;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/ManualPointsFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.ManualPointsArtifact;
-
-import de.intevation.artifactdatabase.state.DefaultFacet;
-import de.intevation.artifactdatabase.state.Facet;
-
-
-/**
- * Facet to access ManualPoints that where added by user.
- */
-public class ManualPointsFacet
-extends      DefaultFacet
-{
-    /** Logger for this class. */
-    private static final Logger logger = Logger.getLogger(ManualPointsFacet.class);
-
-
-    /**
-     * Trivial Constructor.
-     */
-    public ManualPointsFacet() {
-    }
-
-
-    /**
-     * Trivial Constructor for a ManualPointsFacet.
-     *
-     * @param index       Database-Index to use.
-     * @param name        Name (~type) of Facet.
-     * @param description Description of Facet.
-     */
-    public ManualPointsFacet(int index, String name, String description) {
-        super(index, name, description);
-    }
-
-
-    /**
-     * Get List of ManualPoints for river from Artifact.
-     *
-     * @param artifact (ManualPoints-)Artifact to query for list of ManualPoints.
-     * @param context  Ignored.
-     */
-    @Override
-    public Object getData(Artifact artifact, CallContext context) {
-        ManualPointsArtifact pointsArtifact = (ManualPointsArtifact) artifact;
-        return pointsArtifact.getPointsData(this.name);
-    }
-
-
-    /** Do a deep copy. */
-    @Override
-    public Facet deepCopy() {
-        ManualPointsFacet copy = new ManualPointsFacet();
-        copy.set(this);
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/MapserverStyle.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,152 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.util.ArrayList;
-import java.util.List;
-
-
-public class MapserverStyle {
-
-    public static class Clazz {
-        protected List<ClazzItem> items;
-        protected String    name;
-
-        public Clazz(String name) {
-            this.name  = name;
-            this.items = new ArrayList<ClazzItem>();
-        }
-
-        public void addItem(ClazzItem item) {
-            if (item != null) {
-                items.add(item);
-            }
-        }
-
-        @Override
-        public String toString() {
-            StringBuilder sb = new StringBuilder();
-            sb.append("CLASS\n");
-            sb.append("NAME \"" + name + "\"\n");
-
-            for (ClazzItem item: items) {
-                item.toString(sb);
-            }
-
-            sb.append("END\n");
-
-            return sb.toString();
-        }
-    }
-
-    public interface ClazzItem {
-        void toString(StringBuilder sb);
-    }
-
-    public static class Style implements ClazzItem {
-        protected String color;
-        protected String outlinecolor;
-        protected String symbol;
-        protected int    size;
-
-        public void setColor(String color) {
-            this.color = color;
-        }
-
-        public void setOutlineColor(String outlinecolor) {
-            this.outlinecolor = outlinecolor;
-        }
-
-        public void setSize(int size) {
-            this.size = size;
-        }
-
-        public void setSymbol(String symbol) {
-            if (symbol != null && symbol.length() > 0) {
-                this.symbol = symbol;
-            }
-        }
-
-        public void toString(StringBuilder sb) {
-            sb.append("STYLE\n");
-            sb.append("WIDTH " + String.valueOf(size) + "\n");
-
-            if (outlinecolor != null) {
-                sb.append("OUTLINECOLOR " + outlinecolor + "\n");
-            }
-
-            if (color != null) {
-                sb.append("COLOR " + color + "\n");
-            }
-
-            if (symbol != null) {
-                sb.append("SYMBOL '" + symbol + "'\n");
-            }
-
-            sb.append("END\n");
-        }
-    } // end of Style
-
-    public static class Label implements ClazzItem {
-        protected String color;
-        protected int    size;
-
-        public void setColor(String color) {
-            this.color = color;
-        }
-
-        public void setSize(int size) {
-            this.size = size;
-        }
-
-        @Override
-        public void toString(StringBuilder sb) {
-            sb.append("LABEL\n");
-            sb.append("ANGLE auto\n");
-            sb.append("SIZE " + String.valueOf(size) + "\n");
-            sb.append("COLOR " + color + "\n");
-            sb.append("TYPE truetype\n");
-            sb.append("FONT DefaultFont\n");
-            sb.append("POSITION ur\n");
-            sb.append("OFFSET 2 2\n");
-            sb.append("END\n");
-        }
-    }
-
-    public static class Expression implements ClazzItem {
-        protected String value;
-
-        public Expression(String value) {
-            this.value = value;
-        }
-
-        @Override
-        public void toString(StringBuilder sb) {
-            sb.append("EXPRESSION " + value);
-            sb.append("\n");
-        }
-    }
-
-
-    protected List<Clazz> classes;
-
-
-    public MapserverStyle() {
-        classes = new ArrayList<Clazz>();
-    }
-
-    public void addClazz(Clazz clazz) {
-        if (clazz != null) {
-            classes.add(clazz);
-        }
-    }
-
-    public String toString() {
-        StringBuilder sb = new StringBuilder();
-
-        for (Clazz clazz: classes) {
-            sb.append(clazz.toString());
-        }
-
-        return sb.toString();
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/MiddleBedHeightCalculation.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,212 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.common.utils.DateUtils;
-
-import de.intevation.flys.model.BedHeightEpoch;
-import de.intevation.flys.model.BedHeightEpochValue;
-import de.intevation.flys.model.BedHeightSingle;
-import de.intevation.flys.model.BedHeightSingleValue;
-import de.intevation.flys.model.TimeInterval;
-
-import de.intevation.flys.artifacts.access.BedHeightAccess;
-
-
-public class MiddleBedHeightCalculation extends Calculation {
-
-    private static final Logger logger =
-        Logger.getLogger(MiddleBedHeightCalculation.class);
-
-
-    public CalculationResult calculate(BedHeightAccess access) {
-        logger.info("MiddleBedHeightCalculation.calculate");
-
-        int[] singleIds = access.getBedHeightSingleIDs();
-        int[] epochIds  = access.getBedHeightEpochIDs();
-
-
-        if (logger.isDebugEnabled()) {
-            Artifact artifact = access.getArtifact();
-
-            logger.debug("Artifact '" + artifact.identifier() + "' contains:");
-            if (singleIds != null) {
-                logger.debug("   " + singleIds.length + " single bedheight ids");
-            }
-
-            if (epochIds != null) {
-                logger.debug("   " + epochIds.length + " epoch bedheight ids");
-            }
-        }
-
-        List<BedHeightSingle> singles = getSingles(access, singleIds);
-        List<BedHeightEpoch>  epochs  = getEpochs(access, epochIds);
-
-        return buildCalculationResult(access, singles, epochs);
-    }
-
-
-    protected List<BedHeightSingle> getSingles(
-        BedHeightAccess access,
-        int[] ids
-    ) {
-        List<BedHeightSingle> singles = new ArrayList<BedHeightSingle>();
-
-        for (int id: ids) {
-            BedHeightSingle s = BedHeightSingle.getBedHeightSingleById(id);
-
-            if (s != null) {
-                singles.add(s);
-            }
-            else {
-                logger.warn("Cannot find Single by id: " + id);
-                // TODO ADD WARNING
-            }
-        }
-
-        return singles;
-    }
-
-
-    protected List<BedHeightEpoch> getEpochs(
-        BedHeightAccess access,
-        int[] ids
-    ) {
-        List<BedHeightEpoch> epochs = new ArrayList<BedHeightEpoch>();
-
-        for (int id: ids) {
-            BedHeightEpoch e = BedHeightEpoch.getBedHeightEpochById(id);
-
-            if (e != null) {
-                epochs.add(e);
-            }
-            else {
-                logger.warn("Cannot find Epoch by id: " + id);
-                // TODO ADD WARNING
-            }
-        }
-
-        return epochs;
-    }
-
-
-    protected CalculationResult buildCalculationResult(
-        BedHeightAccess       access,
-        List<BedHeightSingle> singles,
-        List<BedHeightEpoch>  epochs
-    ) {
-        logger.info("MiddleBedHeightCalculation.buildCalculationResult");
-
-        double kmLo = access.getLowerKM();
-        double kmHi = access.getUpperKM();
-
-        List<MiddleBedHeightData> data = new ArrayList<MiddleBedHeightData>();
-
-        for (BedHeightSingle single: singles) {
-            MiddleBedHeightData d = prepareSingleData(single, kmLo, kmHi);
-
-            if (d != null) {
-                data.add(d);
-            }
-        }
-
-        for (BedHeightEpoch epoch: epochs) {
-            MiddleBedHeightData d = prepareEpochData(epoch, kmLo, kmHi);
-
-            if (d != null) {
-                data.add(d);
-            }
-        }
-
-        logger.debug("Calculation results in " + data.size() + " data objects.");
-
-        return new CalculationResult((MiddleBedHeightData[])
-            data.toArray(new MiddleBedHeightData[data.size()]), this);
-    }
-
-
-    protected MiddleBedHeightData prepareSingleData(
-        BedHeightSingle single,
-        double kmLo,
-        double kmHi
-    ) {
-        logger.debug("Prepare data for single: " + single.getDescription());
-
-        List<BedHeightSingleValue> values =
-            BedHeightSingleValue.getBedHeightSingleValues(single, kmLo, kmHi);
-
-        MiddleBedHeightData data = new MiddleBedHeightData(
-            single.getYear(),
-            single.getYear(),
-            single.getEvaluationBy(),
-            single.getDescription());
-
-        for (BedHeightSingleValue value: values) {
-            if (value.getHeight() != null) {
-                data.addAll(value.getStation().doubleValue(),
-                    value.getHeight().doubleValue(),
-                    value.getUncertainty().doubleValue(),
-                    value.getSoundingWidth().doubleValue(),
-                    value.getDataGap().doubleValue(),
-                    value.getWidth().doubleValue(),
-                    false);
-             }
-            else {
-                data.addAll(value.getStation().doubleValue(),
-                    0,
-                    0,
-                    0,
-                    0,
-                    0,
-                    true);
-            }
-        }
-
-        logger.debug("Single contains " + values.size() + " values");
-
-        return data;
-    }
-
-
-    /** Create MiddleBedHeightData to return. */
-    protected MiddleBedHeightData prepareEpochData(
-        BedHeightEpoch epoch,
-        double kmLo,
-        double kmHi
-    ) {
-        logger.debug("Prepare data for epoch: " + epoch.getDescription());
-
-        TimeInterval ti = epoch.getTimeInterval();
-
-        List<BedHeightEpochValue> values =
-            BedHeightEpochValue.getBedHeightEpochValues(epoch, kmLo, kmHi);
-
-        MiddleBedHeightData data = new MiddleBedHeightData(
-            DateUtils.getYearFromDate(ti.getStartTime()),
-            DateUtils.getYearFromDate(ti.getStopTime()),
-            epoch.getEvaluationBy(),
-            epoch.getDescription()
-        );
-
-        for (BedHeightEpochValue value: values) {
-            data.addKM(value.getStation().doubleValue());
-            if (value.getHeight() != null) {
-                data.addMiddleHeight(value.getHeight().doubleValue());
-                data.addIsEmpty(false);
-            }
-            else {
-                data.addMiddleHeight(Double.NaN);
-                data.addIsEmpty(true);
-            }
-        }
-
-        logger.debug("Epoch contains " + values.size() + " values");
-
-        return data;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/MiddleBedHeightData.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,185 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.io.Serializable;
-
-import java.util.ArrayList;
-
-import gnu.trove.TDoubleArrayList;
-
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.resources.Resources;
-
-import org.apache.log4j.Logger;
-
-
-public class MiddleBedHeightData implements Serializable {
-
-    /** Very private logger. */
-    private static final Logger logger = Logger.getLogger(MiddleBedHeightData.class);
-
-    public static final String I18N_SINGLE_NAME = "facet.bedheight_middle.single";
-    public static final String I18N_EPOCH_NAME  = "facet.bedheight_middle.epoch";
-
-    private int    startYear;
-    private int    endYear;
-    private String evaluatedBy;
-    private String description;
-
-    private TDoubleArrayList km;
-    private TDoubleArrayList middleHeight;
-    private TDoubleArrayList uncertainty;
-    private TDoubleArrayList soundingWidth;
-    private TDoubleArrayList dataGap;
-    private TDoubleArrayList width;
-    private ArrayList empty;
-
-
-    protected MiddleBedHeightData(int start, int end, String eval, String desc) {
-        this.startYear   = start;
-        this.endYear     = end;
-        this.evaluatedBy = eval;
-        this.description = desc;
-
-        this.km            = new TDoubleArrayList();
-        this.middleHeight  = new TDoubleArrayList();
-        this.uncertainty   = new TDoubleArrayList();
-        this.soundingWidth = new TDoubleArrayList();
-        this.dataGap       = new TDoubleArrayList();
-        this.width         = new TDoubleArrayList();
-        this.empty         = new ArrayList();
-    }
-
-    public void addAll(double station, double height, double uncertainty,
-        double soundingWidth, double dataGap, double width, boolean isEmpty) {
-        addKM(station);
-        addMiddleHeight(height);
-        addUncertainty(uncertainty);
-        addSoundingWidth(soundingWidth);
-        addDataGap(dataGap);
-        addWidth(width);
-        addIsEmpty(isEmpty);
-    }
-
-
-    public int getStartYear() {
-        return startYear;
-    }
-
-    public int getEndYear() {
-        return endYear;
-    }
-
-    public String getEvaluatedBy() {
-        return evaluatedBy;
-    }
-
-    public String getDescription() {
-        return description;
-    }
-
-
-    protected void addKM(double km) {
-        this.km.add(km);
-    }
-
-    public double getKM(int idx) {
-        return km.get(idx);
-    }
-
-    protected void addMiddleHeight(double middleHeight) {
-        this.middleHeight.add(middleHeight);
-    }
-
-    public double getMiddleHeight(int idx) {
-        return middleHeight.get(idx);
-    }
-
-    protected void addUncertainty(double uncertainty) {
-        this.uncertainty.add(uncertainty);
-    }
-
-    public double getUncertainty(int idx) {
-        return uncertainty.get(idx);
-    }
-
-    protected void addSoundingWidth(double soundingWidth) {
-        this.soundingWidth.add(soundingWidth);
-    }
-
-    public double getSoundingWidth(int idx) {
-        return soundingWidth.get(idx);
-    }
-
-    protected void addDataGap(double gap) {
-        this.dataGap.add(gap);
-    }
-
-    public double getDataGap(int idx) {
-        return dataGap.get(idx);
-    }
-
-    protected void addIsEmpty(boolean empty) {
-        this.empty.add(empty);
-    }
-
-    public boolean isEmpty(int idx) {
-        return (Boolean) empty.get(idx);
-    }
-
-
-    protected void addWidth(double width) {
-        this.width.add(width);
-    }
-
-    public double getWidth(int idx) {
-        return width.get(idx);
-    }
-
-    public int size() {
-        return km.size();
-    }
-
-
-    /**
-     * Get the points, ready to be drawn
-     * @return [[km1, km2,...],[height1,height2,...]]
-     */
-    public double[][] getMiddleHeightsPoints() {
-        double[][] points = new double[2][size()];
-
-        for (int i = 0, n = size(); i < n; i++) {
-            if (isEmpty(i)) {
-                points[0][i] = getKM(i);
-                points[1][i] = Double.NaN;
-            }
-            else {
-                points[0][i] = getKM(i);
-                points[1][i] = getMiddleHeight(i);
-            }
-        }
-
-        return points;
-    }
-
-
-    public String getSoundingName(CallContext context) {
-        if (getStartYear() == getEndYear()) {
-            return Resources.getMsg(
-                context.getMeta(),
-                I18N_SINGLE_NAME,
-                I18N_SINGLE_NAME,
-                new Object[] { getStartYear() }
-            );
-        }
-        else {
-            return Resources.getMsg(
-                context.getMeta(),
-                I18N_EPOCH_NAME,
-                I18N_EPOCH_NAME,
-                new Object[] { getStartYear(), getEndYear() }
-            );
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/MiddleBedHeightFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,65 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import de.intevation.artifactdatabase.state.Facet;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-import org.apache.log4j.Logger;
-
-
-/**
- * Facet of a MiddleBedHeight curve.
- */
-public class MiddleBedHeightFacet extends DataFacet {
-
-    private static Logger logger = Logger.getLogger(MiddleBedHeightFacet.class);
-
-
-    public MiddleBedHeightFacet() {
-        // required for clone operation deepCopy()
-    }
-
-
-    public MiddleBedHeightFacet(
-        int         idx,
-        String      name,
-        String      description,
-        ComputeType type,
-        String      stateId,
-        String      hash
-    ) {
-        super(idx, name, description, type, hash, stateId);
-    }
-
-
-    public Object getData(Artifact artifact, CallContext context) {
-        logger.debug("Get data for middle bed height at index: " + index);
-
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-
-        CalculationResult res = (CalculationResult)
-            flys.compute(context, hash, stateId, type, false);
-
-        MiddleBedHeightData[] data = (MiddleBedHeightData[]) res.getData();
-
-        return data[index];
-    }
-
-
-    /** Copy deeply. */
-    @Override
-    public Facet deepCopy() {
-        MiddleBedHeightFacet copy = new MiddleBedHeightFacet();
-        copy.set(this);
-        copy.type    = type;
-        copy.hash    = hash;
-        copy.stateId = stateId;
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/Module.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,25 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-/**
- * Represents a Module as is is loaded from the config
- */
-public class Module {
-
-    private String name;
-    private boolean selected;
-
-    public Module(String name, boolean selected) {
-        this.name = name;
-        this.selected = selected;
-    }
-
-    public String getName() {
-        return this.name;
-    }
-
-    public boolean isSelected() {
-        return this.selected;
-    }
-}
-
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 tw=80:
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/NamedDouble.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,30 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-/**
- * Implementation of a <String,double> pair.
- */
-public class NamedDouble
-extends      NamedObjectImpl
-{
-    protected double value;
-
-
-    /**
-     * @param name  name for the given value.
-     * @param value value.
-     */
-    public NamedDouble(String name, double value) {
-        super(name);
-        this.value = value;
-    }
-
-
-    /**
-     * Get the value.
-     * @return the value.
-     */
-    public double getValue() {
-        return this.value;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/NamedObject.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,19 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.io.Serializable;
-
-
-/**
- * This class represents an object that has a name. The default case would be to
- * inherit from this class.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public interface NamedObject
-extends          Serializable
-{
-    void setName(String name);
-
-    String getName();
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/NamedObjectImpl.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,28 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-public class NamedObjectImpl
-implements   NamedObject
-{
-    /** The name of this object.*/
-    protected String name;
-
-    public NamedObjectImpl() {
-    }
-
-    public NamedObjectImpl(String name) {
-        this.name = name;
-    }
-
-
-    @Override
-    public void setName(String name) {
-        this.name = name;
-    }
-
-
-    @Override
-    public String getName() {
-        return name;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/Parameters.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,336 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import de.intevation.flys.artifacts.math.Linear;
-
-import de.intevation.flys.utils.DoubleUtil;
-
-import gnu.trove.TDoubleArrayList;
-
-import java.io.Serializable;
-
-import org.apache.log4j.Logger;
-
-public class Parameters
-implements   Serializable
-{
-    private static Logger log = Logger.getLogger(Parameters.class);
-
-    public interface Visitor {
-
-        void visit(double [] row);
-
-    } // interface Visitor
-
-    public static final double EPSILON = 1e-4;
-
-    protected String []           columnNames;
-    protected TDoubleArrayList [] columns;
-
-    public Parameters() {
-    }
-
-    public Parameters(String [] columnNames) {
-        if (columnNames == null || columnNames.length < 1) {
-            throw new IllegalArgumentException("columnNames too short.");
-        }
-        this.columnNames = columnNames;
-        columns = new TDoubleArrayList[columnNames.length];
-        for (int i = 0; i < columns.length; ++i) {
-            columns[i] = new TDoubleArrayList();
-        }
-    }
-
-    public int columnIndex(String name) {
-        for (int i = 0; i < columnNames.length; ++i) {
-            if (columnNames[i].equals(name)) {
-                return i;
-            }
-        }
-        if (log.isDebugEnabled()) {
-            log.debug("columnIndex: " + name + " not found in columnNames");
-        }
-        return -1;
-    }
-
-    public int newRow() {
-
-        int N = columns[0].size();
-
-        for (int i = 0; i < columns.length; ++i) {
-            columns[i].add(Double.NaN);
-        }
-
-        return N;
-    }
-
-    public double get(int row, int index) {
-        return columns[index].getQuick(row);
-    }
-
-    public double get(int i, String columnName) {
-        int index = columnIndex(columnName);
-        return index >= 0
-            ? columns[index].getQuick(i)
-            : Double.NaN;
-    }
-
-    public void set(int row, int index, double value) {
-        columns[index].setQuick(row, value);
-    }
-
-    public void set(int i, String columnName, double value) {
-        int idx = columnIndex(columnName);
-        if (idx >= 0) {
-            columns[idx].setQuick(i, value);
-        }
-    }
-
-    public boolean set(int row, int [] indices, double [] values) {
-        boolean invalid = false;
-        for (int i = 0; i < indices.length; ++i) {
-            double v = values[i];
-            if (Double.isNaN(v)) {
-                invalid = true;
-            }
-            else {
-                columns[indices[i]].setQuick(row, v);
-            }
-        }
-        return invalid;
-    }
-
-    public boolean set(int row, String [] names, double [] values) {
-        boolean success = true;
-        for (int i = 0; i < names.length; ++i) {
-            int idx = columnIndex(names[i]);
-            if (idx >= 0) {
-                columns[idx].setQuick(row, values[i]);
-            }
-            else {
-                success = false;
-            }
-        }
-        return success;
-    }
-
-    public int size() {
-        return columns[0].size();
-    }
-
-    public int getNumberColumns() {
-        return columnNames.length;
-    }
-
-    public String [] getColumnNames() {
-        return columnNames;
-    }
-
-    public void removeNaNs() {
-        DoubleUtil.removeNaNs(columns);
-    }
-
-    public int [] columnIndices(String [] columns) {
-        int [] indices = new int[columns.length];
-        for (int i = 0; i < columns.length; ++i) {
-            indices[i] = columnIndex(columns[i]);
-        }
-        return indices;
-    }
-
-    public double getValue(int row, String column) {
-        int idx = columnIndex(column);
-        return idx >= 0
-            ? columns[idx].getQuick(row)
-            : Double.NaN;
-    }
-
-    public double [] get(int row, String [] columns) {
-        return get(row, columns, new double[columns.length]);
-    }
-
-    public double [] get(int row, String [] columns, double [] values) {
-        for (int i = 0; i < columns.length; ++i) {
-            int idx = columnIndex(columns[i]);
-            values[i] = idx < 0
-                ? Double.NaN
-                : this.columns[idx].getQuick(row);
-        }
-
-        return values;
-    }
-
-    public void get(int row, int [] columnIndices, double [] values) {
-        for (int i = 0; i < columnIndices.length; ++i) {
-            int index = columnIndices[i];
-            values[i] = index >= 0 && index < columns.length
-                ? columns[index].getQuick(row)
-                : Double.NaN;
-        }
-    }
-
-    public int binarySearch(String columnName, double value) {
-        return binarySearch(columnIndex(columnName), value);
-    }
-
-    /**
-     * Performes a binary search in the column identified by its
-     * index.
-     * @return Index of found element or negative insertion point (shifted by one)
-     */
-    public int binarySearch(int columnIndex, double value) {
-        TDoubleArrayList column = columns[columnIndex];
-        return column.binarySearch(value);
-    }
-
-    public int binarySearch(String columnName, double value, double epsilon) {
-        return binarySearch(columnIndex(columnName), value, epsilon);
-    }
-
-    public int binarySearch(int columnIndex, double value, double epsilon) {
-        if (epsilon < 0d) epsilon = -epsilon;
-        double vl = value - epsilon;
-        double vh = value + epsilon;
-
-        TDoubleArrayList column = columns[columnIndex];
-        int lo = 0, hi = column.size()-1;
-        while (hi >= lo) {
-            int mid = (lo + hi) >> 1;
-            double v = column.getQuick(mid);
-            if      (v < vl) lo = mid + 1;
-            else if (v > vh) hi = mid - 1;
-            else             return mid;
-        }
-
-        return -(lo + 1);
-    }
-
-    public double [] interpolate(int columnIndex, double key) {
-        return interpolate(columnIndex, key, new double[columns.length]);
-    }
-
-    public double [] interpolate(String columnName, double key) {
-        return interpolate(
-            columnIndex(columnName), key, new double[columns.length]);
-    }
-
-    public double [] interpolate(
-        String    columnName,
-        double    key,
-        double [] values
-    ) {
-        return interpolate(columnIndex(columnName), key, values);
-    }
-
-    public double [] interpolate(
-        int       columnIndex,
-        double    key,
-        double [] values
-    ) {
-        int row = binarySearch(columnIndex, key, EPSILON);
-
-        if (row >= 0) { // direct hit
-            for (int i = 0; i < values.length; ++i) {
-                values[i] = columns[i].getQuick(row);
-            }
-        }
-        else {
-            row = -row - 1;
-            if (row < 1 || row >= size()) {
-                return null;
-            }
-            double v1 = columns[columnIndex].getQuick(row-1);
-            double v2 = columns[columnIndex].getQuick(row);
-            double factor = Linear.factor(key, v1, v2);
-            for (int i = 0; i < values.length; ++i) {
-                values[i] = Linear.weight(
-                    factor,
-                    columns[i].getQuick(row-1),
-                    columns[i].getQuick(row));
-            }
-        }
-        return values;
-    }
-
-
-    public double [] interpolate(
-        String    keyName,
-        double    key,
-        String [] columnNames
-    ) {
-        int keyIndex = columnIndex(keyName);
-        return keyIndex < 0
-            ? null
-            : interpolate(keyIndex, key, columnNames);
-    }
-
-    public double [] interpolate(
-        int       keyIndex,
-        double    key,
-        String [] columnNames
-    ) {
-        int row = binarySearch(keyIndex, key, EPSILON);
-
-        if (row >= 0) { // direct match
-            double [] values = new double[columnNames.length];
-            for (int i = 0; i < values.length; ++i) {
-                int ci = columnIndex(columnNames[i]);
-                values[i] = ci < 0
-                    ? Double.NaN
-                    : columns[ci].getQuick(row);
-            }
-            return values;
-        }
-
-        row = -row - 1;
-        if (row < 1 || row >= size()) {
-            log.debug("interpolate: row is out of bounds");
-            return null;
-        }
-
-        double v1 = columns[keyIndex].getQuick(row-1);
-        double v2 = columns[keyIndex].getQuick(row);
-        double factor = Linear.factor(key, v1, v2);
-
-        double [] values = new double[columnNames.length];
-
-        for (int i = 0; i < values.length; ++i) {
-            int ci = columnIndex(columnNames[i]);
-            values[i] = ci < 0
-                ? Double.NaN
-                : Linear.weight(
-                    factor,
-                    columns[ci].getQuick(row-1),
-                    columns[ci].getQuick(row));
-        }
-
-        return values;
-    }
-
-    public boolean isSorted(String columnName) {
-        return isSorted(columnIndex(columnName));
-    }
-
-    public boolean isSorted(int columnIndex) {
-        TDoubleArrayList column = columns[columnIndex];
-        for (int i = 1, N = column.size(); i < N; ++i) {
-            if (column.getQuick(i-1) > column.getQuick(i)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    public void visit(Visitor visitor) {
-        visit(visitor, new double[columns.length]);
-    }
-
-    public void visit(Visitor visitor, double [] data) {
-        for (int i = 0, R = size(); i < R; ++i) {
-            for (int j = 0; j < data.length; ++j) {
-                data[j] = columns[j].getQuick(i);
-            }
-            visitor.visit(data);
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/QKms.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,18 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import gnu.trove.TDoubleArrayList;
-
-public interface QKms
-extends          NamedObject
-{
-    int size();
-
-    double getKm(int index);
-
-    double getQ(int index);
-
-    TDoubleArrayList allKms();
-
-    TDoubleArrayList allQs();
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/QKmsImpl.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,82 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import gnu.trove.TDoubleArrayList;
-
-import de.intevation.flys.utils.DoubleUtil;
-
-public class QKmsImpl
-extends      NamedObjectImpl
-implements   QKms
-{
-    protected TDoubleArrayList kms;
-    protected TDoubleArrayList qs;
-
-    public QKmsImpl() {
-        super("");
-        kms = new TDoubleArrayList();
-        qs  = new TDoubleArrayList();
-    }
-
-    public QKmsImpl(String name) {
-        super(name);
-        kms = new TDoubleArrayList();
-        qs  = new TDoubleArrayList();
-    }
-
-
-    public QKmsImpl(int capacity) {
-        super("");
-        kms = new TDoubleArrayList(capacity);
-        qs  = new TDoubleArrayList(capacity);
-    }
-
-
-    public QKmsImpl(TDoubleArrayList kms, TDoubleArrayList qs) {
-        this(kms, qs, "");
-    }
-
-    public QKmsImpl(
-        TDoubleArrayList kms,
-        TDoubleArrayList qs,
-        String           name
-    ) {
-        super(name);
-        this.kms = kms;
-        this.qs  = qs;
-    }
-
-    public void add(double km, double q) {
-        kms.add(km);
-        qs .add(q);
-    }
-
-    @Override
-    public double getQ(int index) {
-        return qs.getQuick(index);
-    }
-
-
-    @Override
-    public double getKm(int index) {
-        return kms.getQuick(index);
-    }
-
-    @Override
-    public int size() {
-        return kms.size();
-    }
-
-    @Override
-    public TDoubleArrayList allKms() {
-        return kms;
-    }
-
-    @Override
-    public TDoubleArrayList allQs() {
-        return qs;
-    }
-
-    public void removeNaNs() {
-        DoubleUtil.removeNaNs(new TDoubleArrayList [] { kms, qs });
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/QRangeTree.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,399 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.io.Serializable;
-
-import java.util.List;
-import java.util.ArrayList;
-
-import org.apache.log4j.Logger;
-
-public class QRangeTree
-implements   Serializable
-{
-    private static Logger log = Logger.getLogger(QRangeTree.class);
-
-    public static final double EPSILON = 1e-4;
-
-    public static class Node
-    implements          Serializable
-    {
-        Node left;
-        Node right;
-        Node prev;
-        Node next;
-
-        double a;
-        double b;
-        double q;
-
-        public Node() {
-        }
-
-        public Node(double a, double b, double q) {
-            this.a = a;
-            this.b = b;
-            this.q = q;
-        }
-
-        protected final double interpolatePrev(double pos) {
-            /*
-            f(prev.b) = prev.q
-            f(a)      = q
-
-            prev.q = m*prev.b + n
-            q      = m*a      + n <=> n = q - m*a
-
-            q - prev.q = m*(a - prev.b)
-
-            m = (q - prev.q)/(a - prev.b) # a != prev.b
-            */
-
-            if (a == prev.b) {
-                return 0.5*(q + prev.q);
-            }
-            double m = (q - prev.q)/(a - prev.b);
-            double n = q - m*a;
-            return m*pos + n;
-        }
-
-        protected final double interpolateNext(double pos) {
-            /*
-            f(next.a) = next.q
-            f(b)      = q
-
-            next.q = m*next.a + n
-            q      = m*b      + n <=> n = q - m*b
-
-            q - next.q = m*(b - next.a)
-            m = (q - next.q)/(b - next.a) # b != next.a
-            */
-
-            if (b == next.a) {
-                return 0.5*(q + next.q);
-            }
-            double m = (q - next.q)/(b - next.a);
-            double n = q - m*b;
-            return m*pos + n;
-        }
-
-        public double findQ(double pos) {
-
-            Node current = this;
-            for (;;) {
-                if (pos < current.a) {
-                    if (current.left != null) {
-                        current = current.left;
-                        continue;
-                    }
-                    return current.prev != null
-                        ? current.interpolatePrev(pos)
-                        : Double.NaN;
-                }
-                if (pos > current.b) {
-                    if (current.right != null) {
-                        current = current.right;
-                        continue;
-                    }
-                    return current.next != null
-                        ? current.interpolateNext(pos)
-                        : Double.NaN;
-                }
-                return current.q;
-            }
-        }
-
-        public Node findNode(double pos) {
-            Node current = this;
-            while (current != null) {
-                if (pos < current.a) {
-                    current = current.left;
-                }
-                else if (pos > current.b) {
-                    current = current.right;
-                }
-                return current;
-            }
-            return null;
-        }
-
-        public boolean contains(double c) {
-            return c >= a && c <= b;
-        }
-    } // class Node
-
-    /** Class to cache the last found tree leaf in a search for Q.
-     *  Its likely that a neighbored pos search
-     *  results in using the same leaf node. So
-     *  caching this leaf will minimize expensive
-     *  tree traversals.
-     *  Modeled as inner class because the QRangeTree
-     *  itself is a shared data structure.
-     *  Using this class omits interpolation between
-     *  leaves.
-     */
-    public final class QuickQFinder {
-
-        private Node last;
-
-        public QuickQFinder() {
-        }
-
-        public double findQ(double pos) {
-            if (last != null && last.contains(pos)) {
-                return last.q;
-            }
-            last = QRangeTree.this.findNode(pos);
-            return last != null ? last.q : Double.NaN;
-        }
-
-        public double [] findQs(double [] kms, Calculation report) {
-            return findQs(kms, new double[kms.length], report);
-        }
-
-        public double [] findQs(
-            double []    kms,
-            double []   qs,
-            Calculation report
-        ) {
-            for (int i = 0; i < kms.length; ++i) {
-                if (Double.isNaN(qs[i] = findQ(kms[i]))) {
-                    report.addProblem(kms[i], "cannot.find.q");
-                }
-            }
-            return qs;
-        }
-    } // class QuickQFinder
-
-    protected Node root;
-
-    public QRangeTree() {
-    }
-
-    public static final class AccessQAB {
-        private int startIndex;
-
-        public AccessQAB(int startIndex) {
-            this.startIndex = startIndex;
-        }
-
-        public Double getQ(Object [] row) {
-            return (Double)row[startIndex];
-        }
-
-        public Double getA(Object [] row) {
-            return (Double)row[startIndex+1];
-        }
-
-        public Double getB(Object [] row) {
-            return (Double)row[startIndex+2];
-        }
-    }
-
-    public static final AccessQAB WITH_COLUMN    = new AccessQAB(1);
-    public static final AccessQAB WITHOUT_COLUMN = new AccessQAB(0);
-
-    /** wstQRanges need to be sorted by range.a */
-    public QRangeTree(List<Object []> qRanges, int start, int stop) {
-        this(qRanges, WITH_COLUMN, start, stop);
-    }
-
-    public QRangeTree(
-        List<Object []> qRanges,
-        AccessQAB       accessQAB,
-        int             start,
-        int             stop
-    ) {
-        if (stop <= start) {
-            return;
-        }
-
-        int N = stop-start;
-
-        List<Node> nodes = new ArrayList<Node>(N);
-
-        Node last = null;
-
-        for (int i = 0; i < N; ++i) {
-            Object [] qRange = qRanges.get(start + i);
-            Double q = accessQAB.getQ(qRange);
-            Double a = accessQAB.getA(qRange);
-            Double b = accessQAB.getB(qRange);
-
-            double av = a != null ? a.doubleValue() : -Double.MAX_VALUE;
-            double bv = b != null ? b.doubleValue() :  Double.MAX_VALUE;
-            double qv = q.doubleValue();
-
-            // If nodes are directly neighbored and Qs are the same
-            // join them.
-            if (last != null
-            && Math.abs(last.b - av) < EPSILON
-            && Math.abs(last.q - qv) < EPSILON) {
-                last.b = bv;
-            }
-            else {
-                nodes.add(last = new Node(av, bv, qv));
-            }
-        }
-
-        if (log.isDebugEnabled()) {
-            log.debug("Before/after nodes join: " +
-                N + "/" + nodes.size());
-        }
-
-        root = wireTree(nodes);
-    }
-
-    protected static Node wireTree(List<Node> nodes) {
-        int N = nodes.size();
-        for (int i = 0; i < N; ++i) {
-            Node node = nodes.get(i);
-            if (i > 0  ) node.prev = nodes.get(i-1);
-            if (i < N-1) node.next = nodes.get(i+1);
-        }
-
-        return buildTree(nodes, 0, N-1);
-    }
-
-    protected static Node buildTree(List<Node> nodes, int lo, int hi) {
-
-        if (lo > hi) {
-            return null;
-        }
-
-        int mid = (lo + hi) >> 1;
-        Node parent = nodes.get(mid);
-
-        parent.left  = buildTree(nodes, lo, mid-1);
-        parent.right = buildTree(nodes, mid+1, hi);
-
-        return parent;
-    }
-
-    public double averageQ() {
-        double sum = 0d;
-        int n = 0;
-        for (Node node = head(); node != null; node = node.next) {
-            sum += node.q;
-            ++n;
-        }
-        return sum/n;
-    }
-
-    public double maxQ() {
-        double max = -Double.MAX_VALUE;
-        for (Node node = head(); node != null; node = node.next) {
-            if (node.q > max) {
-                max = node.q;
-            }
-        }
-        return max;
-    }
-
-    public double findQ(double pos) {
-        return root != null ? root.findQ(pos) : Double.NaN;
-    }
-
-    public Node findNode(double pos) {
-        return root != null ? root.findNode(pos) : null;
-    }
-
-    protected Node head() {
-        Node head = root;
-        while (head.left != null) {
-            head = head.left;
-        }
-        return head;
-    }
-
-    public boolean intersectsQRange(double qMin, double qMax) {
-        if (qMin > qMax) {
-            double t = qMin;
-            qMin = qMax;
-            qMax = t;
-        }
-        for (Node curr = head(); curr != null; curr = curr.next) {
-            if (curr.q >= qMin || curr.q <= qMax) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    public List<Range> findSegments(double a, double b) {
-        if (a > b) { double t = a; a = b; b = t; }
-        return findSegments(new Range(a, b));
-    }
-
-    public List<Range> findSegments(Range range) {
-        List<Range> segments = new ArrayList<Range>();
-
-        // Linear scan should be good enough here.
-        for (Node curr = head(); curr != null; curr = curr.next) {
-            if (!range.disjoint(curr.a, curr.b)) {
-                Range r = new Range(curr.a, curr.b);
-                if (r.clip(range)) {
-                    segments.add(r);
-                }
-            }
-        }
-
-        return segments;
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder();
-        inorder(root, sb);
-        return sb.toString();
-    }
-
-    protected static void inorder(Node node, StringBuilder sb) {
-        if (node != null) {
-            inorder(node.left, sb);
-            sb.append('[')
-              .append(node.a)
-              .append(", ")
-              .append(node.b)
-              .append(": ")
-              .append(node.q)
-              .append(']');
-            inorder(node.right, sb);
-        }
-    }
-
-    private static final String name(Object o) {
-        return String.valueOf(System.identityHashCode(o) & 0xffffffffL);
-    }
-
-    public String toGraph() {
-        StringBuilder sb = new StringBuilder();
-        sb.append("subgraph c");
-        sb.append(name(this));
-        sb.append(" {\n");
-        if (root != null) {
-            java.util.Deque<Node> stack = new java.util.ArrayDeque<Node>();
-            stack.push(root);
-            while (!stack.isEmpty()) {
-                Node current = stack.pop();
-                String name = "n" + name(current);
-                sb.append(name);
-                sb.append(" [label=\"");
-                sb.append(current.a).append(", ").append(current.b);
-                sb.append(": ").append(current.q).append("\"]\n");
-                if (current.left != null) {
-                    String leftName = name(current.left);
-                    sb.append(name).append(" -- n").append(leftName).append("\n");
-                    stack.push(current.left);
-                }
-                if (current.right != null) {
-                    String rightName = name(current.right);
-                    sb.append(name).append(" -- n").append(rightName).append("\n");
-                    stack.push(current.right);
-                }
-            }
-        }
-        sb.append("}\n");
-        return sb.toString();
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/QSectorFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,71 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.QSectorArtifact;
-
-import de.intevation.artifactdatabase.state.DefaultFacet;
-import de.intevation.artifactdatabase.state.Facet;
-
-
-/**
- * Facet to access QSector that where added by user.
- */
-public class QSectorFacet
-extends      DefaultFacet
-{
-    /** Logger for this class. */
-    private static final Logger logger = Logger.getLogger(QSectorFacet.class);
-
-
-    /**
-     * Trivial Constructor.
-     */
-    public QSectorFacet() {
-    }
-
-
-    /**
-     * Trivial Constructor for a QSectorFacet.
-     *
-     * @param index       Database-Index to use.
-     * @param name        Name (~type) of Facet.
-     * @param description Description of Facet.
-     */
-    public QSectorFacet(int index, String name, String description) {
-        super(index, name, description);
-    }
-
-
-    /**
-     * Get List of QSector for river from Artifact.
-     *
-     * @param artifact (QSector-)Artifact to query for list of QSector.
-     * @param context  Ignored.
-     */
-    @Override
-    public Object getData(Artifact artifact, CallContext context) {
-        QSectorArtifact qsectorArtifact = (QSectorArtifact) artifact;
-        if (qsectorArtifact == null || context == null ||
-            context.getContextValue("currentKm") == null) {
-            logger.error("No artifact, context or currentKm in QSectorFacet");
-            return null;
-        }
-        double currentKm =
-            ((Double)context.getContextValue("currentKm")).doubleValue();
-        return qsectorArtifact.getQSectors(currentKm, context);
-    }
-
-
-    /** Do a deep copy. */
-    @Override
-    public Facet deepCopy() {
-        QSectorFacet copy = new QSectorFacet();
-        copy.set(this);
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/QW.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,35 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.io.Serializable;
-
-public class QW
-implements   Serializable
-{
-    protected double q;
-    protected double w;
-
-    public QW() {
-    }
-
-    public QW(double q, double w) {
-        this.q = q;
-        this.w = w;
-    }
-
-    public double getQ() {
-        return q;
-    }
-
-    public void setQ(double q) {
-        this.q = q;
-    }
-
-    public double getW() {
-        return w;
-    }
-
-    public void setW(double w) {
-        this.w = w;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/QWDDateRange.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,24 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-
-import java.io.Serializable;
-
-import de.intevation.flys.artifacts.model.fixings.QWD;
-
-public class QWDDateRange
-implements   Serializable
-{
-
-    public QWD qwd;
-    public DateRange dateRange;
-
-    public QWDDateRange(QWD qwd, DateRange dr) {
-        this.qwd = qwd;
-        this.dateRange = dr;
-    }
-
-    public QWD getQWD() {
-        return qwd;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/Range.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,65 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.io.Serializable;
-
-/** A range from ... to .*/
-public class Range implements Serializable {
-
-    public static final double EPSILON = 1e-5;
-
-    protected double start;
-    protected double end;
-
-    public Range() {
-    }
-
-    public Range(Range other) {
-        start = other.start;
-        end   = other.end;
-    }
-
-    public Range(double start, double end) {
-        this.start = start;
-        this.end   = end;
-    }
-
-    public double getStart() {
-        return start;
-    }
-
-    public double getEnd() {
-        return end;
-    }
-
-    public boolean disjoint(double ostart, double oend) {
-        return start > oend || ostart > end;
-    }
-
-    public boolean disjoint(Range other) {
-        return start > other.end || other.start > end;
-    }
-
-    public boolean intersects(Range other) {
-        return !disjoint(other);
-    }
-
-    public void extend(Range other) {
-        if (other.start < start) start = other.start;
-        if (other.end   > end  ) end   = other.end;
-    }
-
-    public boolean clip(Range other) {
-        if (disjoint(other)) return false;
-
-        if (other.start > start) start = other.start;
-        if (other.end   < end  ) end   = other.end;
-
-        return true;
-    }
-
-    /** True if start>x<end (+ some epsilon) . */
-    public boolean inside(double x) {
-        return x > start-EPSILON && x < end+EPSILON;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/RangeWithValues.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,35 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.util.Arrays;
-
-/**
- * A range (from -> to) with associated double array.
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class RangeWithValues extends Range {
-
-    protected double[] values;
-
-    public RangeWithValues() {
-    }
-
-    public RangeWithValues(double lower, double upper, double[] values) {
-        super(lower, upper);
-        this.values = values;
-    }
-
-
-    /** Returns the values. */
-    public double[] getValues() {
-        return values;
-    }
-
-    @Override
-    public String toString() {
-        return new StringBuilder("start=").append(start)
-            .append(" end=" ).append(end)
-            .append(" values=[").append(Arrays.toString(values)).append(']')
-            .toString();
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/ReferenceCurveFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,150 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Collections;
-
-import de.intevation.artifactdatabase.state.Facet;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.WINFOArtifact;
-
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-import org.apache.log4j.Logger;
-
-
-/** Facet for W-over-Ws. */
-public class ReferenceCurveFacet
-extends      DataFacet
-{
-    private static Logger log = Logger.getLogger(ReferenceCurveFacet.class);
-
-    public static final String CONTEXT_KEY = "reference.curve.axis.scale";
-
-    /** Blackboard data provider key for reference curves start km. */
-    public static final String BB_REFERENCECURVE_STARTKM =
-        "reference_curve.startkm";
-
-    /** Blackboard data provider key for reference curves end kms. */
-    public static final String BB_REFERENCECURVE_ENDKMS =
-        "reference_curve.endkms";
-
-
-    public ReferenceCurveFacet() {
-    }
-
-
-    public ReferenceCurveFacet(int index, String name, String description) {
-        super(index, name, description, ComputeType.ADVANCE, null, null);
-    }
-
-
-    public ReferenceCurveFacet(
-        int         index,
-        String      name,
-        String      description,
-        ComputeType type,
-        String      stateID,
-        String      hash
-    ) {
-        super(index, name, description, type, hash, stateID);
-    }
-
-
-    public Object getData(Artifact artifact, CallContext context) {
-
-        if (log.isDebugEnabled()) {
-            log.debug("Get data for reference curve at index: " + index +
-                " /stateId: " + stateId);
-        }
-
-        return getWWQQ(artifact, context);
-    }
-
-
-   /**
-     * Can provide parameters of reference curve
-     * @param key      will respond on BB_REFERENCECURVE START/ENDKMS
-     * @param param    ignored
-     * @param context  ignored
-     * @return whatever parameters for reference curve
-     */
-    @Override
-    public Object provideBlackboardData(Artifact artifact,
-        Object key,
-        Object param,
-        CallContext context
-    ) {
-        WINFOArtifact winfo = (WINFOArtifact) artifact;
-        if (key.equals(BB_REFERENCECURVE_STARTKM)) {
-            return winfo.getReferenceStartKm();
-        }
-        else if (key.equals(BB_REFERENCECURVE_ENDKMS)) {
-            return winfo.getReferenceEndKms();
-        }
-        else {
-            return null;
-        }
-    }
-
-
-    protected WWQQ getWWQQ(Artifact artifact, CallContext context) {
-        FLYSArtifact winfo = (FLYSArtifact)artifact;
-
-        CalculationResult res = (CalculationResult)
-            winfo.compute(context, hash, stateId, type, false);
-
-        return ((WWQQ [])res.getData())[index];
-    }
-
-
-    @Override
-    public void set(Facet other) {
-        super.set(other);
-        ReferenceCurveFacet o = (ReferenceCurveFacet)other;
-        type                  = o.type;
-        hash                  = o.hash;
-        stateId               = o.stateId;
-    }
-
-
-    /** Copy deeply. */
-    @Override
-    public Facet deepCopy() {
-        ReferenceCurveFacet copy = new ReferenceCurveFacet();
-        copy.set(this);
-        return copy;
-    }
-
-
-    @Override
-    public List getStaticDataProviderKeys(Artifact art) {
-        List list = new ArrayList();
-        list.add(BB_REFERENCECURVE_STARTKM);
-        list.add(BB_REFERENCECURVE_ENDKMS);
-        return list;
-    }
-
-
-    @Override
-    public List getDataProviderKeys(Artifact art, CallContext context) {
-
-        // compute / get data
-        Object obj = context.getContextValue(CONTEXT_KEY);
-
-        if (!(obj instanceof WWAxisTypes)) {
-            obj = new WWAxisTypes(getWWQQ(art, context));
-            context.putContextValue(CONTEXT_KEY, obj);
-        }
-        else {
-            ((WWAxisTypes)obj).classify(getWWQQ(art, context));
-        }
-
-        return getStaticDataProviderKeys(art);//Collections.emptyList();
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/RelativePointFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,193 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.util.List;
-import java.awt.geom.Point2D;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.DataProvider;
-
-import de.intevation.flys.artifacts.StaticWKmsArtifact;
-import de.intevation.flys.artifacts.StaticWQKmsArtifact;
-import de.intevation.flys.artifacts.math.Linear;
-
-/**
- * Facet to access a point.
- */
-public class RelativePointFacet
-extends      BlackboardDataFacet
-implements   FacetTypes {
-
-    /** Own logger. */
-    private static Logger logger = Logger.getLogger(RelativePointFacet.class);
-
-    /** Trivial Constructor. */
-    public RelativePointFacet(String description) {
-        this(RELATIVE_POINT, description);
-    }
-
-
-    public RelativePointFacet(String name, String description) {
-        this.name        = name;
-        this.description = description;
-        this.index       = 0;
-    }
-
-
-    protected Point2D calculateDurationCurvePoint(CallContext context,
-        WKms wKms)
-    {
-        // TODO here and in reference curve calc: Do warn if more than 1
-        // provider found or (way better) handle it.
-        Object wqdays = null;
-        double km     = 0d;
-        List<DataProvider> providers = context.
-            getDataProvider(DurationCurveFacet.BB_DURATIONCURVE);
-        if (providers.size() < 1) {
-            logger.warn("Could not find durationcurve data provider.");
-        }
-        else {
-            wqdays = providers.get(0).provideData(
-                DurationCurveFacet.BB_DURATIONCURVE,
-                null,
-                context);
-        }
-        List<DataProvider> kmproviders = context.
-            getDataProvider(DurationCurveFacet.BB_DURATIONCURVE_KM);
-        if (kmproviders.size() < 1) {
-            logger.warn("Could not find durationcurve.km data provider.");
-        }
-        else {
-            logger.debug("Found durationcurve.km data provider.");
-            String dckm = providers.get(0).provideData(
-                DurationCurveFacet.BB_DURATIONCURVE_KM,
-                null,
-                context).toString();
-            km = Double.valueOf(dckm);
-        }
-
-        if (wqdays != null) {
-            // Which W at this km?
-            double w = StaticWKmsArtifact.getWAtKmLin(wKms, km);
-            if (w == -1) {
-                logger.warn("w is -1, already bad sign!");
-            }
-            // Where is this W passed by in the wq-curve?
-            WQDay wqday = (WQDay) wqdays;
-            // Doing a linear Day Of KM.
-            int idx = 0;
-            boolean wIncreases = wqday.getW(0) < wqday.getW(wqday.size()-1);
-            if (wIncreases) {
-                while (idx < wqday.size() && wqday.getW(idx) < w) {
-                    idx++;
-                }
-            }
-            else {
-                idx = wqday.size() -1;
-                while (idx > 0 && wqday.getW(idx) > w) {
-                    idx--;
-                }
-            }
-
-            double day = 0d;
-            int mod = (wIncreases) ? -1 : +1;
-            if (idx != 0 && idx <= wqday.size()-1) {
-                day = Linear.linear(w, wqday.getW(idx+mod), wqday.getW(idx),
-                    wqday.getDay(idx+mod), wqday.getDay(idx));
-            }
-
-            return new Point2D.Double((double) day, w);
-        }
-        logger.warn("not wqkms / w / day found");
-        // TODO better signal failure.
-        return new Point2D.Double(0d, 0d);
-    }
-
-
-    /**
-     * Calculate a reference curve point, that is, a point made of
-     * the Ws from start and end km param of the reference curve.
-     */
-    public Point2D calculateReferenceCurvePoint(CallContext context,
-        WKms wKms) {
-
-        List<DataProvider> providers = context.
-            getDataProvider(ReferenceCurveFacet.BB_REFERENCECURVE_STARTKM);
-        if (providers.size() < 1) {
-            logger.warn("Could not find reference curve startkm data provider.");
-        }
-
-        Double start = (Double) providers.get(0).
-            provideData(ReferenceCurveFacet.BB_REFERENCECURVE_STARTKM, null, context);
-
-        providers = context.
-            getDataProvider(ReferenceCurveFacet.BB_REFERENCECURVE_ENDKMS);
-        if (providers.size() < 1) {
-            logger.warn("Could not find reference curve endkms data provider.");
-        }
-        double[] ends = (double[]) providers.get(0).
-            provideData(ReferenceCurveFacet.BB_REFERENCECURVE_ENDKMS, null, context);
-
-        logger.debug("Got s " + start + " e " + ends);
-
-        double startW = StaticWKmsArtifact.getWAtKmLin(wKms, start.doubleValue());
-        // TODO handle multiple ends.
-        double endW = StaticWKmsArtifact.getWAtKmLin(wKms, ends[0]);
-        logger.debug("Gotw s " + startW + " e " + endW);
-        return new Point2D.Double(startW, endW);
-    }
-
-
-    /**
-     * Returns the data this facet requires.
-     *
-     * @param artifact the owner artifact.
-     * @param context  the CallContext (ignored).
-     *
-     * @return the data.
-     */
-    @Override
-    public Object getData(Artifact artifact, CallContext context) {
-        WKms wKms = null;
-        if (artifact instanceof StaticWKmsArtifact) {
-            wKms = ((StaticWKmsArtifact) artifact).getWKms(0);
-        }
-        else if (artifact instanceof StaticWQKmsArtifact) {
-            wKms = ((StaticWQKmsArtifact) artifact).getWQKms();
-        }
-        else {
-            logger.error("Cannot handle Artifact to create relative point.");
-            return null;
-        }
-
-        // Find out whether we live in a duration curve context, there we would
-        // provide only a single point.
-
-        if (context.getDataProvider(
-            DurationCurveFacet.BB_DURATIONCURVE_KM).size() > 0) {
-            return calculateDurationCurvePoint(context, wKms);
-        }
-        else if (context.getDataProvider(
-            ReferenceCurveFacet.BB_REFERENCECURVE_STARTKM).size() > 0) {
-            return calculateReferenceCurvePoint(context, wKms);
-        }
-
-        // TODO better signal failure.
-        return new Point2D.Double(0d, 0d);
-    }
-
-
-    /**
-     * Create a deep copy of this Facet.
-     * @return a deep copy.
-     */
-    @Override
-    public RelativePointFacet deepCopy() {
-        RelativePointFacet copy = new RelativePointFacet(description);
-        copy.set(this);
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/ReportFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.artifactdatabase.state.DefaultFacet;
-import de.intevation.artifactdatabase.state.Facet;
-
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-
-import org.apache.log4j.Logger;
-
-public class ReportFacet
-extends      DefaultFacet
-implements   FacetTypes
-{
-    private static Logger logger = Logger.getLogger(ReportFacet.class);
-
-    protected ComputeType type;
-    protected String      hash;
-    protected String      stateId;
-
-    public ReportFacet() {
-        this(ComputeType.ADVANCE);
-    }
-
-    public ReportFacet(ComputeType type) {
-        super(0, REPORT, "report");
-        this.type = type;
-    }
-
-
-    public ReportFacet(ComputeType type, String hash, String stateId) {
-        super(0, REPORT, "report");
-        this.type    = type;
-        this.hash    = hash;
-        this.stateId = stateId;
-    }
-
-    public Object getData(Artifact artifact, CallContext context) {
-        logger.debug("get report data");
-
-        FLYSArtifact flys = (FLYSArtifact)artifact;
-
-        CalculationResult cr = (CalculationResult)flys.compute(
-            context, hash, stateId, type, false);
-
-        return cr.getReport();
-    }
-
-    @Override
-    public Facet deepCopy() {
-        ReportFacet copy = new ReportFacet();
-        copy.set(this);
-        copy.type    = type;
-        copy.hash    = hash;
-        copy.stateId = stateId;
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/RiverFactory.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,71 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.util.List;
-
-import de.intevation.flys.backend.SessionHolder;
-import de.intevation.flys.model.River;
-
-import org.hibernate.Query;
-import org.hibernate.Session;
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class RiverFactory {
-
-    /** We don't need to instantiate concrete objects of this class. */
-    private RiverFactory() {
-    }
-
-
-    /**
-     * Returns all rivers that were found in the backend.
-     *
-     * @return all rivers.
-     */
-    public static List<River> getRivers() {
-        Session session = SessionHolder.HOLDER.get();
-
-        return session.createQuery("from River order by name").list();
-    }
-
-
-    /**
-     * Returns a River object fetched from database based on its id.
-     *
-     * @param river_id The id of the desired river.
-     *
-     * @return the river.
-     */
-    public static River getRiver(int river_id) {
-        Session session = SessionHolder.HOLDER.get();
-
-        Query query = session.createQuery("from River where id=:river_id");
-        query.setParameter("river_id", river_id);
-
-        List<River> rivers = query.list();
-
-        return rivers.isEmpty() ? null : rivers.get(0);
-    }
-
-
-    /**
-     * Returns a River object fetched from database based on its name.
-     *
-     * @param river The name of a river.
-     *
-     * @return the River object.
-     */
-    public static River getRiver(String river) {
-        Session session = SessionHolder.HOLDER.get();
-
-        Query query = session.createQuery(
-            "from River where name =:name");
-        query.setParameter("name", river);
-
-        List<River> rivers = query.list();
-
-        return rivers.isEmpty() ? null : rivers.get(0);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/SQOverview.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,209 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.io.Serializable;
-
-import java.text.SimpleDateFormat;
-
-import java.util.ArrayList;
-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.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-import de.intevation.flys.utils.KMIndex;
-
-public class SQOverview
-implements   Serializable
-{
-    private static Logger log = Logger.getLogger(SQOverview.class);
-
-    /**
-     * Serial version UId.
-     */
-    private static final long serialVersionUID = -8934372438968398508L;
-
-    public interface Filter {
-
-        boolean accept(KMIndex<List<Date>> entry);
-
-    } // interface Filter
-
-
-    public static final Filter ACCEPT = new Filter() {
-        @Override
-        public boolean accept(KMIndex<List<Date>> entry) {
-            return true;
-        }
-    };
-
-    public static class KmFilter implements Filter {
-
-        protected double km;
-
-        public KmFilter (double km) {
-            this.km = km;
-        }
-        @Override
-        public boolean accept(KMIndex<List<Date>> list) {
-            for (KMIndex.Entry<List<Date>> e: list){
-                if (e.getKm() == km) {
-                    return true;
-                }
-            }
-            return false;
-        }
-    };
-
-    public static class DateFilter implements Filter {
-
-        protected Date date;
-
-        public DateFilter (Date date) {
-            this.date = date;
-        }
-        @Override
-        public boolean accept(KMIndex<List<Date>> list) {
-            for (KMIndex.Entry<List<Date>> e: list){
-                if (e.getValue().equals(this.date)) {
-                    return true;
-                }
-            }
-            return false;
-        }
-    };
-
-    public static final double EPSILON = 1e-4;
-
-    public static final String DATE_FORMAT = "dd.MM.yyyy";
-
-    public static final String SQL_SQ =
-        "SELECT" +
-        "    s.km    AS km," +
-        "    m.datum AS datum " +
-        "FROM messung m " +
-        "    JOIN station s" +
-        "       ON m.stationid = s.stationid " +
-        "    JOIN gewaesser g " +
-        "       ON s.gewaesserid = g.gewaesserid " +
-        "WHERE" +
-        "    m.q_bpegel IS NOT NULL AND" +
-        "    g.name = :name " +
-        "ORDER by" +
-        "    s.km, m.datum";
-
-    protected String       riverName;
-
-    protected KMIndex<List<Date>> entries;
-
-    public SQOverview() {
-        entries = new KMIndex<List<Date>>();
-    }
-
-    public SQOverview(String riverName) {
-        this();
-        this.riverName = riverName;
-    }
-
-    private static final boolean epsilonEquals(double a, double b) {
-        return Math.abs(a - b) < EPSILON;
-    }
-
-    protected void loadData(Session session) {
-        SQLQuery query = session.createSQLQuery(SQL_SQ)
-            .addScalar("km",    StandardBasicTypes.DOUBLE)
-            .addScalar("datum", StandardBasicTypes.DATE);
-
-        query.setString("name", riverName);
-
-        List<Object []> list = query.list();
-
-        if (list.isEmpty()) {
-            log.warn("No river '" + riverName + "' found.");
-        }
-
-        Double prevKm = -Double.MAX_VALUE;
-        List<Date> dates = new ArrayList<Date>();
-
-        for (Object [] row: list) {
-            Double km = (Double)row[0];
-            if (!epsilonEquals(km, prevKm) && !dates.isEmpty()) {
-                entries.add(prevKm, dates);
-                dates = new ArrayList<Date>();
-            }
-            dates.add((Date)row[1]);
-            prevKm = km;
-        }
-
-        if (!dates.isEmpty()) {
-            entries.add(prevKm, dates);
-        }
-    }
-
-    public boolean load(Session session) {
-
-        loadData(session);
-
-        return true;
-    }
-
-
-    public void generateOverview(Document document) {
-        generateOverview(document, ACCEPT);
-    }
-
-    public KMIndex<List<Date>> filter(Filter f) {
-        // TODO: Apply filter
-        return entries;
-    }
-
-    public void generateOverview(
-        Document document,
-        Filter   filter
-    ) {
-        KMIndex<List<Date>> filtered = filter(ACCEPT);
-
-        Element sqElement = document.createElement("sq");
-
-        Element riverElement = document.createElement("river");
-
-        riverElement.setAttribute("name", riverName);
-
-        sqElement.appendChild(riverElement);
-
-        SimpleDateFormat df = new SimpleDateFormat(DATE_FORMAT);
-
-        Element kmE = document.createElement("km");
-
-        for (KMIndex.Entry<List<Date>> e: filtered) {
-
-            List<Date> dates = e.getValue();
-
-            if (!dates.isEmpty()) {
-                Element dEs = document.createElement("dates");
-
-                for (Date d: dates) {
-                    Element dE = document.createElement("date");
-
-                    dE.setAttribute("value", df.format(d));
-
-                    dEs.appendChild(dE);
-                }
-
-                kmE.appendChild(dEs);
-            }
-        }
-
-        sqElement.appendChild(kmE);
-
-        document.appendChild(sqElement);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/SQOverviewFactory.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,73 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import de.intevation.flys.artifacts.cache.CacheFactory;
-
-import de.intevation.flys.backend.SedDBSessionHolder;
-
-import net.sf.ehcache.Cache;
-import net.sf.ehcache.Element;
-
-import org.apache.log4j.Logger;
-
-import org.hibernate.Session;
-
-public class SQOverviewFactory {
-
-    private static Logger log = Logger.getLogger(SQOverviewFactory.class);
-
-    public static final String CACHE_NAME = "sq-overviews";
-
-    private SQOverviewFactory() {
-    }
-
-
-    public static SQOverview getOverview(String river) {
-
-        boolean debug = log.isDebugEnabled();
-
-        if (debug) {
-            log.debug(
-                "Looking for sq overview for river '" + river + "'");
-        }
-
-        Cache cache = CacheFactory.getCache(CACHE_NAME);
-
-        if (cache == null) {
-            if (debug) {
-                log.debug("Cache not configured.");
-            }
-            return getUncached(river);
-        }
-
-        String key = "sq-over-" + river;
-
-        Element element = cache.get(key);
-
-        if (element != null) {
-            if (debug) {
-                log.debug("Overview found in cache");
-            }
-            return (SQOverview)element.getValue();
-        }
-
-        SQOverview overview = getUncached(river);
-
-        if (overview != null) {
-            if (debug) {
-                log.debug("Store overview in cache.");
-            }
-            cache.put(new Element(key, overview));
-        }
-
-        return overview;
-    }
-
-    public static SQOverview getUncached(String river) {
-        SQOverview overview = new SQOverview(river);
-
-        Session session = SedDBSessionHolder.HOLDER.get();
-
-        return overview.load(session) ? overview : null;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/Segment.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,216 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import de.intevation.flys.model.DischargeTable;
-import de.intevation.flys.model.Gauge;
-import de.intevation.flys.model.River;
-
-import de.intevation.flys.utils.DoubleUtil;
-
-import java.io.Serializable;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-/** A Range with values and a reference point. */
-public class Segment
-implements   Serializable
-{
-    private static Logger log = Logger.getLogger(Segment.class);
-
-    public static final Comparator<Segment> REF_CMP =
-        new Comparator<Segment>() {
-            @Override
-            public int compare(Segment a, Segment b) {
-                double d = a.referencePoint - b.referencePoint;
-                if (d < 0d) return -1;
-                return d > 0d ? +1 : 0;
-            }
-        };
-
-    protected double    from;
-    protected double    to;
-    protected double [] values;
-    protected double [] backup;
-    protected double    referencePoint;
-
-    public Segment() {
-    }
-
-    public Segment(double referencePoint) {
-        this.referencePoint = referencePoint;
-    }
-
-    public Segment(double from, double to, double [] values) {
-        this.from   = from;
-        this.to     = to;
-        this.values = values;
-    }
-
-    public boolean isUp() {
-        return from < to;
-    }
-
-    /** Checks whether given km lies inside the to/from bounds of this segment. */
-    public boolean inside(double km) {
-        return from < to
-            ? km >= from && km <= to
-            : km >= to   && km <= from;
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder("Segment: [");
-        sb.append("from: ").append(from).append("; to: ")
-          .append(to)
-          .append("; ref: ").append(referencePoint)
-          .append("; values: (");
-        for (int i = 0; i < values.length; ++i) {
-            if (i > 0) sb.append(", ");
-            sb.append(values[i]);
-        }
-        sb.append(")]");
-        return sb.toString();
-    }
-
-    public void setFrom(double from) {
-        this.from = from;
-    }
-
-    public void backup() {
-        backup = values != null
-            ? (double [])values.clone()
-            : null;
-    }
-
-    public double [] getBackup() {
-        return backup;
-    }
-
-    public double getFrom() {
-        return from;
-    }
-
-    public void setTo(double to) {
-        this.to = to;
-    }
-
-    public double getTo() {
-        return to;
-    }
-
-    public void setValues(double [] values) {
-        this.values = values;
-    }
-
-    public double [] getValues() {
-        return values;
-    }
-
-    public int numValues() {
-        return values.length;
-    }
-
-    public void setReferencePoint(double referencePoint) {
-        this.referencePoint = referencePoint;
-    }
-
-    public double getReferencePoint() {
-        return referencePoint;
-    }
-
-    /** Use DoubleUtil to parse Segments. */
-    public static List<Segment> parseSegments(String input) {
-
-        final List<Segment> segments = new ArrayList<Segment>();
-
-        DoubleUtil.parseSegments(input, new DoubleUtil.SegmentCallback() {
-            @Override
-            public void newSegment(double from, double to, double [] values) {
-                segments.add(new Segment(from, to, values));
-            }
-        });
-
-        return segments;
-    }
-
-    public static boolean setReferencePointConvertQ(
-        List<Segment> segments,
-        River         river,
-        boolean       isQ,
-        Calculation   report
-    ) {
-        int numResults = -1;
-
-        boolean success = true;
-
-        // assign reference points
-        for (Segment segment: segments) {
-            Gauge gauge = river.maxOverlap(segment.getFrom(), segment.getTo());
-
-            if (gauge == null) {
-                log.warn("no gauge found. Defaults to mid point.");
-                segment.setReferencePoint(
-                    0.5*(segment.getFrom()+segment.getTo()));
-            }
-            else {
-                double ref = gauge.getStation().doubleValue();
-                log.debug(
-                    "reference gauge: " + gauge.getName() +
-                    " (km " + ref + ")");
-                segment.setReferencePoint(ref);
-            }
-
-            double [] values = segment.values;
-
-            if (numResults == -1) {
-                numResults = values.length;
-            }
-            else if (numResults != values.length) {
-                log.warn("wrong length of values");
-                return false;
-            }
-
-            // convert to Q if needed
-            if (!isQ && gauge != null) {
-
-                DischargeTable dt = gauge.fetchMasterDischargeTable();
-
-                //TODO: Change scale from 100 to 1 immediately after
-                //      discharge table import changed to cm!
-                double [][] table =
-                    DischargeTables.loadDischargeTableValues(dt, 100);
-
-                // need the original values for naming
-                segment.backup();
-
-                for (int i = 0; i < values.length; ++i) {
-                    //TODO: s.o.
-                    double w = values[i]; /* / 100.0; */
-                    double [] qs = DischargeTables.getQsForW(table, w);
-                    if (qs.length == 0) {
-                        log.warn("No Qs found for W = " + values[i]);
-                        report.addProblem("cannot.find.q.for.w", values[i]);
-                        values[i] = Double.NaN;
-                        success = false;
-                    }
-                    else {
-                        values[i] = qs[0];
-                        if (qs.length > 1) {
-                            log.warn(
-                                "More than one Q found for W = " + values[i]);
-                        }
-                    }
-                }
-            }
-        } // for all segments
-
-        Collections.sort(segments, Segment.REF_CMP);
-
-        return success;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/StaticBedHeightCacheKey.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,28 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.io.Serializable;
-
-public class StaticBedHeightCacheKey implements Serializable {
-    public static final String CACHE_NAME = "bedheight-value-table-static";
-
-    private int time;
-    private int height_id;
-
-    public StaticBedHeightCacheKey(int column, int wst_id) {
-        this.height_id  = wst_id;
-        this.time  = column;
-    }
-
-    public int hashCode() {
-        return (height_id << 8) | time;
-    }
-
-    public boolean equals(Object other) {
-        if (!(other instanceof StaticBedHeightCacheKey)) {
-            return false;
-        }
-        StaticBedHeightCacheKey o = (StaticBedHeightCacheKey) other;
-        return this.height_id == o.height_id && this.time == o.time;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/StaticMorphoWidthCacheKey.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,29 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-
-public class StaticMorphoWidthCacheKey
-{
-   public static final String CACHE_NAME = "morpho-width-table-static";
-
-    private int width_id;
-
-    public StaticMorphoWidthCacheKey(
-        int width_id
-    ) {
-        this.width_id = width_id;
-    }
-
-    @Override
-    public int hashCode() {
-        return (String.valueOf(width_id)).hashCode();
-    }
-
-    @Override
-    public boolean equals(Object other) {
-        if (!(other instanceof StaticMorphoWidthCacheKey)) {
-            return false;
-        }
-        StaticMorphoWidthCacheKey o = (StaticMorphoWidthCacheKey) other;
-        return this.width_id == o.width_id;
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/StaticSedimentLoadCacheKey.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import org.apache.commons.lang.builder.HashCodeBuilder;
-
-
-public class StaticSedimentLoadCacheKey
-{
-    public static final String CACHE_NAME = "sedimentload-value-table-static";
-
-    private String river;
-    private double startKm;
-    private double endKm;
-    private int sYear;
-    private int eYear;
-
-    public StaticSedimentLoadCacheKey(
-        String river,
-        double startKm,
-        double endKm,
-        int sYear,
-        int eYear
-    ) {
-        this.river = river;
-        this.startKm = startKm;
-        this.endKm = endKm;
-        this.sYear = sYear;
-        this.eYear = eYear;
-    }
-
-    @Override
-    public int hashCode() {
-        HashCodeBuilder builder = new HashCodeBuilder();
-        builder.append(river);
-        builder.append(startKm);
-        builder.append(endKm);
-        builder.append(sYear);
-        builder.append(eYear);
-        return builder.toHashCode();
-    }
-
-    @Override
-    public boolean equals(Object other) {
-        if (!(other instanceof StaticBedHeightCacheKey)) {
-            return false;
-        }
-        StaticSedimentLoadCacheKey o = (StaticSedimentLoadCacheKey) other;
-        return this.river == o.river &&
-            this.startKm == o.startKm &&
-            this.endKm == o.endKm &&
-            this.sYear == o.sYear &&
-            this.eYear == o.eYear;
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/StaticWKmsCacheKey.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.io.Serializable;
-
-/**
- * Caching-Key object for 'static' wst- data.
- */
-public final class StaticWKmsCacheKey
-implements         Serializable
-{
-    public static final String CACHE_NAME = "wst-value-table-static";
-
-    private int column;
-    private int wst_id;
-
-    public StaticWKmsCacheKey(int column, int wst_id) {
-        this.wst_id  = wst_id;
-        this.column  = column;
-    }
-
-    public int hashCode() {
-        return (wst_id << 8) | column;
-    }
-
-    public boolean equals(Object other) {
-        if (!(other instanceof StaticWKmsCacheKey)) {
-            return false;
-        }
-        StaticWKmsCacheKey o = (StaticWKmsCacheKey) other;
-        return wst_id == o.wst_id && this.column == o.column;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/StaticWQKmsCacheKey.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.io.Serializable;
-
-/**
- * Caching-Key object for 'static' wst- data.
- */
-public final class StaticWQKmsCacheKey
-implements         Serializable
-{
-    public static final String CACHE_NAME = "wst-wq-value-table-static";
-
-    private int column;
-    private int wst_id;
-
-    public StaticWQKmsCacheKey(int column, int wst_id) {
-        this.wst_id  = wst_id;
-        this.column  = column;
-    }
-
-    public int hashCode() {
-        return (wst_id << 8) | column;
-    }
-
-    public boolean equals(Object other) {
-        if (!(other instanceof StaticWQKmsCacheKey)) {
-            return false;
-        }
-        StaticWQKmsCacheKey o = (StaticWQKmsCacheKey) other;
-        return this.wst_id == o.wst_id && this.column == o.column;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/Timerange.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.io.Serializable;
-
-import java.util.Date;
-
-
-/**
- * This class represents time ranges specified by start and end time. Start and
- * end times are stored as long (number of milliseconds since january 1, 1970).
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class Timerange implements Serializable {
-
-    private long start;
-    private long end;
-
-
-    public Timerange(long start, long end) {
-        this.start = start;
-        this.end   = end;
-    }
-
-
-    public Timerange(Date start, Date stop) {
-        this.start = start.getTime();
-        this.end   = stop != null ? stop.getTime() : System.currentTimeMillis();
-    }
-
-
-    public long getStart() {
-        return start;
-    }
-
-
-    public long getEnd() {
-        return end;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/W.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,124 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import de.intevation.flys.utils.DataUtil;
-import de.intevation.flys.utils.DoubleUtil;
-
-import gnu.trove.TDoubleArrayList;
-
-import org.apache.log4j.Logger;
-
-public class W
-extends      NamedObjectImpl
-{
-    private static Logger log = Logger.getLogger(W.class);
-
-    protected TDoubleArrayList ws;
-
-    public W() {
-        ws = new TDoubleArrayList();
-    }
-
-    public W(String name) {
-        super(name);
-        ws = new TDoubleArrayList();
-    }
-
-    public W(int capacity) {
-        this(capacity, "");
-    }
-
-    public W(int capacity, String name) {
-        super(name);
-        ws = new TDoubleArrayList(capacity);
-    }
-
-    public void add(double value) {
-        ws.add(value);
-    }
-
-    public int size() {
-        return ws.size();
-    }
-
-    public double getW(int idx) {
-        return ws.getQuick(idx);
-    }
-
-    public double [] getWs() {
-        return ws.toNativeArray();
-    }
-
-    public double [] get(int idx) {
-        return get(idx, new double [1]);
-    }
-
-    public double [] get(int idx, double [] dst) {
-        dst[0] = ws.getQuick(idx);
-        return dst;
-    }
-
-    public double minWs() {
-        return ws.min();
-    }
-
-    public void removeNaNs() {
-        DoubleUtil.removeNaNs(new TDoubleArrayList [] { ws });
-    }
-
-    public boolean guessWaterIncreasing() {
-        return guessWaterIncreasing(0.05f);
-    }
-
-    public boolean guessWaterIncreasing(float factor) {
-        return DataUtil.guessWaterIncreasing(ws, factor);
-    }
-
-    public int [] longestIncreasingWRangeIndices() {
-        return longestIncreasingWRangeIndices(new int[2]);
-    }
-
-    public int [] longestIncreasingWRangeIndices(int [] bounds) {
-
-        int N = size();
-        int start = 0;
-        int stop  = 0;
-
-        double lastW = Double.MAX_VALUE;
-
-        for (int i = 0; i < N; ++i) {
-            double v = ws.getQuick(i);
-            if (v <= lastW) {
-                if (stop-start > bounds[1]-bounds[0]) {
-                    bounds[0] = start;
-                    bounds[1] = stop;
-                    if (log.isDebugEnabled()) {
-                        log.debug("new range: " +
-                            bounds[0] + " - " + bounds[1] + " (" +
-                            ws.getQuick(bounds[0]) + ", " +
-                            ws.getQuick(bounds[1]) + ")");
-
-                    }
-                }
-                start = stop = i;
-            }
-            else {
-                stop = i;
-            }
-            lastW = v;
-        }
-
-        if (stop-start > bounds[1]-bounds[0]) {
-            bounds[0] = start;
-            bounds[1] = stop;
-            if (log.isDebugEnabled()) {
-                log.debug("new range @end: " +
-                    bounds[0] + " - " + bounds[1] + " (" +
-                    ws.getQuick(bounds[0]) + ", " +
-                    ws.getQuick(bounds[1]) + ")");
-            }
-        }
-
-        return bounds;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WKms.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,20 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import gnu.trove.TDoubleArrayList;
-
-public interface WKms
-extends          NamedObject
-{
-    int size();
-
-    double getKm(int index);
-
-    double getW(int index);
-
-    TDoubleArrayList allKms();
-
-    TDoubleArrayList allWs();
-
-    boolean guessWaterIncreasing();
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WKmsFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.StaticWKmsArtifact;
-
-/**
- * Facet to show W|km Values.
- */
-public class WKmsFacet
-extends      BlackboardDataFacet
-implements   FacetTypes {
-
-    /** Trivial Constructor. */
-    public WKmsFacet(String description) {
-        this(STATIC_WKMS, description);
-    }
-
-    public WKmsFacet(String name, String description) {
-        this.name        = name;
-        this.description = description;
-        this.index       = 0;
-    }
-
-
-    /**
-     * Returns the data this facet requires.
-     *
-     * @param artifact the owner artifact.
-     * @param context  the CallContext (ignored).
-     *
-     * @return the data.
-     */
-    @Override
-    public Object getData(Artifact artifact, CallContext context) {
-        StaticWKmsArtifact staticData =
-            (StaticWKmsArtifact) artifact;
-        return staticData.getWKms(0);
-    }
-
-
-    /**
-     * Create a deep copy of this Facet.
-     * @return a deep copy.
-     */
-    @Override
-    public WKmsFacet deepCopy() {
-        WKmsFacet copy = new WKmsFacet(description);
-        copy.set(this);
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WKmsFactory.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,152 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.util.List;
-
-import net.sf.ehcache.Cache;
-import net.sf.ehcache.Element;
-
-import org.apache.log4j.Logger;
-
-import org.hibernate.Session;
-
-import org.hibernate.SQLQuery;
-import org.hibernate.type.StandardBasicTypes;
-
-import de.intevation.flys.artifacts.cache.CacheFactory;
-
-import de.intevation.flys.backend.SessionHolder;
-
-
-/**
- * Factory to access ready-made WKms for other (than computed) 'kinds' of
- * WST-data.
- */
-public class WKmsFactory
-{
-    /** Private logger to use here. */
-    private static Logger log = Logger.getLogger(WKmsFactory.class);
-
-    /** Query to get km and ws for wst_id and column_pos. */
-    public static final String SQL_SELECT_WS =
-        "SELECT km, w FROM wst_w_values " +
-        "WHERE wst_id = :wst_id AND column_pos = :column_pos";
-
-    /** Query to get name for wst_id and column_pos. */
-    public static final String SQL_SELECT_NAME =
-        "SELECT name " +
-        "FROM wst_columns "+
-        "WHERE wst_id = :wst_id AND position = :column_pos";
-
-    /** Query to get name (description) for wst_id. */
-    public static final String SQL_SELECT_WST_NAME =
-        "SELECT description from wsts "+
-        "WHERE id = :wst_id";
-
-
-    private WKmsFactory() {
-    }
-
-
-    /**
-     * Get WKms for given column and wst_id, caring about the cache.
-     */
-    public static WKms getWKms(int column, int wst_id) {
-        log.debug("WKmsFactory.getWKms");
-        Cache cache = CacheFactory.getCache(StaticWKmsCacheKey.CACHE_NAME);
-
-        StaticWKmsCacheKey cacheKey;
-
-        if (cache != null) {
-            cacheKey = new StaticWKmsCacheKey(wst_id, column);
-            Element element = cache.get(cacheKey);
-            if (element != null) {
-                log.debug("Got static wst values from cache");
-                return (WKms)element.getValue();
-            }
-        }
-        else {
-            cacheKey = null;
-        }
-
-        WKms values = getWKmsUncached(column, wst_id);
-
-        if (values != null && cacheKey != null) {
-            log.debug("Store static wst values in cache.");
-            Element element = new Element(cacheKey, values);
-            cache.put(element);
-        }
-        return values;
-    }
-
-    /** Get name for a WKms. */
-    public static String getWKmsName(int wst_id) {
-        log.debug("WKmsFactory.getWKmsName wst_id/" + wst_id);
-
-        String name = null;
-        Session session = SessionHolder.HOLDER.get();
-
-        SQLQuery nameQuery = session.createSQLQuery(SQL_SELECT_WST_NAME)
-            .addScalar("description", StandardBasicTypes.STRING);
-        nameQuery.setInteger("wst_id",     wst_id);
-
-        List<String> names = nameQuery.list();
-        if (names.size() >= 1) {
-            name = names.get(0);
-        }
-
-        return name;
-    }
-
-    /** Get name for a WKms. */
-    public static String getWKmsName(int column, int wst_id) {
-        log.debug("WKmsFactory.getWKmsName c/" + column + ", wst_id/" + wst_id);
-
-        String name = null;
-        Session session = SessionHolder.HOLDER.get();
-
-        SQLQuery nameQuery = session.createSQLQuery(SQL_SELECT_NAME)
-            .addScalar("name", StandardBasicTypes.STRING);
-        nameQuery.setInteger("wst_id",     wst_id);
-        nameQuery.setInteger("column_pos", column);
-
-        List<String> names = nameQuery.list();
-        if (names.size() >= 1) {
-            name = names.get(0);
-        }
-
-        return name;
-    }
-
-
-    /**
-     * Get WKms from db.
-     * @param column the position columns value
-     * @param wst_id database id of the wst
-     * @return according WKms.
-     */
-    public static WKms getWKmsUncached(int column, int wst_id) {
-
-        if (log.isDebugEnabled()) {
-            log.debug("WKmsFactory.getWKmsUncached c/" + column + ", wst_id/" + wst_id);
-        }
-
-        WKmsImpl wkms = new WKmsImpl(getWKmsName(column, wst_id));
-
-        Session session = SessionHolder.HOLDER.get();
-        SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_WS)
-            .addScalar("km", StandardBasicTypes.DOUBLE)
-            .addScalar("w",  StandardBasicTypes.DOUBLE);
-        sqlQuery.setInteger("wst_id",     wst_id);
-        sqlQuery.setInteger("column_pos", column);
-
-        List<Object []> results = sqlQuery.list();
-
-        for (int i = 0, N = results.size(); i < N; i++) {
-            Object[] row = results.get(i);
-            wkms.add((Double) row[0], (Double) row[1]);
-        }
-
-        return wkms;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WKmsImpl.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,99 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import gnu.trove.TDoubleArrayList;
-
-import de.intevation.flys.utils.DataUtil;
-
-
-public class WKmsImpl
-extends      NamedObjectImpl
-implements   WKms
-{
-    protected TDoubleArrayList kms;
-    protected TDoubleArrayList ws;
-
-    public WKmsImpl() {
-        super("");
-        kms = new TDoubleArrayList();
-        ws  = new TDoubleArrayList();
-    }
-
-
-    /**
-     * Create named, empty WKms.
-     */
-    public WKmsImpl(String name) {
-        super(name);
-        kms = new TDoubleArrayList();
-        ws  = new TDoubleArrayList();
-    }
-
-
-    public WKmsImpl(int capacity) {
-        super("");
-        kms = new TDoubleArrayList(capacity);
-        ws  = new TDoubleArrayList(capacity);
-    }
-
-
-    public WKmsImpl(TDoubleArrayList kms, TDoubleArrayList ws) {
-        this(kms, ws, "");
-    }
-
-
-    public WKmsImpl(
-        TDoubleArrayList kms,
-        TDoubleArrayList ws,
-        String           name
-    ) {
-        super(name);
-        this.kms = kms;
-        this.ws  = ws;
-    }
-
-
-    /**
-     * Add a W (in NN+m) for a km (in km).
-     */
-    public void add(double km, double w) {
-        kms.add(km);
-        ws .add(w);
-    }
-
-
-    @Override
-    public double getW(int index) {
-        return ws.getQuick(index);
-    }
-
-
-    @Override
-    public double getKm(int index) {
-        return kms.getQuick(index);
-    }
-
-    @Override
-    public boolean guessWaterIncreasing() {
-        return guessWaterIncreasing(0.05f);
-    }
-
-    protected boolean guessWaterIncreasing(float factor) {
-        return DataUtil.guessWaterIncreasing(ws, factor);
-    }
-
-    @Override
-    public int size() {
-        return kms.size();
-    }
-
-    @Override
-    public TDoubleArrayList allKms() {
-        return kms;
-    }
-
-    @Override
-    public TDoubleArrayList allWs() {
-        return ws;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WKmsJRDataSource.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,115 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-
-import org.apache.log4j.Logger;
-
-import net.sf.jasperreports.engine.JRDataSource;
-import net.sf.jasperreports.engine.JRException;
-import net.sf.jasperreports.engine.JRField;
-
-
-/**
- * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
- */
-public class WKmsJRDataSource implements JRDataSource
-{
-    /** The logger used in this exporter.*/
-    private static Logger logger = Logger.getLogger(WKmsJRDataSource.class);
-
-    /**
-     *
-     */
-    private ArrayList<String[]> data;
-    private HashMap<String, String> metaData;
-
-    private int index = -1;
-
-    /**
-     *
-     */
-    public WKmsJRDataSource()
-    {
-        data = new ArrayList<String[]>();
-        metaData = new HashMap<String, String>();
-    }
-
-
-    /**
-     *
-     */
-    public void addData(String[] data) {
-        this.data.add(data);
-    }
-
-
-    /**
-     *
-     */
-    public void addMetaData(String key, String value) {
-        this.metaData.put(key, value);
-    }
-
-
-    /**
-     *
-     */
-    public boolean next() throws JRException
-    {
-        index++;
-
-        return (index < data.size());
-    }
-
-
-    /**
-     *
-     */
-    public Object getFieldValue(JRField field) throws JRException
-    {
-        Object value = "";
-        String fieldName = field.getName();
-        if ("river".equals(fieldName)) {
-            value = metaData.get("river");
-        }
-        else if ("date".equals(fieldName)) {
-            value = metaData.get("date");
-        }
-        else if ("range".equals(fieldName)) {
-            value = metaData.get("range");
-        }
-        else if ("gauge".equals(fieldName)) {
-            value = metaData.get("gauge");
-        }
-        else if ("calculation".equals(fieldName)) {
-            value = metaData.get("calculation");
-        }
-        else if ("differences".equals(fieldName)) {
-            value = metaData.get("differences");
-        }
-        else if ("km".equals(fieldName)) {
-            value = data.get(index)[0];
-        }
-        else if ("W".equals(fieldName)) {
-            value = data.get(index)[1];
-        }
-        else if ("Q".equals(fieldName)) {
-            value = data.get(index)[2];
-        }
-        else if ("description".equals(fieldName)) {
-            value = data.get(index)[3];
-        }
-        else if ("location".equals(fieldName)) {
-            value = data.get(index)[4];
-        }
-        else if ("gaugename".equals(fieldName)) {
-            value = data.get(index)[5];
-        }
-        else if ("day".equals(fieldName)) {
-            value = data.get(index)[6];
-        }
-        return value;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WQ.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,104 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import de.intevation.flys.utils.DoubleUtil;
-
-import gnu.trove.TDoubleArrayList;
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.apache.log4j.Logger;
-
-public class WQ
-extends      W
-{
-    public static final Pattern NUMBERS_PATTERN =
-        Pattern.compile("\\D*(\\d++.\\d*)\\D*");
-
-    private static Logger log = Logger.getLogger(WQ.class);
-
-    protected TDoubleArrayList qs;
-
-    public WQ() {
-        this("");
-    }
-
-    public WQ(String name) {
-        super(name);
-        qs = new TDoubleArrayList();
-    }
-
-    public WQ(int capacity) {
-        this(capacity, "");
-    }
-
-
-    public WQ(int capacity, String name) {
-        super(capacity, name);
-        qs = new TDoubleArrayList(capacity);
-    }
-
-    public WQ(double [] qs, double [] ws) {
-        this(qs, ws, "");
-    }
-
-    public WQ(double [] qs, double [] ws, String name) {
-        super(name);
-        this.ws = new TDoubleArrayList(ws);
-        this.qs = new TDoubleArrayList(qs);
-    }
-
-
-    public Double getRawValue() {
-        if (name == null || name.length() == 0) {
-            // this should never happen
-            return null;
-        }
-
-        Matcher m = NUMBERS_PATTERN.matcher(name);
-
-        if (m.matches()) {
-            String raw = m.group(1);
-
-            try {
-                return Double.valueOf(raw);
-            }
-            catch (NumberFormatException nfe) {
-                // do nothing
-            }
-        }
-
-        return null;
-    }
-
-    public void add(double w, double q) {
-        ws.add(w);
-        qs.add(q);
-    }
-
-    public double getQ(int idx) {
-        return qs.getQuick(idx);
-    }
-
-    @Override
-    public double [] get(int idx) {
-        return get(idx, new double [2]);
-    }
-
-    @Override
-    public double [] get(int idx, double [] dst) {
-        dst[0] = ws.getQuick(idx);
-        dst[1] = qs.getQuick(idx);
-        return dst;
-    }
-
-    public double [] getQs() {
-        return qs.toNativeArray();
-    }
-
-    @Override
-    public void removeNaNs() {
-        DoubleUtil.removeNaNs(new TDoubleArrayList [] { ws, qs });
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WQCKms.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,95 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import de.intevation.flys.utils.DoubleUtil;
-
-import gnu.trove.TDoubleArrayList;
-
-/**
- * This class represents a pool of data triples that consists of 'W', 'Q' and
- * 'KM' data with corrected 'W' values computed by a BackJumpCorrector.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class WQCKms
-extends      WQKms
-{
-    protected TDoubleArrayList cws;
-
-    public WQCKms() {
-    }
-
-    public WQCKms(WQKms other, double [] cws) {
-        this.ws  = other.ws;
-        this.qs  = other.qs;
-        this.kms = other.kms;
-        this.cws = new TDoubleArrayList(cws);
-    }
-
-
-    public WQCKms(double[] kms, double[] qs, double[] ws, double[] cws) {
-        super(kms, qs, ws);
-
-        this.cws = new TDoubleArrayList(cws);
-    }
-
-    @Override
-    public void removeNaNs() {
-        DoubleUtil.removeNaNs(new TDoubleArrayList [] { ws, qs, cws, kms });
-    }
-
-    /**
-     * Adds a new row to this data pool with corrected W.
-     *
-     * @param w a W.
-     * @param q a Q.
-     * @param kms a Kms.
-     * @param cw The corrected W.
-     */
-    public void add(double w, double q, double kms, double cw) {
-        super.add(w, q, kms);
-        cws.add(cw);
-    }
-
-    @Override
-    public double[] get(int idx) {
-        return get(idx, new double[4]);
-    }
-
-    /**
-     * This method returns a 4dim array of W, Q,Kms and corrected W.
-     *
-     * @param idx The position of the triple.
-     * @param dst destination array
-     *
-     * @return a 4dim array of [W, Q, Kms, CW] in dst.
-     */
-    @Override
-    public double[] get(int idx, double[] dst) {
-        dst = super.get(idx, dst);
-
-        if (dst.length < 4) {
-            return dst;
-        }
-
-        if (cws != null && cws.size() > idx) {
-            dst[3] = cws.getQuick(idx);
-        }
-
-        return dst;
-    }
-
-    public double getC(int idx) {
-        return cws.getQuick(idx);
-    }
-
-
-    /**
-     * Returns the double array of corrected W values.
-     *
-     * @return the double array of corrected W values.
-     */
-    public double[] getCWs() {
-        return cws.toNativeArray();
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WQDay.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,77 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import gnu.trove.TIntArrayList;
-
-/**
- * This class represents a pool of data triples that consists of 'W', 'Q' and
- * 'Day' data.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class WQDay
-extends      WQ
-{
-    protected TIntArrayList days;
-
-    public WQDay() {
-        super("");
-        days = new TIntArrayList();
-    }
-
-    public WQDay(int capacity) {
-        super(capacity);
-        days = new TIntArrayList(capacity);
-    }
-
-    public WQDay(int [] days, double [] ws, double [] qs) {
-        super(qs, ws, "");
-        this.days = new TIntArrayList(days);
-    }
-
-
-    public void add(int day, double w, double q) {
-        super.add(w, q);
-        days.add(day);
-    }
-
-
-    public boolean isIncreasing() {
-        int lo = getDay(0);
-        int hi = getDay(size()-1);
-
-        return lo < hi;
-    }
-
-
-    public int getDay(int idx) {
-        return days.getQuick(idx);
-    }
-
-    @Override
-    public void removeNaNs() {
-
-        int dest = 0;
-        int N = ws.size();
-
-        for (int i = 0; i < N; ++i) {
-            double wi = ws.getQuick(i);
-            double qi = qs.getQuick(i);
-
-            if (Double.isNaN(wi) || Double.isNaN(qi)) {
-                continue;
-            }
-
-            days.setQuick(dest, days.getQuick(i));
-            ws.setQuick(dest, wi);
-            qs.setQuick(dest, qi);
-            ++dest;
-        }
-
-        if (dest < N) {
-            days.remove(dest, N-dest);
-            ws  .remove(dest, N-dest);
-            qs  .remove(dest, N-dest);
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WQFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.artifactdatabase.state.DefaultFacet;
-
-import de.intevation.flys.artifacts.WQKmsInterpolArtifact;
-
-/**
- * Facet to show W|Q Values.
- */
-public class WQFacet
-extends      DefaultFacet
-implements   FacetTypes {
-
-    /** Trivial Constructor. */
-    public WQFacet(String description) {
-        this(STATIC_WQ, description);
-    }
-
-
-    /**
-     * A Facet with WQ data.
-     */
-    public WQFacet(String name, String description) {
-        this.name        = name;
-        this.description = description;
-        this.index       = 0;
-    }
-
-
-    /**
-     * Returns the data this facet provides at km given in context.
-     *
-     * @param artifact the owner artifact.
-     * @param context  the CallContext.
-     *
-     * @return the data.
-     */
-    @Override
-    public Object getData(Artifact artifact, CallContext context) {
-        WQKmsInterpolArtifact interpolData =
-            (WQKmsInterpolArtifact) artifact;
-        Double currentKm = (Double)context.getContextValue("currentKm");
-        return interpolData.getWQAtKm(currentKm);
-    }
-
-
-    /**
-     * Create a deep copy of this Facet.
-     * @return a deep copy.
-     */
-    @Override
-    public WQKmsFacet deepCopy() {
-        WQKmsFacet copy = new WQKmsFacet(description);
-        copy.set(this);
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WQKms.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,130 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import de.intevation.flys.utils.DoubleUtil;
-
-import gnu.trove.TDoubleArrayList;
-
-import org.apache.log4j.Logger;
-
-
-/**
- * This class represents a pool of data triples that consists of 'W', 'Q' and
- * 'KM' data.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class WQKms
-extends      WQ
-implements   WKms, QKms
-{
-    private static Logger logger = Logger.getLogger(WQKms.class);
-
-    /** The array that contains the 'KMs' values. */
-    protected TDoubleArrayList kms;
-
-
-    public WQKms() {
-        this("");
-    }
-
-
-    public WQKms(String name) {
-        super(name);
-        this.kms = new TDoubleArrayList();
-    }
-
-
-    public WQKms(int capacity) {
-        this(capacity, "");
-    }
-
-
-    public WQKms(int capacity, String name) {
-        super(capacity, name);
-        this.kms = new TDoubleArrayList(capacity);
-    }
-
-    public WQKms(double [] kms, double [] qs, double [] ws) {
-        this(kms, qs, ws, "");
-    }
-
-
-    public WQKms(double [] kms, double [] qs, double [] ws, String name) {
-        super(qs, ws, name);
-        this.kms = new TDoubleArrayList(kms);
-    }
-
-    @Override
-    public void removeNaNs() {
-        DoubleUtil.removeNaNs(new TDoubleArrayList [] { ws, qs, kms });
-    }
-
-    /**
-     * Adds a new row to this data pool.
-     *
-     * @param w a W.
-     * @param q a Q.
-     * @param km a kms.
-     */
-    public void add(double w, double q, double km) {
-        super.add(w, q);
-        kms.add(km);
-    }
-
-    @Override
-    public double [] get(int idx) {
-        return get(idx, new double [3]);
-    }
-
-    /**
-     * This method returns a triple of W, Q and Kms in a single 3dim array.
-     *
-     * @param idx The position of the triple.
-     * @param dst destination array
-     *
-     * @return a triple of [W, Q, Kms] in dst.
-     */
-    @Override
-    public double [] get(int idx, double [] dst) {
-        dst[0] = ws .getQuick(idx);
-        dst[1] = qs .getQuick(idx);
-        dst[2] = kms.getQuick(idx);
-        return dst;
-    }
-
-    @Override
-    public double getKm(int idx) {
-        return kms.getQuick(idx);
-    }
-
-    @Override
-    public TDoubleArrayList allKms() {
-        return kms;
-    }
-
-    @Override
-    public TDoubleArrayList allWs() {
-        return ws;
-    }
-
-    @Override
-    public TDoubleArrayList allQs() {
-        return qs;
-    }
-
-    public double[] getKms() {
-        return kms.toNativeArray();
-    }
-
-    /**
-     * Returns a string that consist of the first and last kilometer.
-     *
-     * @return a string that consist of the first and last kilometer.
-     */
-    public String toString() {
-        double from = getKm(0);
-        double to   = getKm(size()-1);
-        return from + " - " + to;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WQKmsFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,72 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.StaticWQKmsArtifact;
-
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-
-/**
- * Facet to show W|Q|km Values.
- * We have following 'Types' (from FacetTypes):
- *   String STATIC_WQKMS = "other.wqkms";
- *   String STATIC_WQMS_W = "other.wqkms.w";
- *   String STATIC_WQKMS_Q = "other.wqkms.q";
- */
-public class WQKmsFacet
-extends      DataFacet
-implements   FacetTypes {
-
-    /** House logger. */
-    private static Logger logger = Logger.getLogger(WQKmsFacet.class);
-
-    /** Trivial Constructor. */
-    public WQKmsFacet(String description) {
-        this(STATIC_WQKMS, description);
-    }
-
-
-    /**
-     * @param name Name of this facet (we have at least two flavors (w and q).
-     */
-    public WQKmsFacet(String name, String description) {
-        super(0, name, description, ComputeType.FEED, null, null);
-    }
-
-
-    /**
-     * Returns the data this facet requires.
-     *
-     * @param artifact the owner artifact.
-     * @param context  the CallContext (ignored).
-     *
-     * @return the data.
-     */
-    @Override
-    public Object getData(Artifact artifact, CallContext context) {
-        logger.debug("WQKmsFacet.getData");
-
-        StaticWQKmsArtifact staticData =
-            (StaticWQKmsArtifact) artifact;
-        Object res = staticData.compute(context, hash, stateId, type, false);
-
-        return res;
-    }
-
-
-    /**
-     * Create a deep copy of this Facet.
-     * @return a deep copy.
-     */
-    @Override
-    public WQKmsFacet deepCopy() {
-        WQKmsFacet copy = new WQKmsFacet(description);
-        copy.set(this);
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WQKmsFactory.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,199 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.util.List;
-
-import net.sf.ehcache.Cache;
-import net.sf.ehcache.Element;
-
-import org.apache.log4j.Logger;
-
-import org.hibernate.Session;
-
-import org.hibernate.SQLQuery;
-import org.hibernate.type.StandardBasicTypes;
-
-import de.intevation.flys.artifacts.cache.CacheFactory;
-
-import de.intevation.flys.backend.SessionHolder;
-
-/**
- * Factory to access ready-made WQKms for other (than computed) 'kinds' of
- * WST-data.
- */
-public class WQKmsFactory
-{
-    private static Logger log = Logger.getLogger(WQKmsFactory.class);
-
-    /** Query to get km and wqs for wst_id and column_pos. */
-    public static final String SQL_SELECT_WQS =
-        "SELECT position, w, q FROM wst_value_table " +
-        "WHERE wst_id = :wst_id AND column_pos = :column_pos";
-
-    /** Get wst_id and position from wst_columns. */
-    public static final String SQL_SELECT_COLUMN =
-        "SELECT wst_id, position FROM wst_columns WHERE id = :column_id";
-
-    /** Query to get name for wst_id and column_pos. */
-    public static final String SQL_SELECT_NAME =
-        "SELECT name " +
-        "FROM wst_columns "+
-        "WHERE id = :column_id";
-
-
-    /** Hidden constructor, use static methods instead. */
-    private WQKmsFactory() {
-    }
-
-
-    /**
-     * Get WKms for given column (pos) and wst_id, caring about the cache.
-     */
-    public static WQKms getWQKms(int columnPos, int wst_id) {
-        log.debug("WQKmsFactory.getWQKms");
-        Cache cache = CacheFactory.getCache(StaticWQKmsCacheKey.CACHE_NAME);
-
-        StaticWQKmsCacheKey cacheKey;
-
-        if (cache != null) {
-            cacheKey = new StaticWQKmsCacheKey(wst_id, columnPos);
-            Element element = cache.get(cacheKey);
-            if (element != null) {
-                log.debug("Got static wst values from cache");
-                return (WQKms)element.getValue();
-            }
-        }
-        else {
-            cacheKey = null;
-        }
-
-        WQKms values = getWQKmsUncached(columnPos, wst_id);
-
-        if (values != null && cacheKey != null) {
-            log.debug("Store static wst values in cache.");
-            Element element = new Element(cacheKey, values);
-            cache.put(element);
-        }
-        return values;
-    }
-
-    /**
-     * Get WKms for given column (id), caring about the cache.
-     */
-    public static WQKms getWQKmsCID(int columnID) {
-        log.debug("WQKmsFactory.getWQKms");
-        Cache cache = CacheFactory.getCache(StaticWQKmsCacheKey.CACHE_NAME);
-
-        StaticWQKmsCacheKey cacheKey;
-
-        if (cache != null) {
-            cacheKey = new StaticWQKmsCacheKey(-columnID, -columnID);
-            Element element = cache.get(cacheKey);
-            if (element != null) {
-                log.debug("Got static wst values from cache");
-                return (WQKms)element.getValue();
-            }
-        }
-        else {
-            cacheKey = null;
-        }
-
-        int[] cInfo = getColumn(columnID);
-        if (cInfo == null) return null;
-        WQKms values = getWQKmsUncached(cInfo[1], cInfo[0]);
-
-
-        if (values != null && cacheKey != null) {
-            log.debug("Store static wst values in cache.");
-            Element element = new Element(cacheKey, values);
-            cache.put(element);
-        }
-        return values;
-    }
-
-
-    /**
-     * Get WQKms from db.
-     * @param column the position columns value
-     * @param wst_id database id of the wst
-     * @return respective WQKms.
-     */
-    public static WQKms getWQKmsUncached(int column, int wst_id) {
-
-        if (log.isDebugEnabled()) {
-            log.debug("WQKmsFactory.getWQKmsUncached, column "
-                + column + ", wst_id " + wst_id);
-        }
-
-        WQKms wqkms = new WQKms(WKmsFactory.getWKmsName(column, wst_id));
-
-        Session session = SessionHolder.HOLDER.get();
-        SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_WQS)
-            .addScalar("position", StandardBasicTypes.DOUBLE)
-            .addScalar("w",  StandardBasicTypes.DOUBLE)
-            .addScalar("q",  StandardBasicTypes.DOUBLE);
-        sqlQuery.setInteger("wst_id",     wst_id);
-        sqlQuery.setInteger("column_pos", column);
-
-        List<Object []> results = sqlQuery.list();
-
-        for (int i = 0, N = results.size(); i < N; i++) {
-            Object[] row = results.get(i);
-            // add(w, q, km)
-            wqkms.add((Double) row[1], (Double) row[2], (Double) row[0]);
-        }
-
-        return wqkms;
-    }
-
-
-    /**
-     * Get WQKms from db.
-     * @param columnID the columns database id value
-     * @param wst_id database id of the wst
-     * @return respective WQKms.
-     */
-    public static int[] getColumn(int columnID) {
-
-        if (log.isDebugEnabled()) {
-            log.debug("WQKmsFactory.getColumn, columnID "
-                + columnID);
-        }
-
-        Session session = SessionHolder.HOLDER.get();
-
-        SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_COLUMN)
-            .addScalar("wst_id",  StandardBasicTypes.INTEGER)
-            .addScalar("position", StandardBasicTypes.INTEGER);
-        sqlQuery.setInteger("column_id", columnID);
-
-        List<Object []> results = sqlQuery.list();
-
-        for (int i = 0, N = results.size(); i < N; i++) {
-            Object[] row = results.get(i);
-            return new int[] {(Integer)row[0], (Integer)row[1]};
-        }
-
-        return null;
-    }
-
-
-    /** Get name for a WKms. */
-    public static String getWQKmsName(int columnID) {
-        log.debug("WQKmsFactory.getWQKmsName c/" + columnID);
-
-        String name = null;
-        Session session = SessionHolder.HOLDER.get();
-
-        SQLQuery nameQuery = session.createSQLQuery(SQL_SELECT_NAME)
-            .addScalar("name", StandardBasicTypes.STRING);
-        nameQuery.setInteger("column_id", columnID);
-
-        List<String> names = nameQuery.list();
-        if (names.size() >= 1) {
-            name = names.get(0);
-        }
-
-        return name;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WQKmsResult.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,9 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import de.intevation.flys.artifacts.model.WQKms;
-
-/** A result that delivers WQKms. */
-public interface WQKmsResult {
-    public WQKms[] getWQKms();
-}
-
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WQTJRDataSource.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,100 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-
-import org.apache.log4j.Logger;
-
-import net.sf.jasperreports.engine.JRDataSource;
-import net.sf.jasperreports.engine.JRException;
-import net.sf.jasperreports.engine.JRField;
-
-
-/**
- * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
- */
-public class WQTJRDataSource implements JRDataSource
-{
-    /** The logger used in this exporter.*/
-    private static Logger logger = Logger.getLogger(WQTJRDataSource.class);
-
-    /**
-     *
-     */
-    private ArrayList<String[]> data;
-    private HashMap<String, String> metaData;
-
-    private int index = -1;
-
-    /**
-     *
-     */
-    public WQTJRDataSource()
-    {
-        data = new ArrayList<String[]>();
-        metaData = new HashMap<String, String>();
-    }
-
-
-    /**
-     *
-     */
-    public void addData(String[] data) {
-        this.data.add(data);
-    }
-
-
-    /**
-     *
-     */
-    public void addMetaData(String key, String value) {
-        this.metaData.put(key, value);
-    }
-
-
-    /**
-     *
-     */
-    public boolean next() throws JRException
-    {
-        index++;
-
-        return (index < data.size());
-    }
-
-
-    /**
-     *
-     */
-    public Object getFieldValue(JRField field) throws JRException
-    {
-        Object value = "";
-        String fieldName = field.getName();
-        if ("river".equals(fieldName)) {
-            value = metaData.get("river");
-        }
-        else if ("date".equals(fieldName)) {
-            value = metaData.get("date");
-        }
-        else if ("calculation".equals(fieldName)) {
-            value = metaData.get("calculation");
-        }
-        else if ("range".equals(fieldName)) {
-            value = data.get(index)[0];
-        }
-        else if ("W".equals(fieldName)) {
-            value = data.get(index)[1];
-        }
-        else if ("Q".equals(fieldName)) {
-            value = data.get(index)[2];
-        }
-        else if ("delta".equals(fieldName)) {
-            value = data.get(index)[3];
-        }
-        else if ("gaugename".equals(fieldName)) {
-            value = data.get(index)[4];
-        }
-        return value;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WQTimerange.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,96 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-
-/**
- * A collection of triples W,Q,Timerange.
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class WQTimerange extends WQ {
-
-    /** Used to sort &lt;w,q,timerange&gt; triples. */
-    public static class TimerangeItem implements Comparable<TimerangeItem> {
-        public double q;
-        public double w;
-        public Timerange timerange;
-
-        public TimerangeItem (Timerange timerange, double q, double w) {
-            this.timerange = timerange;
-            this.q = q;
-            this.w = w;
-        }
-
-        /** Sets [w,q] in wq. */
-        public double[] get(double[] wq) {
-            if (wq.length >= 2) {
-                wq[0] = w;
-                wq[1] = q;
-            }
-
-            return wq;
-        }
-
-        @Override
-        public int compareTo(TimerangeItem other) {
-            if (other.timerange.getStart() < timerange.getStart()) {
-                return 1;
-            }
-            else if (other.timerange.getStart() > timerange.getStart()) {
-                return -1;
-            }
-            else if (other.timerange.getEnd() < timerange.getEnd()) {
-                return 1;
-            }
-            else if (other.timerange.getEnd() > timerange.getEnd()){
-                return -1;
-            }
-            else {
-                return 0;
-            }
-        }
-    }
-
-    protected List<Timerange> timeranges;
-
-
-    public WQTimerange() {
-        super("");
-    }
-
-
-    public WQTimerange(String name) {
-        super(name);
-        timeranges = new ArrayList<Timerange>();
-    }
-
-
-    public void add(double w, double q, Timerange t) {
-        ws.add(w);
-        qs.add(q);
-        timeranges.add(t);
-    }
-
-
-    public Timerange getTimerange(int idx) {
-        return timeranges.get(idx);
-    }
-
-
-    public Timerange[] getTimeranges() {
-        return timeranges.toArray(new Timerange[timeranges.size()]);
-    }
-
-    public List<TimerangeItem> sort() {
-        ArrayList<TimerangeItem> items = new ArrayList<TimerangeItem>(timeranges.size());
-        for (int i = 0, n = size(); i < n; i++) {
-            items.add(new TimerangeItem(getTimerange(i), getQ(i), getW(i)));
-        }
-
-        Collections.sort(items);
-        return items;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WW.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,202 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import de.intevation.flys.artifacts.math.Function;
-import de.intevation.flys.artifacts.math.Identity;
-
-import de.intevation.flys.utils.DoubleUtil;
-
-import gnu.trove.TDoubleArrayList;
-
-public class WW
-extends      W
-{
-    public static class ApplyFunctionIterator
-    {
-        protected Function function1;
-        protected Function function2;
-        protected int      pos;
-        protected WW       ww;
-
-        public ApplyFunctionIterator(WW ww) {
-            this(ww, Identity.IDENTITY, Identity.IDENTITY);
-        }
-
-        public ApplyFunctionIterator(
-            WW       ww,
-            Function function1,
-            Function function2
-        ) {
-            this.ww        = ww;
-            this.function1 = function1;
-            this.function2 = function2;
-        }
-
-        public boolean hasNext() {
-            return pos < ww.size();
-        }
-
-        public int size() {
-            return ww.size();
-        }
-
-        public void reset() {
-            pos = 0;
-        }
-
-        public WW getWW() {
-            return ww;
-        }
-
-        public void get(int idx, double [] wwPair) {
-            wwPair[0] = function1.value(ww.getW(idx));
-            wwPair[1] = function2.value(ww.getW2(idx));
-        }
-
-        public void next(double [] wwPair) {
-            get(pos++, wwPair);
-        }
-    } // class FunctionIterator
-
-    protected TDoubleArrayList ws2;
-
-    protected double startKm;
-    protected double endKm;
-
-    protected Double startDatum;
-    protected Double endDatum;
-
-    public WW() {
-        this("");
-    }
-
-    public WW(String name) {
-        super(name);
-    }
-
-    public WW(int capacity) {
-        this(capacity, "");
-    }
-
-    public WW(int capacity, String name) {
-        super(capacity, name);
-        ws2 = new TDoubleArrayList(capacity);
-    }
-
-    public WW(
-        String    name,
-        double    startKm,
-        Double    startDatum,
-        double [] ws,
-        double    endKm,
-        Double    endDatum,
-        double [] ws2
-    ) {
-        this.name       = name;
-        this.ws         = new TDoubleArrayList(ws);
-        this.ws2        = new TDoubleArrayList(ws2);
-        this.startKm    = startKm;
-        this.startDatum = startDatum;
-        this.endKm      = endKm;
-        this.endDatum   = endDatum;
-    }
-
-    public WW(String name, TDoubleArrayList ws, TDoubleArrayList ws2) {
-        this.name = name;
-        this.ws  = ws;
-        this.ws2 = ws2;
-    }
-
-    public void add(double w1, double w2) {
-        ws .add(w1);
-        ws2.add(w2);
-    }
-
-    public double getW1(int idx) {
-        return ws.getQuick(idx);
-    }
-
-    public double getW2(int idx) {
-        return ws2.getQuick(idx);
-    }
-
-    public double [] getWs2() {
-        return ws2.toNativeArray();
-    }
-
-    @Override
-    public double [] get(int idx) {
-        return get(idx, new double[2]);
-    }
-
-    @Override
-    public double [] get(int idx, double [] dst) {
-        dst[0] = ws .getQuick(idx);
-        dst[1] = ws2.getQuick(idx);
-        return dst;
-    }
-
-    public double getStartKm() {
-        return startKm;
-    }
-
-    public void setStartKm(double startKm) {
-        this.startKm = startKm;
-    }
-
-    public double getEndKm() {
-        return endKm;
-    }
-
-    public void setEndKm(double endKm) {
-        this.endKm = endKm;
-    }
-
-    public Double getStartDatum() {
-        return startDatum;
-    }
-
-    public boolean startAtGauge() {
-        return startDatum != null;
-    }
-
-    public boolean endAtGauge() {
-        return endDatum != null;
-    }
-
-    public void setStartDatum(Double startDatum) {
-        this.startDatum = startDatum;
-    }
-
-    public Double getEndDatum() {
-        return endDatum;
-    }
-
-    public void setEndDatum(Double endDatum) {
-        this.endDatum = endDatum;
-    }
-
-    @Override
-    public void removeNaNs() {
-        DoubleUtil.removeNaNs(new TDoubleArrayList [] { ws, ws2 });
-    }
-
-    public double minWs2() {
-        return ws2.min();
-    }
-
-    // Note that we can also easily define a Function to do so.
-    public double getRelHeight1Cm(int idx) {
-        if (this.startAtGauge()) {
-            return (ws.getQuick(idx) - getStartDatum())*100d;
-        }
-        else return ws.getQuick(idx)*100d;
-    }
-
-    public double getRelHeight2Cm(int idx) {
-        if (this.endAtGauge()) {
-            return (ws2.getQuick(idx) - getEndDatum())*100d;
-        }
-        else return ws2.getQuick(idx)*100d;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WWAxisTypes.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,84 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import de.intevation.flys.artifacts.math.Function;
-import de.intevation.flys.artifacts.math.Identity;
-import de.intevation.flys.artifacts.math.AddScaleFunction;
-
-public class WWAxisTypes
-{
-    protected boolean inCm1;
-    protected boolean inCm2;
-
-    public WWAxisTypes() {
-        inCm1 = inCm2 = true;
-    }
-
-    public WWAxisTypes(WW ww) {
-        this();
-        classify(ww);
-    }
-
-    public void classify(WW ww) {
-        if (!ww.startAtGauge()) inCm1 = false;
-        if (!ww.endAtGauge())   inCm2 = false;
-    }
-
-    public boolean getInCm(int index) {
-        switch (index) {
-            case 0:  return inCm1;
-            case 1:  return inCm2;
-            default: return false;
-        }
-    }
-
-    public boolean getInCm1() {
-        return inCm1;
-    }
-
-    public void setInCm1(boolean inCm1) {
-        this.inCm1 = inCm1;
-    }
-
-    public boolean getInCm2() {
-        return inCm2;
-    }
-
-    public void setInCm2(boolean inCm2) {
-        this.inCm2 = inCm2;
-    }
-
-    public WW.ApplyFunctionIterator transform(WW ww) {
-        return transform(ww, false);
-    }
-
-    private static final double zero(Double d) {
-        return d == null ? 0 : d;
-    }
-
-    public WW.ApplyFunctionIterator transform(WW ww, boolean normalized) {
-
-        Function function1;
-        Function function2;
-
-        if (!normalized) {
-            function1 = inCm1
-                ? new AddScaleFunction(-ww.getStartDatum(), 100d)
-                : Identity.IDENTITY;
-
-            function2 = inCm2
-                ? new AddScaleFunction(-ww.getEndDatum(), 100d)
-                : Identity.IDENTITY;
-        }
-        else {
-            double minW1 = ww.minWs();
-            double minW2 = ww.minWs2();
-            double scale1 = inCm1 ? 100d : 1d;
-            double scale2 = inCm2 ? 100d : 1d;
-            function1 = new AddScaleFunction(-minW1, scale1);
-            function2 = new AddScaleFunction(-minW2, scale2);
-        }
-
-        return new WW.ApplyFunctionIterator(ww, function1, function2);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WWQQ.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,99 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import de.intevation.flys.artifacts.math.Identity;
-
-import de.intevation.flys.utils.DoubleUtil;
-
-import gnu.trove.TDoubleArrayList;
-
-public class WWQQ
-extends      WW
-{
-    public static class ApplyFunctionIterator
-    extends             WW.ApplyFunctionIterator
-    {
-        public ApplyFunctionIterator(WWQQ ww) {
-            super(ww, Identity.IDENTITY, Identity.IDENTITY);
-        }
-
-        @Override
-        public void get(int idx, double [] wwqqPair) {
-            WWQQ wwqq = (WWQQ)ww;
-            wwqqPair[0] = function1.value(wwqq.getW(idx));
-            wwqqPair[1] = function2.value(wwqq.getW2(idx));
-            wwqqPair[2] = wwqq.getQ1(idx);
-            wwqqPair[3] = wwqq.getQ2(idx);
-        }
-    } // class ApplyFunctionIterator
-
-    protected TDoubleArrayList qs1;
-    protected TDoubleArrayList qs2;
-
-    public WWQQ() {
-        this("");
-    }
-
-    public WWQQ(String name) {
-        super(name);
-    }
-
-    public WWQQ(int capacity) {
-        this(capacity, "");
-    }
-
-    public WWQQ(int capacity, String name) {
-        super(capacity, name);
-    }
-
-    public WWQQ(
-        String    name,
-        double    startKm,
-        Double    startDatum,
-        double [] ws1,
-        double [] qs1,
-        double    endKm,
-        Double    endDatum,
-        double [] ws2,
-        double [] qs2
-    ) {
-        super(name, startKm, startDatum, ws1, endKm, endDatum, ws2);
-        this.qs1 = new TDoubleArrayList(qs1);
-        this.qs2 = new TDoubleArrayList(qs2);
-    }
-
-    public double getQ1(int idx) {
-        return qs1.getQuick(idx);
-    }
-
-    public double getQ2(int idx) {
-        return qs2.getQuick(idx);
-    }
-
-    public double [] getQs1() {
-        return qs1.toNativeArray();
-    }
-
-    public double [] getQs2() {
-        return qs2.toNativeArray();
-    }
-
-    @Override
-    public double [] get(int idx) {
-        return get(idx, new double[4]);
-    }
-
-    @Override
-    public double [] get(int idx, double [] dst) {
-        dst[0] = ws .getQuick(idx);
-        dst[1] = ws2.getQuick(idx);
-        dst[2] = qs1.getQuick(idx);
-        dst[3] = qs2.getQuick(idx);
-        return dst;
-    }
-
-    @Override
-    public void removeNaNs() {
-        DoubleUtil.removeNaNs(new TDoubleArrayList [] { ws, ws2, qs1, qs2 });
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WWQQJRDataSource.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,121 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-
-import org.apache.log4j.Logger;
-
-import net.sf.jasperreports.engine.JRDataSource;
-import net.sf.jasperreports.engine.JRException;
-import net.sf.jasperreports.engine.JRField;
-
-
-/**
- * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
- */
-public class WWQQJRDataSource implements JRDataSource
-{
-    /** The logger used in this exporter.*/
-    private static Logger logger = Logger.getLogger(WWQQJRDataSource.class);
-
-    /**
-     *
-     */
-    private ArrayList<String[]> data;
-    private HashMap<String, String> metaData;
-
-    private int index = -1;
-
-    /**
-     *
-     */
-    public WWQQJRDataSource()
-    {
-        data = new ArrayList<String[]>();
-        metaData = new HashMap<String, String>();
-    }
-
-
-    /**
-     *
-     */
-    public void addData(String[] data) {
-        this.data.add(data);
-    }
-
-
-    /**
-     *
-     */
-    public void addMetaData(String key, String value) {
-        this.metaData.put(key, value);
-    }
-
-
-    /**
-     *
-     */
-    public boolean next() throws JRException
-    {
-        index++;
-
-        return (index < data.size());
-    }
-
-
-    /**
-     *
-     */
-    public Object getFieldValue(JRField field) throws JRException
-    {
-        Object value = "";
-        String fieldName = field.getName();
-        if ("river".equals(fieldName)) {
-            value = metaData.get("river");
-        }
-        else if ("date".equals(fieldName)) {
-            value = metaData.get("date");
-        }
-        else if ("calculation".equals(fieldName)) {
-            value = metaData.get("calculation");
-        }
-        else if ("reference".equals(fieldName)) {
-            value = metaData.get("reference");
-        }
-        else if ("location".equals(fieldName)) {
-            value = metaData.get("location");
-        }
-        else if ("km1".equals(fieldName)) {
-            value = data.get(index)[0];
-        }
-        else if ("location1".equals(fieldName)) {
-            value = data.get(index)[1];
-        }
-        else if ("W1".equals(fieldName)) {
-            value = data.get(index)[2];
-        }
-        else if ("Q1".equals(fieldName)) {
-            value = data.get(index)[3];
-        }
-        else if ("km2".equals(fieldName)) {
-            value = data.get(index)[4];
-        }
-        else if ("location2".equals(fieldName)) {
-            value = data.get(index)[5];
-        }
-        else if ("W2".equals(fieldName)) {
-            value = data.get(index)[6];
-        }
-        else if ("Q2".equals(fieldName)) {
-            value = data.get(index)[7];
-        }
-        else if ("Wcm1".equals(fieldName)) {
-            value = data.get(index)[8];
-        }
-        else if ("Wcm2".equals(fieldName)) {
-            value = data.get(index)[9];
-        }
-        return value;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WaterlevelFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,160 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.math.Linear;
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-import de.intevation.flys.artifacts.model.extreme.ExtremeResult;
-
-import org.apache.log4j.Logger;
-
-/**
- * Facet of a Waterlevel (WQKms).
- */
-public class WaterlevelFacet extends DataFacet {
-
-    private static Logger logger = Logger.getLogger(WaterlevelFacet.class);
-
-    public WaterlevelFacet(int index, String name, String description) {
-        super(index, name, description, ComputeType.ADVANCE, null, null);
-    }
-
-    public WaterlevelFacet(
-        int         index,
-        String      name,
-        String      description,
-        ComputeType type,
-        String      stateID,
-        String      hash
-    ) {
-        super(index, name, description, type, hash, stateID);
-    }
-
-    public WaterlevelFacet() {
-    }
-
-    protected WQKms [] getWQKms(CalculationResult res) {
-        if (res.getData() instanceof ExtremeResult)
-            return ((ExtremeResult) res.getData()).getWQKms();
-        else if (res.getData() instanceof WQKms[]) {
-            return (WQKms []) res.getData();
-        }
-        else {
-            logger.error("WaterlevelFacet got wrong data type " + res.getData());
-            return null;
-        }
-    }
-
-    /**
-     * Get waterlevel data.
-     * @return a WQKms at given index.
-     */
-    @Override
-    public Object getData(Artifact artifact, CallContext context) {
-
-        if (logger.isDebugEnabled()) {
-            logger.debug("Get data for waterlevels at index: " + index +
-                " /stateId: " + stateId);
-        }
-
-        if (artifact == null) {
-            logger.error("WaterlevelFacet.getData: artifact is null");
-            return null;
-        }
-
-        FLYSArtifact winfo = (FLYSArtifact) artifact;
-
-        CalculationResult res = (CalculationResult)
-            winfo.compute(context, hash, stateId, type, false);
-
-        if (res == null) {
-            logger.error("WaterlevelFacet.getData: null result");
-            return null;
-        }
-
-        WQKms [] wqkms = getWQKms(res);
-        Object KM = context.getContextValue("currentKm");
-
-        // Interpolation.
-        if (KM != null) {
-            linearInterpolate(wqkms[index], (Double) KM);
-        }
-        else {
-            logger.debug("Do not interpolate.");
-        }
-
-        return wqkms != null ? wqkms[index] : null;
-    }
-
-
-    /**
-     * Linear interpolation of WQKms.
-     * TODO rewrite.
-     * @return [w, q, km]
-     */
-    public WQKms linearInterpolate(WQKms wqkms, double km) {
-        logger.debug("interpolate at given km (" + km + ")");
-
-        WQKms resultWQKms = new WQKms();
-        int size = wqkms.size();
-        boolean kmIncreasing = wqkms.getKm(0) < wqkms.getKm(size-1);
-        int mod = kmIncreasing ? +1 : -1;
-        int idx = 0;
-        // Move idx to closest from one direction, check for match.
-        if (!kmIncreasing) {
-            while (idx < size && wqkms.getKm(idx) < km) {
-                if (Math.abs(wqkms.getKm(idx) - km) < 0.01d) {
-                    resultWQKms.add(wqkms.getW(idx), wqkms.getQ(idx), wqkms.getKm(idx));
-                    return resultWQKms;
-                }
-                idx++;
-            }
-        }
-        else {
-            idx = size-1;
-            while (idx > 0 && wqkms.getKm(idx) > km) {
-                if (Math.abs(wqkms.getKm(idx) - km) < 0.01d) {
-                    resultWQKms.add(wqkms.getW(idx), wqkms.getQ(idx), wqkms.getKm(idx));
-                    return resultWQKms;
-                }
-                idx--;
-            }
-        }
-        if (Math.abs(wqkms.getKm(idx) - km) < 0.01d) {
-            resultWQKms.add(wqkms.getW(idx), wqkms.getQ(idx), wqkms.getKm(idx));
-            return resultWQKms;
-        }
-
-        if ((idx != -1) && (idx < size) && (idx - mod != -1) && (idx - mod < size)) {
-            double inW = Linear.linear(
-                km,
-                wqkms.getKm(idx), wqkms.getKm(idx - mod),
-                wqkms.getW(idx), wqkms.getW(idx - mod));
-            double inQ = Linear.linear(
-                km,
-                wqkms.getKm(idx), wqkms.getKm(idx - mod),
-                wqkms.getQ(idx), wqkms.getQ(idx - mod));
-            resultWQKms.add(inW, inQ, km);
-        }
-        else {
-            logger.debug("waterlevelfacet stuff " + idx + " size " + size + " mod: " + mod);
-        }
-
-        return resultWQKms;
-    }
-
-
-    /** Copy deeply. */
-    @Override
-    public Facet deepCopy() {
-        WaterlevelFacet copy = new WaterlevelFacet();
-        copy.set(this);
-        copy.type    = type;
-        copy.hash    = hash;
-        copy.stateId = stateId;
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WstFactory.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,48 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.util.List;
-
-import de.intevation.flys.backend.SessionHolder;
-import de.intevation.flys.model.River;
-import de.intevation.flys.model.Wst;
-
-import org.hibernate.Query;
-import org.hibernate.Session;
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class WstFactory {
-
-    public static final int DEFAULT_KIND = 0;
-
-    /** We don't need to instantiate concrete objects of this class. */
-    private WstFactory() {
-    }
-
-
-    /**
-     * Returns the Wst object for a given river.
-     *
-     * @param river The river.
-     *
-     * @return the Wst of <i>river</i>.
-     */
-    public static Wst getWst(River river) {
-        return getWst(river, DEFAULT_KIND);
-    }
-
-    public static Wst getWst(River river, int kind) {
-        Session session = SessionHolder.HOLDER.get();
-
-        Query query = session.createQuery(
-            "from Wst where river=:river and kind = :kind");
-        query.setParameter("river", river);
-        query.setInteger("kind", kind);
-
-        List<Wst> wsts = query.list();
-
-        return wsts.isEmpty() ? null : wsts.get(0);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WstLine.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,88 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import gnu.trove.TDoubleArrayList;
-
-
-/**
- * A model class that is used to store a line of a WST.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class WstLine {
-
-    /** The kilometer value of the line.*/
-    protected double km;
-
-    /** The W values.*/
-    protected TDoubleArrayList ws;
-
-    /** The Q values.*/
-    protected TDoubleArrayList qs;
-
-
-    /**
-     * A constructor that builds a new WstLine for a specific kilometer.
-     *
-     * @param km The kilometer.
-     */
-    public WstLine(double km) {
-        this.km = km;
-        this.ws = new TDoubleArrayList();
-        this.qs = new TDoubleArrayList();
-    }
-
-
-    /**
-     * Adds a pair of W/Q to this line.
-     *
-     * @param w The W value.
-     * @param q The Q value.
-     */
-    public void add(double w, double q) {
-        ws.add(w);
-        qs.add(q);
-    }
-
-
-    /**
-     * Returns the kilometer of this line.
-     *
-     * @return the kilomter of this line.
-     */
-    public double getKm() {
-        return km;
-    }
-
-
-    /**
-     * Returns the W value at index <i>idx</i> of this line.
-     *
-     * @param idx The position of the desired W value.
-     *
-     * @return the W at position <i>idx</i>.
-     */
-    public double getW(int idx) {
-        return ws.size() > idx ? ws.get(idx) : -1d;
-    }
-
-
-    /**
-     * Returns the Q values of this line.
-     *
-     * @return the Q values of this line.
-     */
-    public double[] getQs() {
-        return qs.toNativeArray();
-    }
-
-
-    /**
-     * Returns the number of columns this line consists of.
-     *
-     * @return the columns this line consists of.
-     */
-    public int getSize() {
-        return qs.size();
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1268 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.io.Serializable;
-
-import de.intevation.flys.artifacts.math.Linear;
-import de.intevation.flys.artifacts.math.Function;
-
-import java.util.Arrays;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Collections;
-
-import org.apache.log4j.Logger;
-
-import org.apache.commons.math.analysis.interpolation.SplineInterpolator;
-
-import org.apache.commons.math.analysis.polynomials.PolynomialSplineFunction;
-
-import org.apache.commons.math.ArgumentOutsideDomainException;
-
-import org.apache.commons.math.exception.MathIllegalArgumentException;
-
-import gnu.trove.TDoubleArrayList;
-
-/**
- * W, Q and km data from database 'wsts' spiced with interpolation algorithms.
- */
-public class WstValueTable
-implements   Serializable
-{
-    private static Logger log = Logger.getLogger(WstValueTable.class);
-
-    public static final int DEFAULT_Q_STEPS = 500;
-
-    public static final int RELATE_WS_SAMPLES = 200;
-
-    /**
-     * A Column in the table, typically representing one measurement session.
-     */
-    public static final class Column
-    implements                Serializable
-    {
-        protected String name;
-
-        protected QRangeTree qRangeTree;
-
-        public Column() {
-        }
-
-        public Column(String name) {
-            this.name = name;
-        }
-
-        public String getName() {
-            return name;
-        }
-
-        public void setName(String name) {
-            this.name = name;
-        }
-
-        public QRangeTree getQRangeTree() {
-            return qRangeTree;
-        }
-
-        public void setQRangeTree(QRangeTree qRangeTree) {
-            this.qRangeTree = qRangeTree;
-        }
-    } // class Column
-
-    /**
-     * A (weighted) position used for interpolation.
-     */
-    public static final class QPosition {
-
-        protected int    index;
-        protected double weight;
-
-        public QPosition() {
-        }
-
-        public QPosition(int index, double weight) {
-            this.index  = index;
-            this.weight = weight;
-        }
-
-        public QPosition set(int index, double weight) {
-            this.index  = index;
-            this.weight = weight;
-            return this;
-        }
-
-    } // class Position
-
-    public static final class SplineFunction {
-
-        public PolynomialSplineFunction spline;
-        public double []                splineQs;
-        public double []                splineWs;
-
-        public SplineFunction(
-            PolynomialSplineFunction spline,
-            double []                splineQs,
-            double []                splineWs
-        ) {
-            this.spline   = spline;
-            this.splineQs = splineQs;
-            this.splineWs = splineWs;
-        }
-
-        public double [][] sample(
-            int         numSamples,
-            double      km,
-            Calculation errors
-        ) {
-            double minQ = getQMin();
-            double maxQ = getQMax();
-
-            double [] outWs = new double[numSamples];
-            double [] outQs = new double[numSamples];
-
-            Arrays.fill(outWs, Double.NaN);
-            Arrays.fill(outQs, Double.NaN);
-
-            double stepWidth = (maxQ - minQ)/numSamples;
-
-            try {
-                double q = minQ;
-                for (int i = 0; i < outWs.length; ++i, q += stepWidth) {
-                    outWs[i] = spline.value(outQs[i] = q);
-                }
-            }
-            catch (ArgumentOutsideDomainException aode) {
-                if (errors != null) {
-                    errors.addProblem(km, "spline.interpolation.failed");
-                }
-                log.error("spline interpolation failed.", aode);
-            }
-
-            return new double [][] { outWs, outQs };
-        }
-
-        public double getQMin() {
-            return Math.min(splineQs[0], splineQs[splineQs.length-1]);
-        }
-
-        public double getQMax() {
-            return Math.max(splineQs[0], splineQs[splineQs.length-1]);
-        }
-
-        /** Constructs a continues index between the columns to Qs. */
-        public PolynomialSplineFunction createIndexQRelation() {
-
-            double [] indices = new double[splineQs.length];
-            for (int i = 0; i < indices.length; ++i) {
-                indices[i] = i;
-            }
-
-            try {
-                SplineInterpolator interpolator = new SplineInterpolator();
-                return interpolator.interpolate(indices, splineQs);
-            }
-            catch (MathIllegalArgumentException miae) {
-                // Ignore me!
-            }
-            return null;
-        }
-    } // class SplineFunction
-
-    /**
-     * A row, typically a position where measurements were taken.
-     */
-    public static final class Row
-    implements                Serializable, Comparable<Row>
-    {
-        double    km;
-        double [] ws;
-
-        public Row() {
-        }
-
-        public Row(double km) {
-            this.km = km;
-        }
-
-        public Row(double km, double [] ws) {
-            this(km);
-            this.ws = ws;
-        }
-
-        /**
-         * Compare according to place of measurement (km).
-         */
-        public int compareTo(Row other) {
-            double d = km - other.km;
-            if (d < -0.0001) return -1;
-            if (d >  0.0001) return +1;
-            return 0;
-        }
-
-        /**
-         * Interpolate Ws, given Qs and a km.
-         *
-         * @param iqs Given ("input") Qs.
-         * @param ows Resulting ("output") Ws.
-         * @param table Table of which to use data for interpolation.
-         */
-        public void interpolateW(
-            Row           other,
-            double        km,
-            double []     iqs,
-            double []     ows,
-            WstValueTable table,
-            Calculation   errors
-        ) {
-            double kmWeight = Linear.factor(km, this.km, other.km);
-
-            QPosition qPosition = new QPosition();
-
-            for (int i = 0; i < iqs.length; ++i) {
-                if (table.getQPosition(km, iqs[i], qPosition) != null) {
-                    double wt =       getW(qPosition);
-                    double wo = other.getW(qPosition);
-                    if (Double.isNaN(wt) || Double.isNaN(wo)) {
-                        if (errors != null) {
-                            errors.addProblem(
-                                km, "cannot.find.w.for.q", iqs[i]);
-                        }
-                        ows[i] = Double.NaN;
-                    }
-                    else {
-                        ows[i] = Linear.weight(kmWeight, wt, wo);
-                    }
-                }
-                else {
-                    if (errors != null) {
-                        errors.addProblem(km, "cannot.find.q", iqs[i]);
-                    }
-                    ows[i] = Double.NaN;
-                }
-            }
-        }
-
-
-        public SplineFunction createSpline(
-            WstValueTable table,
-            Calculation   errors
-        ) {
-            int W = ws.length;
-
-            if (W < 1) {
-                if (errors != null) {
-                    errors.addProblem(km, "no.ws.found");
-                }
-                return null;
-            }
-
-            double [] splineQs = new double[W];
-
-            for (int i = 0; i < W; ++i) {
-                double sq = table.getQIndex(i, km);
-                if (Double.isNaN(sq) && errors != null) {
-                    errors.addProblem(
-                        km, "no.q.found.in.column", (i+1));
-                }
-                splineQs[i] = sq;
-            }
-
-            try {
-                SplineInterpolator interpolator = new SplineInterpolator();
-                PolynomialSplineFunction spline =
-                    interpolator.interpolate(splineQs, ws);
-
-                return new SplineFunction(spline, splineQs, ws);
-            }
-            catch (MathIllegalArgumentException miae) {
-                if (errors != null) {
-                    errors.addProblem(km, "spline.creation.failed");
-                }
-                log.error("spline creation failed", miae);
-            }
-            return null;
-        }
-
-        public SplineFunction createSpline(
-            Row           other,
-            double        km,
-            WstValueTable table,
-            Calculation   errors
-        ) {
-            int W = Math.min(ws.length, other.ws.length);
-
-            if (W < 1) {
-                if (errors != null) {
-                    errors.addProblem("no.ws.found");
-                }
-                return null;
-            }
-
-            double factor = Linear.factor(km, this.km, other.km);
-
-            double [] splineQs = new double[W];
-            double [] splineWs = new double[W];
-
-            for (int i = 0; i < W; ++i) {
-                double wws = Linear.weight(factor, ws[i], other.ws[i]);
-                double wqs = Linear.weight(
-                    factor,
-                    table.getQIndex(i, km),
-                    table.getQIndex(i, other.km));
-
-                if (Double.isNaN(wws) || Double.isNaN(wqs)) {
-                    if (errors != null) {
-                        errors.addProblem(km, "cannot.find.w.or.q");
-                    }
-                }
-
-                splineWs[i] = wws;
-                splineQs[i] = wqs;
-            }
-
-            SplineInterpolator interpolator = new SplineInterpolator();
-
-            try {
-                PolynomialSplineFunction spline =
-                    interpolator.interpolate(splineQs, splineWs);
-
-                return new SplineFunction(spline, splineQs, splineWs);
-            }
-            catch (MathIllegalArgumentException miae) {
-                if (errors != null) {
-                    errors.addProblem(km, "spline.creation.failed");
-                }
-                log.error("spline creation failed", miae);
-            }
-
-            return null;
-        }
-
-        public double [][] interpolateWQ(
-            Row           other,
-            double        km,
-            int           steps,
-            WstValueTable table,
-            Calculation   errors
-        ) {
-            SplineFunction sf = createSpline(other, km, table, errors);
-
-            return sf != null
-                ? sf.sample(steps, km, errors)
-                : new double[2][0];
-        }
-
-
-        public double [][] interpolateWQ(
-            int           steps,
-            WstValueTable table,
-            Calculation   errors
-        ) {
-            SplineFunction sf = createSpline(table, errors);
-
-            return sf != null
-                ? sf.sample(steps, km, errors)
-                : new double[2][0];
-        }
-
-
-        public double getW(QPosition qPosition) {
-            int    index  = qPosition.index;
-            double weight = qPosition.weight;
-
-            return weight == 1.0
-                ? ws[index]
-                : Linear.weight(weight, ws[index-1], ws[index]);
-        }
-
-        public double getW(
-            Row       other,
-            double    km,
-            QPosition qPosition
-        ) {
-            double kmWeight = Linear.factor(km, this.km, other.km);
-
-            int    index  = qPosition.index;
-            double weight = qPosition.weight;
-
-            double tw, ow;
-
-            if (weight == 1.0) {
-                tw = ws[index];
-                ow = other.ws[index];
-            }
-            else {
-                tw = Linear.weight(weight, ws[index-1], ws[index]);
-                ow = Linear.weight(weight, other.ws[index-1], other.ws[index]);
-            }
-
-            return Linear.weight(kmWeight, tw, ow);
-        }
-
-        public double [] findQsForW(double w, WstValueTable table) {
-
-            TDoubleArrayList qs = new TDoubleArrayList();
-
-            if (ws.length > 0 && Math.abs(ws[0]-w) < 0.000001) {
-                double q = table.getQIndex(0, km);
-                if (!Double.isNaN(q)) {
-                    qs.add(q);
-                }
-            }
-
-            for (int i = 1; i < ws.length; ++i) {
-                double w2 = ws[i];
-                if (Double.isNaN(w2)) {
-                    continue;
-                }
-                if (Math.abs(w2-w) < 0.000001) {
-                    double q = table.getQIndex(i, km);
-                    if (!Double.isNaN(q)) {
-                        qs.add(q);
-                    }
-                    continue;
-                }
-                double w1 = ws[i-1];
-                if (Double.isNaN(w1)) {
-                    continue;
-                }
-
-                if (w < Math.min(w1, w2) || w > Math.max(w1, w2)) {
-                    continue;
-                }
-
-                double q1 = table.getQIndex(i-1, km);
-                double q2 = table.getQIndex(i,   km);
-                if (Double.isNaN(q1) || Double.isNaN(q2)) {
-                    continue;
-                }
-
-                double q = Linear.linear(w, w1, w2, q1, q2);
-                qs.add(q);
-            }
-
-            return qs.toNativeArray();
-        }
-
-        public double [] findQsForW(
-            Row           other,
-            double        w,
-            double        km,
-            WstValueTable table
-        ) {
-            TDoubleArrayList qs = new TDoubleArrayList();
-
-            double factor = Linear.factor(km, this.km, other.km);
-
-            if (ws.length > 0) {
-                double wt = Linear.weight(factor, ws[0], other.ws[0]);
-                if (!Double.isNaN(wt)) {
-                    double q = table.getQIndex(0, km);
-                    if (!Double.isNaN(q)) {
-                        qs.add(q);
-                    }
-                }
-            }
-
-            for (int i = 1; i < ws.length; ++i) {
-                double w2 = Linear.weight(factor, ws[i], other.ws[i]);
-                if (Double.isNaN(w2)) {
-                    continue;
-                }
-                if (Math.abs(w2-w) < 0.000001) {
-                    double q = table.getQIndex(i, km);
-                    if (!Double.isNaN(q)) {
-                        qs.add(q);
-                    }
-                    continue;
-                }
-                double w1 = Linear.weight(factor, ws[i-1], other.ws[i-1]);
-                if (Double.isNaN(w1)) {
-                    continue;
-                }
-
-                if (w < Math.min(w1, w2) || w > Math.max(w1, w2)) {
-                    continue;
-                }
-
-                double q1 = table.getQIndex(i-1, km);
-                double q2 = table.getQIndex(i,   km);
-                if (Double.isNaN(q1) || Double.isNaN(q2)) {
-                    continue;
-                }
-
-                double q = Linear.linear(w, w1, w2, q1, q2);
-                qs.add(q);
-            }
-
-            return qs.toNativeArray();
-        }
-
-        public double [] getMinMaxW(double [] result) {
-            double minW =  Double.MAX_VALUE;
-            double maxW = -Double.MAX_VALUE;
-            for (int i = 0; i < ws.length; ++i) {
-                double w = ws[i];
-                if (w < minW) minW = w;
-                if (w > maxW) maxW = w;
-            }
-            result[0] = minW;
-            result[1] = maxW;
-            return result;
-        }
-
-        public double [] getMinMaxW(Row other, double km, double [] result) {
-            double [] m1 = this .getMinMaxW(new double [2]);
-            double [] m2 = other.getMinMaxW(new double [2]);
-            double factor = Linear.factor(km, this.km, other.km);
-            result[0] = Linear.weight(factor, m1[0], m2[0]);
-            result[1] = Linear.weight(factor, m1[1], m2[1]);
-            return result;
-        }
-    } // class Row
-
-    /** Rows in table. */
-    protected List<Row> rows;
-
-    /** Columns in table. */
-    protected Column [] columns;
-
-    public WstValueTable() {
-        rows = new ArrayList<Row>();
-    }
-
-    public WstValueTable(Column [] columns) {
-        this();
-        this.columns = columns;
-    }
-
-    public WstValueTable(Column [] columns, List<Row> rows) {
-        this.columns = columns;
-        this.rows    = rows;
-    }
-
-    public Column [] getColumns() {
-        return columns;
-    }
-
-    /** Sort rows (by km). */
-    public void sortRows() {
-        Collections.sort(rows);
-    }
-
-    /**
-     * @param km Given kilometer.
-     * @param qs Given Q values.
-     * @param ws output parameter.
-     */
-    public double [] interpolateW(double km, double [] qs, double [] ws) {
-        return interpolateW(km, qs, ws, null);
-    }
-
-
-    /**
-     * @param ws (output parameter), gets returned.
-     * @return output parameter ws.
-     */
-    public double [] interpolateW(
-        double      km,
-        double []   qs,
-        double []   ws,
-        Calculation errors
-    ) {
-        int rowIndex = Collections.binarySearch(rows, new Row(km));
-
-        QPosition qPosition = new QPosition();
-
-        if (rowIndex >= 0) { // direct row match
-            Row row = rows.get(rowIndex);
-            for (int i = 0; i < qs.length; ++i) {
-                if (getQPosition(km, qs[i], qPosition) == null) {
-                    if (errors != null) {
-                        errors.addProblem(km, "cannot.find.q", qs[i]);
-                    }
-                    ws[i] = Double.NaN;
-                }
-                else {
-                    if (Double.isNaN(ws[i] = row.getW(qPosition))
-                    && errors != null) {
-                        errors.addProblem(
-                            km, "cannot.find.w.for.q", qs[i]);
-                    }
-                }
-            }
-        }
-        else { // needs bilinear interpolation
-            rowIndex = -rowIndex -1;
-
-            if (rowIndex < 1 || rowIndex >= rows.size()) {
-                // do not extrapolate
-                Arrays.fill(ws, Double.NaN);
-                if (errors != null) {
-                    errors.addProblem(km, "km.not.found");
-                }
-            }
-            else {
-                Row r1 = rows.get(rowIndex-1);
-                Row r2 = rows.get(rowIndex);
-                r1.interpolateW(r2, km, qs, ws, this, errors);
-            }
-        }
-
-        return ws;
-    }
-
-    public double [] getMinMaxQ(double km) {
-        return getMinMaxQ(km, new double [2]);
-    }
-
-    public double [] getMinMaxQ(double km, double [] result) {
-        double minQ =  Double.MAX_VALUE;
-        double maxQ = -Double.MAX_VALUE;
-
-        for (int i = 0; i < columns.length; ++i) {
-            double q = columns[i].getQRangeTree().findQ(km);
-            if (!Double.isNaN(q)) {
-                if (q < minQ) minQ = q;
-                if (q > maxQ) maxQ = q;
-            }
-        }
-
-        if (minQ < Double.MAX_VALUE) {
-            result[0] = minQ;
-            result[1] = maxQ;
-            return result;
-        }
-
-        return null;
-    }
-
-    public double [] getMinMaxQ(double from, double to, double step) {
-        double [] result = new double[2];
-
-        double minQ =  Double.MAX_VALUE;
-        double maxQ = -Double.MAX_VALUE;
-
-        if (from > to) {
-            double tmp = from;
-            from = to;
-            to = tmp;
-        }
-
-        step = Math.max(Math.abs(step), 0.0001);
-
-        double d = from;
-        for (; d <= to; d += step) {
-            if (getMinMaxQ(d, result) != null) {
-                if (result[0] < minQ) minQ = result[0];
-                if (result[1] > maxQ) maxQ = result[1];
-            }
-        }
-
-        if (d != to) {
-            if (getMinMaxQ(to, result) != null) {
-                if (result[0] < minQ) minQ = result[0];
-                if (result[1] > maxQ) maxQ = result[1];
-            }
-        }
-
-        return minQ < Double.MAX_VALUE
-            ? new double [] { minQ, maxQ }
-            : null;
-    }
-
-    public double [] getMinMaxW(double km) {
-        return getMinMaxW(km, new double [2]);
-
-    }
-    public double [] getMinMaxW(double km, double [] result) {
-        int rowIndex = Collections.binarySearch(rows, new Row(km));
-
-        if (rowIndex >= 0) {
-            return rows.get(rowIndex).getMinMaxW(result);
-        }
-
-        rowIndex = -rowIndex -1;
-
-        if (rowIndex < 1 || rowIndex >= rows.size()) {
-            // do not extrapolate
-            return null;
-        }
-
-        Row r1 = rows.get(rowIndex-1);
-        Row r2 = rows.get(rowIndex);
-
-        return r1.getMinMaxW(r2, km, result);
-    }
-
-    public double [] getMinMaxW(double from, double to, double step) {
-        double [] result = new double[2];
-
-        double minW =  Double.MAX_VALUE;
-        double maxW = -Double.MAX_VALUE;
-
-        if (from > to) {
-            double tmp = from;
-            from = to;
-            to = tmp;
-        }
-
-        step = Math.max(Math.abs(step), 0.0001);
-
-        double d = from;
-        for (; d <= to; d += step) {
-            if (getMinMaxW(d, result) != null) {
-                if (result[0] < minW) minW = result[0];
-                if (result[1] > maxW) maxW = result[1];
-            }
-        }
-
-        if (d != to) {
-            if (getMinMaxW(to, result) != null) {
-                if (result[0] < minW) minW = result[0];
-                if (result[1] > maxW) maxW = result[1];
-            }
-        }
-
-        return minW < Double.MAX_VALUE
-            ? new double [] { minW, maxW }
-            : null;
-    }
-
-    /**
-     * Interpolate W and Q values at a given km.
-     */
-    public double [][] interpolateWQ(double km) {
-        return interpolateWQ(km, null);
-    }
-
-    /**
-     * Interpolate W and Q values at a given km.
-     *
-     * @param errors where to store errors.
-     *
-     * @return double double array, first index Ws, second Qs.
-     */
-    public double [][] interpolateWQ(double km, Calculation errors) {
-        return interpolateWQ(km, DEFAULT_Q_STEPS, errors);
-    }
-
-
-    /**
-     * Interpolate W and Q values at a given km.
-     */
-    public double [][] interpolateWQ(double km, int steps, Calculation errors) {
-
-        int rowIndex = Collections.binarySearch(rows, new Row(km));
-
-        if (rowIndex >= 0) { // direct row match
-            Row row = rows.get(rowIndex);
-            return row.interpolateWQ(steps, this, errors);
-        }
-
-        rowIndex = -rowIndex -1;
-
-        if (rowIndex < 1 || rowIndex >= rows.size()) {
-            // do not extrapolate
-            if (errors != null) {
-                errors.addProblem(km, "km.not.found");
-            }
-            return new double[2][0];
-        }
-
-        Row r1 = rows.get(rowIndex-1);
-        Row r2 = rows.get(rowIndex);
-
-        return r1.interpolateWQ(r2, km, steps, this, errors);
-    }
-
-    public boolean interpolate(
-        double    km,
-        double [] out,
-        QPosition qPosition,
-        Function  qFunction
-    ) {
-        int R1 = rows.size()-1;
-
-        out[1] = qFunction.value(getQ(qPosition, km));
-
-        if (Double.isNaN(out[1])) {
-            return false;
-        }
-
-        QPosition nPosition = new QPosition();
-        if (getQPosition(km, out[1], nPosition) == null) {
-            return false;
-        }
-
-        int rowIndex = Collections.binarySearch(rows, new Row(km));
-
-        if (rowIndex >= 0) {
-            // direct row match
-            out[0] = rows.get(rowIndex).getW(nPosition);
-            return !Double.isNaN(out[0]);
-        }
-
-        rowIndex = -rowIndex -1;
-
-        if (rowIndex < 1 || rowIndex > R1) {
-            // do not extrapolate
-            return false;
-        }
-
-        Row r1 = rows.get(rowIndex-1);
-        Row r2 = rows.get(rowIndex);
-        out[0] = r1.getW(r2, km, nPosition);
-
-        return !Double.isNaN(out[0]);
-    }
-
-
-    /**
-     * Look up interpolation of a Q at given positions.
-     *
-     * @param q           the non-interpolated Q value.
-     * @param referenceKm the reference km (e.g. gauge position).
-     * @param kms         positions for which to interpolate.
-     * @param ws          (output) resulting interpolated ws.
-     * @param qs          (output) resulting interpolated qs.
-     * @param errors      calculation object to store errors.
-     */
-    public QPosition interpolate(
-        double      q,
-        double      referenceKm,
-        double []   kms,
-        double []   ws,
-        double []   qs,
-        Calculation errors
-    ) {
-        return interpolate(
-            q, referenceKm, kms, ws, qs, 0, kms.length, errors);
-    }
-
-    public QPosition interpolate(
-        double      q,
-        double      referenceKm,
-        double []   kms,
-        double []   ws,
-        double []   qs,
-        int         startIndex,
-        int         length,
-        Calculation errors
-    ) {
-        QPosition qPosition = getQPosition(referenceKm, q);
-
-        if (qPosition == null) {
-            // we cannot locate q at km
-            Arrays.fill(ws, Double.NaN);
-            Arrays.fill(qs, Double.NaN);
-            if (errors != null) {
-                errors.addProblem(referenceKm, "cannot.find.q", q);
-            }
-            return null;
-        }
-
-        Row kmKey = new Row();
-
-        int R1 = rows.size()-1;
-
-        for (int i = startIndex, end = startIndex+length; i < end; ++i) {
-
-            if (Double.isNaN(qs[i] = getQ(qPosition, kms[i]))) {
-                if (errors != null) {
-                    errors.addProblem(kms[i], "cannot.find.q", q);
-                }
-                ws[i] = Double.NaN;
-                continue;
-            }
-
-            kmKey.km = kms[i];
-            int rowIndex = Collections.binarySearch(rows, kmKey);
-
-            if (rowIndex >= 0) {
-                // direct row match
-                if (Double.isNaN(ws[i] = rows.get(rowIndex).getW(qPosition))
-                && errors != null) {
-                    errors.addProblem(kms[i], "cannot.find.w.for.q", q);
-                }
-                continue;
-            }
-
-            rowIndex = -rowIndex -1;
-
-            if (rowIndex < 1 || rowIndex > R1) {
-                // do not extrapolate
-                if (errors != null) {
-                    errors.addProblem(kms[i], "km.not.found");
-                }
-                ws[i] = Double.NaN;
-                continue;
-            }
-            Row r1 = rows.get(rowIndex-1);
-            Row r2 = rows.get(rowIndex);
-
-            if (Double.isNaN(ws[i] = r1.getW(r2, kms[i], qPosition))
-            && errors != null) {
-                errors.addProblem(kms[i], "cannot.find.w.for.q", q);
-            }
-        }
-
-        return qPosition;
-    }
-
-    /**
-     * Linearly interpolate w at a km at a column of two rows.
-     *
-     * @param km   position for which to interpolate.
-     * @param row1 first row.
-     * @param row2 second row.
-     * @param col  column-index at which to look.
-     *
-     * @return Linearly interpolated w, NaN if one of the given rows was null.
-     */
-    public static double linearW(double km, Row row1, Row row2, int col) {
-        if (row1 == null || row2 == null) {
-            return Double.NaN;
-        }
-
-        return Linear.linear(km,
-            row1.km, row2.km,
-            row1.ws[col], row2.ws[col]);
-    }
-
-    /**
-     * Do interpolation/lookup of W and Q within columns (i.e. ignoring values
-     * of other columns).
-     * @param km position (km) at which to interpolate/lookup.
-     * @return [[q0, q1, .. qx] , [w0, w1, .. wx]] (can contain NaNs)
-     */
-    public double [][] interpolateWQColumnwise(double km) {
-        log.debug("WstValueTable.interpolateWQColumnwise");
-        double [] qs = new double[columns.length];
-        double [] ws = new double[columns.length];
-
-        // Find out row from where we will start searching.
-        int rowIndex = Collections.binarySearch(rows, new Row(km));
-
-        if (rowIndex < 0) {
-            rowIndex = -rowIndex -1;
-        }
-
-        // TODO Beyond definition, we could stop more clever.
-        if (rowIndex >= rows.size()) {
-            rowIndex = rows.size() -1;
-        }
-
-        Row startRow = rows.get(rowIndex);
-
-        for (int col = 0; col < columns.length; col++) {
-            qs[col] = columns[col].getQRangeTree().findQ(km);
-            if (startRow.km == km && startRow.ws[col] != Double.NaN) {
-                // Great. W is defined at km.
-                ws[col] = startRow.ws[col];
-                continue;
-            }
-
-            // Search neighbouring rows that define w at this col.
-            Row rowBefore = null;
-            Row rowAfter  = null;
-            for (int before = rowIndex -1; before >= 0; before--) {
-                if (!Double.isNaN(rows.get(before).ws[col])) {
-                    rowBefore = rows.get(before);
-                    break;
-                }
-            }
-            if (rowBefore != null) {
-                for (int after = rowIndex, R = rows.size(); after < R; after++) {
-                    if (!Double.isNaN(rows.get(after).ws[col])) {
-                        rowAfter = rows.get(after);
-                        break;
-                    }
-                }
-            }
-
-            ws[col] = linearW(km, rowBefore, rowAfter, col);
-        }
-
-        return new double [][] {qs, ws};
-    }
-
-    public double [] findQsForW(double km, double w) {
-
-        int rowIndex = Collections.binarySearch(rows, new Row(km));
-
-        if (rowIndex >= 0) {
-            return rows.get(rowIndex).findQsForW(w, this);
-        }
-
-        rowIndex = -rowIndex - 1;
-
-        if (rowIndex < 1 || rowIndex >= rows.size()) {
-            // Do not extrapolate.
-            return new double[0];
-        }
-
-        // Needs bilinear interpolation.
-        Row r1 = rows.get(rowIndex-1);
-        Row r2 = rows.get(rowIndex);
-
-        return r1.findQsForW(r2, w, km, this);
-    }
-
-    protected SplineFunction createSpline(double km, Calculation errors) {
-
-        int rowIndex = Collections.binarySearch(rows, new Row(km));
-
-        if (rowIndex >= 0) {
-            SplineFunction sf = rows.get(rowIndex).createSpline(this, errors);
-            if (sf == null && errors != null) {
-                errors.addProblem(km, "cannot.create.wq.relation");
-            }
-            return sf;
-        }
-
-        rowIndex = -rowIndex - 1;
-
-        if (rowIndex < 1 || rowIndex >= rows.size()) {
-            // Do not extrapolate.
-            if (errors != null) {
-                errors.addProblem(km, "km.not.found");
-            }
-            return null;
-        }
-
-        // Needs bilinear interpolation.
-        Row r1 = rows.get(rowIndex-1);
-        Row r2 = rows.get(rowIndex);
-
-        SplineFunction sf = r1.createSpline(r2, km, this, errors);
-        if (sf == null && errors != null) {
-            errors.addProblem(km, "cannot.create.wq.relation");
-        }
-
-        return sf;
-    }
-
-    /** 'Bezugslinienverfahren' */
-    public double [][] relateWs(
-        double      km1,
-        double      km2,
-        Calculation errors
-    ) {
-        return relateWs(km1, km2, RELATE_WS_SAMPLES, errors);
-    }
-
-    private static class ErrorHandler {
-
-        boolean     hasErrors;
-        Calculation errors;
-
-        ErrorHandler(Calculation errors) {
-            this.errors = errors;
-        }
-
-        void error(double km, String key, Object ... args) {
-            if (errors != null && !hasErrors) {
-                hasErrors = true;
-                errors.addProblem(km, key, args);
-            }
-        }
-    } // class ErrorHandler
-
-
-    /* TODO: Add optimized methods of relateWs to relate one
-     *       start km to many end kms. The index generation/spline stuff for
-     *       the start km is always the same.
-     */
-    public double [][] relateWs(
-        double      km1,
-        double      km2,
-        int         numSamples,
-        Calculation errors
-    ) {
-        SplineFunction sf1 = createSpline(km1, errors);
-        if (sf1 == null) {
-            return new double[2][0];
-        }
-
-        SplineFunction sf2 = createSpline(km2, errors);
-        if (sf2 == null) {
-            return new double[2][0];
-        }
-
-        PolynomialSplineFunction iQ1 = sf1.createIndexQRelation();
-        if (iQ1 == null) {
-            if (errors != null) {
-                errors.addProblem(km1, "cannot.create.index.q.relation");
-            }
-            return new double[2][0];
-        }
-
-        PolynomialSplineFunction iQ2 = sf2.createIndexQRelation();
-        if (iQ2 == null) {
-            if (errors != null) {
-                errors.addProblem(km2, "cannot.create.index.q.relation");
-            }
-            return new double[2][0];
-        }
-
-        int N = Math.min(sf1.splineQs.length, sf2.splineQs.length);
-        double stepWidth = N/(double)numSamples;
-
-        PolynomialSplineFunction qW1 = sf1.spline;
-        PolynomialSplineFunction qW2 = sf2.spline;
-
-        TDoubleArrayList ws1 = new TDoubleArrayList(numSamples);
-        TDoubleArrayList ws2 = new TDoubleArrayList(numSamples);
-        TDoubleArrayList qs1 = new TDoubleArrayList(numSamples);
-        TDoubleArrayList qs2 = new TDoubleArrayList(numSamples);
-
-        ErrorHandler err = new ErrorHandler(errors);
-
-        int i = 0;
-        for (double p = 0d; p <= N-1; p += stepWidth, ++i) {
-
-            double q1;
-            try {
-                q1 = iQ1.value(p);
-            }
-            catch (ArgumentOutsideDomainException aode) {
-                err.error(km1, "w.w.qkm1.failed", p);
-                continue;
-            }
-
-            double w1;
-            try {
-                w1 = qW1.value(q1);
-            }
-            catch (ArgumentOutsideDomainException aode) {
-                err.error(km1, "w.w.wkm1.failed", q1, p);
-                continue;
-            }
-
-            double q2;
-            try {
-                q2 = iQ2.value(p);
-            }
-            catch (ArgumentOutsideDomainException aode) {
-                err.error(km2, "w.w.qkm2.failed", p);
-                continue;
-            }
-
-            double w2;
-            try {
-                w2 = qW2.value(q2);
-            }
-            catch (ArgumentOutsideDomainException aode) {
-                err.error(km2, "w.w.wkm2.failed", q2, p);
-                continue;
-            }
-
-            ws1.add(w1);
-            ws2.add(w2);
-            qs1.add(q1);
-            qs2.add(q2);
-        }
-
-        return new double [][] {
-            ws1.toNativeArray(),
-            qs1.toNativeArray(),
-            ws2.toNativeArray(),
-            qs2.toNativeArray() };
-    }
-
-    public QPosition getQPosition(double km, double q) {
-        return getQPosition(km, q, new QPosition());
-    }
-
-    public QPosition getQPosition(double km, double q, QPosition qPosition) {
-
-        if (columns.length == 0) {
-            return null;
-        }
-
-        double qLast = columns[0].getQRangeTree().findQ(km);
-
-        if (Math.abs(qLast - q) < 0.00001) {
-            return qPosition.set(0, 1d);
-        }
-
-        for (int i = 1; i < columns.length; ++i) {
-            double qCurrent = columns[i].getQRangeTree().findQ(km);
-            if (Math.abs(qCurrent - q) < 0.00001) {
-                return qPosition.set(i, 1d);
-            }
-
-            double qMin, qMax;
-            if (qLast < qCurrent) { qMin = qLast; qMax = qCurrent; }
-            else                  { qMin = qCurrent; qMax = qLast; }
-
-            if (q > qMin && q < qMax) {
-                double weight = Linear.factor(q, qLast, qCurrent);
-                return qPosition.set(i, weight);
-            }
-            qLast = qCurrent;
-        }
-
-        return null;
-    }
-
-    public double getQIndex(int index, double km) {
-        return columns[index].getQRangeTree().findQ(km);
-    }
-
-    public double getQ(QPosition qPosition, double km) {
-        int    index  = qPosition.index;
-        double weight = qPosition.weight;
-
-        if (weight == 1d) {
-            return columns[index].getQRangeTree().findQ(km);
-        }
-        double q1 = columns[index-1].getQRangeTree().findQ(km);
-        double q2 = columns[index  ].getQRangeTree().findQ(km);
-        return Linear.weight(weight, q1, q2);
-    }
-
-    public double [][] interpolateTabulated(double km) {
-        return interpolateTabulated(km, new double[2][columns.length]);
-    }
-
-    public double [][] interpolateTabulated(double km, double [][] result) {
-
-        int rowIndex = Collections.binarySearch(rows, new Row(km));
-
-        if (rowIndex >= 0) {
-            // Direct hit -> copy ws.
-            Row row = rows.get(rowIndex);
-            System.arraycopy(
-                row.ws, 0, result[0], 0,
-                Math.min(row.ws.length, result[0].length));
-        }
-        else {
-            rowIndex = -rowIndex -1;
-            if (rowIndex < 1 || rowIndex >= rows.size()) {
-                // Out of bounds.
-                return null;
-            }
-            // Interpolate ws.
-            Row r1 = rows.get(rowIndex-1);
-            Row r2 = rows.get(rowIndex);
-            double factor = Linear.factor(km, r1.km, r2.km);
-            Linear.weight(factor, r1.ws, r2.ws, result[0]);
-        }
-
-        double [] qs = result[1];
-        for (int i = Math.min(qs.length, columns.length)-1; i >= 0; --i) {
-            qs[i] = columns[i].getQRangeTree().findQ(km);
-        }
-        return result;
-    }
-
-
-    /** Find ranges that are between km1 and km2 (inclusive?) */
-    public List<Range> findSegments(double km1, double km2) {
-        return columns.length != 0
-            ? columns[columns.length-1].getQRangeTree().findSegments(km1, km2)
-            : Collections.<Range>emptyList();
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WstValueTableCacheKey.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.io.Serializable;
-
-/**
- * Cache Key (identifier) for WstValueTables.
- */
-public final class WstValueTableCacheKey
-implements         Serializable
-{
-    public static final String CACHE_NAME = "wst-value-table";
-
-    private int riverId;
-    private int kind;
-
-    public WstValueTableCacheKey(int riverId, int kind) {
-        this.riverId = riverId;
-        this.kind    = kind;
-    }
-
-    public int hashCode() {
-        return (riverId << 8) | kind;
-    }
-
-    public boolean equals(Object other) {
-        if (!(other instanceof WstValueTableCacheKey)) {
-            return false;
-        }
-        WstValueTableCacheKey o = (WstValueTableCacheKey)other;
-        return riverId == o.riverId && kind == o.kind;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WstValueTableFactory.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,520 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.ArrayList;
-
-import net.sf.ehcache.Cache;
-import net.sf.ehcache.Element;
-
-import de.intevation.flys.artifacts.cache.CacheFactory;
-
-import de.intevation.flys.backend.SessionHolder;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.flys.model.River;
-import de.intevation.flys.model.Wst;
-
-import org.hibernate.Session;
-import org.hibernate.Query;
-import org.hibernate.SQLQuery;
-
-import org.hibernate.type.StandardBasicTypes;
-
-/**
- * Creates WstValueTable s from database.
- * WstValueTable s are used to interpolate given w/q/km values.
- */
-public class WstValueTableFactory
-{
-    private static Logger log = Logger.getLogger(WstValueTableFactory.class);
-
-    public static final int DEFAULT_KIND = 0;
-
-    // TODO: put this into a property file
-
-    public static final String HQL_WST =
-        "from Wst where river=:river and kind=:kind";
-
-    public static final String SQL_SELECT_NAMES_POS =
-        "SELECT position, name FROM wst_columns " +
-        "WHERE wst_id = :wst_id ORDER BY position";
-
-    /** Select Qs for wst (view sorted by column). */
-    public static final String SQL_SELECT_QS =
-        "SELECT column_pos, q, a, b FROM wst_q_values " +
-        "WHERE wst_id = :wst_id";
-
-    // (sorted by km)
-    public static final String SQL_SELECT_WS =
-        "SELECT km, w, column_pos FROM wst_w_values " +
-        "WHERE wst_id = :wst_id";
-
-    /** Statement to query qranges of a single column. */
-    public static final String SQL_SELECT_QS_AT_COL =
-        "SELECT q, a, b FROM wst_q_values " +
-        "WHERE wst_id = :wst_id AND column_pos = :column_pos";
-
-    // (sorted by km)
-    public static final String SQL_SELECT_WS_AT_COL =
-        "SELECT km, w FROM wst_w_values " +
-        "WHERE wst_id = :wst_id AND column_pos = :column_pos";
-
-
-    private WstValueTableFactory() {
-    }
-
-
-    public static WstValueTable getTable(River river) {
-        return getTable(river, DEFAULT_KIND);
-    }
-
-
-    /**
-     * Get WstValueTable to interpolate values of a given Wst.
-     */
-    public static WstValueTable getTable(int wst_id) {
-
-        Cache cache = CacheFactory.getCache(WstValueTableCacheKey.CACHE_NAME);
-
-        WstValueTableCacheKey cacheKey;
-
-        if (cache != null) {
-            // "-1" is the symbolic river-id for "no river, but wst_id".
-            cacheKey = new WstValueTableCacheKey(-1, wst_id);
-            Element element = cache.get(cacheKey);
-            if (element != null) {
-                log.debug("Got specific wst value table from cache");
-                return (WstValueTable) element.getValue();
-            }
-        }
-        else {
-            cacheKey = null;
-        }
-
-        Session session = SessionHolder.HOLDER.get();
-
-        // Fetch data for one column only.
-
-        WstValueTable.Column [] columns = loadColumns(session, wst_id);
-        int [] map = loadQRangesMap(session, columns, wst_id);
-        List<WstValueTable.Row> rows = loadRows(session, wst_id, columns.length, map);
-
-        WstValueTable valueTable = new WstValueTable(columns, rows);
-
-        if (valueTable != null && cacheKey != null) {
-            log.debug("Store wst value table in cache");
-            Element element = new Element(cacheKey, valueTable);
-            cache.put(element);
-        }
-
-        return valueTable;
-    }
-
-    /**
-     * Get Table for a specific column of a wst.
-     */
-    public static WstValueTable getWstColumnTable(int wst_id, int col_pos) {
-
-        Cache cache = CacheFactory.getCache(WstValueTableCacheKey.CACHE_NAME);
-
-        WstValueTableCacheKey cacheKey;
-
-        if (cache != null) {
-            // A negaitve/negative number is the symbolic 'river-id' for
-            // "no river and kind but wst_id and colpos".
-            cacheKey = new WstValueTableCacheKey(-wst_id, -col_pos);
-            Element element = cache.get(cacheKey);
-            if (element != null) {
-                log.debug("Got specific wst value table from cache");
-                return (WstValueTable) element.getValue();
-            }
-        }
-        else {
-            cacheKey = null;
-        }
-
-        Session session = SessionHolder.HOLDER.get();
-
-        // Fetch data for one column only.
-
-        WstValueTable.Column [] columns = loadColumn(session, wst_id, col_pos);
-        loadQRanges(session, columns, wst_id, col_pos);
-        List<WstValueTable.Row> rows = loadRowsOneColumn(session, wst_id, col_pos);
-
-        WstValueTable valueTable = new WstValueTable(columns, rows);
-
-        if (valueTable != null && cacheKey != null) {
-            log.debug("Store wst value table in cache (wst: "
-                + wst_id + "/ col: " + col_pos + ")");
-            Element element = new Element(cacheKey, valueTable);
-            cache.put(element);
-        }
-
-        return valueTable;
-    }
-
-
-    /**
-     * Get table for first wst of given kind at given river.
-     */
-    public static WstValueTable getTable(River river, int kind) {
-
-        Cache cache = CacheFactory.getCache(WstValueTableCacheKey.CACHE_NAME);
-
-        WstValueTableCacheKey cacheKey;
-
-        if (cache != null) {
-            cacheKey = new WstValueTableCacheKey(river.getId(), kind);
-            Element element = cache.get(cacheKey);
-            if (element != null) {
-                log.debug("got wst value table from cache");
-                return (WstValueTable)element.getValue();
-            }
-        }
-        else {
-            cacheKey = null;
-        }
-
-        WstValueTable valueTable = getTableUncached(river, kind);
-
-        if (valueTable != null && cacheKey != null) {
-            log.debug("store wst value table in cache");
-            Element element = new Element(cacheKey, valueTable);
-            cache.put(element);
-        }
-
-        return valueTable;
-    }
-
-    public static WstValueTable getTableUncached(River river) {
-        return getTableUncached(river, DEFAULT_KIND);
-    }
-
-    public static WstValueTable getTableUncached(River river, int kind) {
-
-        Session session = SessionHolder.HOLDER.get();
-
-        Wst wst = loadWst(session, river, kind);
-
-        if (wst == null) {
-            return null;
-        }
-
-        WstValueTable.Column [] columns = loadColumns(session, wst);
-
-        int map [] = loadQRangesMap(session, columns, wst);
-
-        List<WstValueTable.Row> rows =
-            loadRows(session, wst.getId(), columns.length, map);
-
-        return new WstValueTable(columns, rows);
-    }
-
-    /**
-     * @param kind Kind of wst.
-     */
-    protected static Wst loadWst(Session session, River river, int kind) {
-        Query query = session.createQuery(HQL_WST);
-        query.setParameter("river", river);
-        query.setInteger("kind",    kind);
-
-        List<Wst> wsts = query.list();
-
-        // TODO Multiple wsts can match, why return just the first one?
-        return wsts.isEmpty() ? null : wsts.get(0);
-    }
-
-
-    /**
-     * Load rows with a single columns result.
-     *
-     * @param session    session to use for querying db.
-     * @param wstId     id of wst (in db).
-     * @param column_pos the column_pos (within the db) of the wst_value_table
-     *                   of which the values shall be fetched.
-     *
-     * @return resultant rows.
-     */
-    protected static List<WstValueTable.Row> loadRowsOneColumn(
-        Session session,
-        int     wstId,
-        int     column_pos
-    ) {
-        SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_WS_AT_COL)
-            .addScalar("km", StandardBasicTypes.DOUBLE)
-            .addScalar("w",  StandardBasicTypes.DOUBLE);
-
-        sqlQuery.setInteger("wst_id", wstId);
-        sqlQuery.setInteger("column_pos", column_pos);
-
-        List<Object []> results = sqlQuery.list();
-
-        double [] ws = null;
-
-        List<WstValueTable.Row> rows =
-            new ArrayList<WstValueTable.Row>(results.size());
-
-        // Walk over rows.
-        for (Object [] result: results) {
-            ws = new double[1];
-            WstValueTable.Row row =
-                new WstValueTable.Row((Double) result[0], ws);
-            rows.add(row);
-
-            Double w = (Double) result[1];
-            ws[0] = w != null ? w : Double.NaN;
-        }
-
-        return rows;
-    }
-
-    protected static List<WstValueTable.Row> loadRows(
-        Session session,
-        int     wst_id,
-        int     numColumns
-    ) {
-        return loadRows(session, wst_id, numColumns, null);
-    }
-
-    protected static List<WstValueTable.Row> loadRows(
-        Session session,
-        int     wst_id,
-        int     numColumns,
-        int []  map
-    ) {
-        SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_WS)
-            .addScalar("km",         StandardBasicTypes.DOUBLE)
-            .addScalar("w",          StandardBasicTypes.DOUBLE)
-            .addScalar("column_pos", StandardBasicTypes.INTEGER);
-
-        sqlQuery.setInteger("wst_id", wst_id);
-
-        List<Object []> results = sqlQuery.list();
-
-        int lastColumn = Integer.MAX_VALUE;
-        double [] ws = null;
-
-        ArrayList<WstValueTable.Row> rows = new ArrayList<WstValueTable.Row>();
-
-        for (Object [] result: results) {
-            int column = (Integer)result[2];
-            if (column < lastColumn) {
-                ws = new double[numColumns];
-                Arrays.fill(ws, Double.NaN);
-                WstValueTable.Row row =
-                    new WstValueTable.Row((Double)result[0], ws);
-                rows.add(row);
-            }
-            Double w = (Double)result[1];
-            int index = map != null ? map[column] : column;
-            ws[index] = w != null ? w : Double.NaN;
-            lastColumn = column;
-        }
-
-        rows.trimToSize();
-        return rows;
-    }
-
-    protected static List<WstValueTable.Row> loadRows(
-        Session session,
-        Wst     wst,
-        int     numColumns
-    ) {
-        return loadRows(session, wst.getId(), numColumns);
-    }
-
-
-    protected static WstValueTable.Column [] loadColumn(
-        Session session,
-        int wst_id,
-        int col_pos
-    ) {
-        return new WstValueTable.Column [] {
-            new WstValueTable.Column(WKmsFactory.getWKmsName(col_pos, wst_id))};
-    }
-
-
-    /**
-     * Get columns from wst-id.
-     */
-    protected static WstValueTable.Column [] loadColumns(
-        Session session,
-        int wst_id
-    ) {
-        SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_NAMES_POS)
-            .addScalar("position",   StandardBasicTypes.INTEGER)
-            .addScalar("name",       StandardBasicTypes.STRING);
-
-        sqlQuery.setInteger("wst_id", wst_id);
-
-        List<Object []> columnNames = sqlQuery.list();
-
-        WstValueTable.Column [] columns =
-            new WstValueTable.Column[columnNames.size()];
-
-        for (int i = 0; i < columns.length; ++i) {
-            columns[i] = new WstValueTable.Column(
-                (String)columnNames.get(i)[1]);
-        }
-        return columns;
-    }
-
-    /**
-     * Get columns from Wst.
-     */
-    protected static WstValueTable.Column [] loadColumns(
-        Session session,
-        Wst     wst
-    ) {
-        return loadColumns(session, wst.getId());
-    }
-
-
-    /**
-     * Build a QRange-Tree.
-     */
-    protected static void loadQRanges(
-        Session                 session,
-        WstValueTable.Column [] columns,
-        int                     wst_id,
-        int                     column_pos
-    ) {
-        SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_QS_AT_COL)
-            .addScalar("q", StandardBasicTypes.DOUBLE)
-            .addScalar("a", StandardBasicTypes.DOUBLE)
-            .addScalar("b", StandardBasicTypes.DOUBLE);
-
-        sqlQuery.setInteger("wst_id",     wst_id);
-        sqlQuery.setInteger("column_pos", column_pos);
-
-        List<Object []> qRanges = sqlQuery.list();
-
-        int qSize = qRanges.size();
-
-        QRangeTree qRangeTree = new QRangeTree(
-            qRanges, QRangeTree.WITHOUT_COLUMN, 0, qSize);
-        columns[0].setQRangeTree(qRangeTree);
-    }
-
-    protected static int [] loadQRangesMap(
-        Session                 session,
-        WstValueTable.Column [] columns,
-        int                     wst_id
-    ) {
-        SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_QS)
-            .addScalar("column_pos", StandardBasicTypes.INTEGER)
-            .addScalar("q",          StandardBasicTypes.DOUBLE)
-            .addScalar("a",          StandardBasicTypes.DOUBLE)
-            .addScalar("b",          StandardBasicTypes.DOUBLE);
-
-        sqlQuery.setInteger("wst_id", wst_id);
-
-        List<Object []> qRanges = sqlQuery.list();
-
-        int     start      = -1;
-        int     Q          = qRanges.size();
-        Integer lastColumn = null;
-
-        for (int i = 0; i < Q; ++i) {
-            Object [] qRange = qRanges.get(i);
-            Integer columnId = (Integer)qRange[0];
-            if (lastColumn == null) {
-                lastColumn = columnId;
-                start = i;
-            }
-            else if (!lastColumn.equals(columnId)) {
-                QRangeTree qRangeTree = new QRangeTree(qRanges, start, i);
-                columns[lastColumn].setQRangeTree(qRangeTree);
-                lastColumn = columnId;
-                start = i;
-            }
-        }
-
-        if (start != -1) {
-            QRangeTree qRangeTree = new QRangeTree(qRanges, start, Q);
-            columns[lastColumn].setQRangeTree(qRangeTree);
-        }
-
-        return sortColumnsByAverageQ(columns);
-
-        /* This is debug code to visualize the q ranges trees
-
-        java.io.PrintWriter out = null;
-        try {
-            out = new java.io.PrintWriter(
-                new java.io.FileWriter(
-                    "/tmp/qranges" + System.currentTimeMillis() + ".dot"));
-
-            out.println("graph \"Q ranges trees\" {");
-
-            for (int i = 0; i < columns.length; ++i) {
-                QRangeTree tree = columns[i].getQRangeTree();
-                out.println(tree.toGraph());
-            }
-
-            out.println("}");
-
-            out.flush();
-        }
-        catch (java.io.IOException ioe) {
-            log.error(ioe);
-        }
-        finally {
-            if (out != null) {
-                out.close();
-            }
-        }
-        */
-    }
-
-    private static final class QIndex implements Comparable<QIndex> {
-        double q;
-        int    index;
-
-        QIndex(double q, int index) {
-            this.q     = q;
-            this.index = index;
-        }
-
-        @Override
-        public int compareTo(QIndex other) {
-            double diff = q - other.q;
-            if (diff < 0d) return -1;
-            if (diff > 0d) return +1;
-            return 0;
-        }
-    } // class QIndex
-
-    /** Ensure that the q colums are sorted in ascending order. */
-    protected static int [] sortColumnsByAverageQ(WstValueTable.Column [] columns) {
-        QIndex [] order = new QIndex[columns.length];
-        for (int i = 0; i < order.length; ++i) {
-            QRangeTree tree = columns[i].getQRangeTree();
-            double avg = tree.averageQ();
-            double max = tree.maxQ();
-            double q   = (avg+max)*0.5d;
-            order[i] = new QIndex(q, i);
-        }
-        Arrays.sort(order);
-        WstValueTable.Column [] copy = new WstValueTable.Column[order.length];
-        int [] map = new int[order.length];
-        for (int i = 0; i < copy.length; ++i) {
-            copy[i] = columns[order[i].index];
-            map[order[i].index] = i;
-        }
-        System.arraycopy(copy, 0, columns, 0, order.length);
-        return map;
-    }
-
-    protected static int [] loadQRangesMap(
-        Session                 session,
-        WstValueTable.Column [] columns,
-        Wst                     wst
-    ) {
-        return loadQRangesMap(session, columns, wst.getId());
-    }
-
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/ZoomScale.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,75 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeMap;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.flys.artifacts.math.Linear;
-
-
-public class ZoomScale
-{
-    private static Logger logger = Logger.getLogger(ZoomScale.class);
-
-    private HashMap<String, TreeMap<Double, Double>> rivers;
-
-    public ZoomScale() {
-        this.rivers = new HashMap<String, TreeMap<Double, Double>>();
-    }
-
-    public ZoomScale(String river) {
-        this();
-        rivers.put(river, new TreeMap<Double, Double>());
-    }
-
-    public double getRadius(String river, double lower, double upper) {
-        double range = Math.abs(upper) - Math.abs(lower);
-        TreeMap<Double, Double> ranges = rivers.get(river);
-        if (ranges == null) {
-            return 0.001;
-        }
-        Map.Entry<Double, Double> next = ranges.higherEntry(range);
-        Map.Entry<Double, Double> prev = ranges.lowerEntry(range);
-        double x0 = 0d;
-        double x1 = 0d;
-        double y0 = 0d;
-        double y1 = 0d;
-        if (prev == null && next != null) {
-            x1 = next.getKey();
-            y1 = next.getValue();
-        }
-        else if (prev != null && next == null) {
-            return prev.getValue();
-        }
-        else {
-            x0 = prev.getKey();
-            x1 = next.getKey();
-            y0 = prev.getValue();
-            y1 = next.getValue();
-        }
-        return Linear.linear(range, x0, x1, y0, y1);
-    }
-
-    public void addRiver(String river) {
-        if (!this.rivers.containsKey(river)) {
-            this.rivers.put(river, new TreeMap<Double, Double>());
-        }
-    }
-
-    public Set<String> getRivers() {
-        return this.rivers.keySet();
-    }
-
-    public void addRange(String river, double range, double radius) {
-        if (this.rivers.containsKey(river)) {
-            this.rivers.get(river).put(range, radius);
-        }
-        else {
-            this.rivers.put(river, new TreeMap<Double, Double>());
-            this.rivers.get(river).put(range, radius);
-        }
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/extreme/Curve.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,144 +0,0 @@
-package de.intevation.flys.artifacts.model.extreme;
-
-import de.intevation.flys.artifacts.math.Function;
-import de.intevation.flys.artifacts.math.NaNFunction;
-import de.intevation.flys.artifacts.math.UnivariateRealFunctionFunction;
-
-import de.intevation.flys.artifacts.math.fitting.FunctionFactory;
-
-import java.io.Serializable;
-
-import java.lang.ref.SoftReference;
-
-import org.apache.commons.math.analysis.interpolation.SplineInterpolator;
-
-import org.apache.commons.math.exception.MathIllegalArgumentException;
-
-import org.apache.log4j.Logger;
-
-/** An extrapolating W/Q function/curve. */
-public class Curve
-implements   Serializable, Function
-{
-    private static Logger log = Logger.getLogger(Curve.class);
-
-    protected double [] qs;
-    protected double [] ws;
-    protected String    function;
-    protected double [] coeffs;
-    protected double    chiSqr;
-
-    /** Suggested maximum value for q to input. */
-    protected double    suggestedMaxQ;
-
-    // The spline is pretty heavy weight so cache it with a soft ref only.
-    protected transient SoftReference<Function> spline;
-    protected transient Function                extrapolation;
-
-    public Curve() {
-    }
-
-    public Curve(
-        double [] qs,
-        double [] ws,
-        String    function,
-        double [] coeffs,
-        double    chiSqr
-    ) {
-        this.qs       = qs;
-        this.ws       = ws;
-        this.function = function;
-        this.coeffs   = coeffs;
-        this.suggestedMaxQ = Double.MAX_VALUE;
-    }
-
-    public double [] getQs() {
-        return qs;
-    }
-
-    public double [] getWs() {
-        return ws;
-    }
-
-    public String getFunction() {
-        return function;
-    }
-
-    public double [] getCoeffs() {
-        return coeffs;
-    }
-
-
-    public void setSuggestedMaxQ(double newMaxQ) {
-        this.suggestedMaxQ = newMaxQ;
-    }
-
-
-    public double getSuggestedMaxQ() {
-        return this.suggestedMaxQ;
-    }
-
-
-    /** Calculate value at given x. */
-    @Override
-    public double value(double x) {
-        if (qs == null || x < qs[0]) return Double.NaN;
-        return (x <= qs[qs.length-1]
-            ? getSpline()
-            : getExtrapolation()).value(x);
-    }
-
-    protected synchronized Function getExtrapolation() {
-        if (extrapolation == null) {
-            de.intevation.flys.artifacts.math.fitting.Function
-                f = FunctionFactory.getInstance().getFunction(function);
-
-            extrapolation = f != null
-                ? f.instantiate(coeffs)
-                : NaNFunction.INSTANCE;
-        }
-        return extrapolation;
-    }
-
-    /**
-     * Gets the chiSqr for this instance.
-     *
-     * @return The chiSqr.
-     */
-    public double getChiSqr() {
-        return this.chiSqr;
-    }
-
-    /**
-     * Sets the chiSqr for this instance.
-     *
-     * @param chiSqr The chiSqr.
-     */
-    public void setChiSqr(double chiSqr) {
-        this.chiSqr = chiSqr;
-    }
-
-    protected synchronized Function getSpline() {
-        Function sp;
-        if (spline != null) {
-            if ((sp = spline.get()) != null) {
-                return sp;
-            }
-        }
-        spline = new SoftReference<Function>(sp = createSpline());
-        return sp;
-    }
-
-    protected Function createSpline() {
-        SplineInterpolator interpolator = new SplineInterpolator();
-        try {
-            return new UnivariateRealFunctionFunction(
-                interpolator.interpolate(qs, ws));
-        }
-        catch (MathIllegalArgumentException miae) {
-            log.debug("creation on spline failed", miae);
-            return NaNFunction.INSTANCE;
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/extreme/ExtremeCalculation.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,418 +0,0 @@
-package de.intevation.flys.artifacts.model.extreme;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-import org.apache.commons.math.MathException;
-
-import org.apache.commons.math.optimization.fitting.CurveFitter;
-
-import org.apache.commons.math.optimization.general.LevenbergMarquardtOptimizer;
-
-import de.intevation.flys.artifacts.access.ExtremeAccess;
-
-import de.intevation.flys.artifacts.math.Linear;
-//import de.intevation.flys.artifacts.math.Utils;
-
-import de.intevation.flys.artifacts.math.fitting.Function;
-import de.intevation.flys.artifacts.math.fitting.FunctionFactory;
-
-import de.intevation.flys.artifacts.model.Calculation;
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.RangeWithValues;
-import de.intevation.flys.artifacts.model.RiverFactory;
-import de.intevation.flys.artifacts.model.WQKms;
-import de.intevation.flys.artifacts.model.WstValueTable;
-import de.intevation.flys.artifacts.model.WstValueTableFactory;
-
-import de.intevation.flys.model.River;
-
-import de.intevation.flys.utils.DoubleUtil;
-import de.intevation.flys.utils.KMIndex;
-
-import gnu.trove.TDoubleArrayList;
-
-import java.util.List;
-
-import java.awt.geom.Line2D;
-
-/** Calculate extrapolated W. */
-public class ExtremeCalculation
-extends      Calculation
-{
-    private static final Log log =
-        LogFactory.getLog(ExtremeCalculation.class);
-
-    protected String                river;
-    protected String                function;
-    protected double                from;
-    protected double                to;
-    protected double                step;
-    protected double                percent;
-    protected List<RangeWithValues> ranges;
-
-    public ExtremeCalculation() {
-    }
-
-    public ExtremeCalculation(ExtremeAccess access) {
-        String                river    = access.getRiver();
-        String                function = access.getFunction();
-        Double                from     = access.getFrom();
-        Double                to       = access.getTo();
-        Double                step     = access.getStep();
-        Double                percent  = access.getPercent();
-        List<RangeWithValues> ranges   = access.getRanges();
-
-        if (river == null) {
-            // TODO: i18n
-            addProblem("extreme.no.river");
-        }
-
-        if (function == null) {
-            // TODO: i18n
-            addProblem("extreme.no.function");
-        }
-
-        if (from == null) {
-            // TODO: i18n
-            addProblem("extreme.no.from");
-        }
-
-        if (to == null) {
-            // TODO: i18n
-            addProblem("extreme.no.to");
-        }
-
-        if (step == null) {
-            // TODO: i18n
-            addProblem("extreme.no.step");
-        }
-
-        if (percent == null) {
-            // TODO: i18n
-            addProblem("extreme.no.percent");
-        }
-
-        if (ranges == null) {
-            // TODO: i18n
-            addProblem("extreme.no.ranges");
-        }
-
-        if (!hasProblems()) {
-            this.river    = river;
-            this.function = function;
-            this.from     = Math.min(from, to);
-            this.to       = Math.max(from, to);
-            this.step     = Math.max(0.001d, Math.abs(step)/1000d);
-            this.percent  = Math.max(0d, Math.min(100d, percent));
-            this.ranges   = ranges;
-        }
-    }
-
-
-    /** Calculate an extreme curve (extrapolate). */
-    public CalculationResult calculate() {
-
-        WstValueTable wst = null;
-
-        River river = RiverFactory.getRiver(this.river);
-        if (river == null) {
-            // TODO: i18n
-            addProblem("extreme.no.such.river", this.river);
-        }
-        else {
-            wst = WstValueTableFactory.getTable(river);
-            if (wst == null) {
-                // TODO: i18n
-                addProblem("extreme.no.wst.table");
-            }
-        }
-
-        Function function =
-            FunctionFactory.getInstance().getFunction(this.function);
-        if (function == null) {
-            // TODO: i18n
-            addProblem("extreme.no.such.function", this.function);
-        }
-
-        return hasProblems()
-            ? new CalculationResult(this)
-            : innerCalculate(wst, function);
-    }
-
-
-    /** Name of wqkms like W(5000,6000) */
-    protected String wqkmsName(int i) {
-        StringBuilder sb = new StringBuilder("W(");
-        boolean already = false;
-        for (RangeWithValues r: ranges) {
-            double [] values = r.getValues();
-            if (i < values.length) {
-                if (already) {
-                    sb.append(", ");
-                }
-                else {
-                    already = true;
-                }
-                // TODO: i18n
-                sb.append(values[i]);
-            }
-        }
-        return sb.append(')').toString();
-    }
-
-    protected WQKms [] allocWQKms() {
-        int max = 0;
-        for (RangeWithValues r: ranges) {
-            double [] values = r.getValues();
-            if (values.length > max) {
-                max = values.length;
-            }
-        }
-        WQKms [] wqkms = new WQKms[max];
-        for (int i = 0; i < max; ++i) {
-            wqkms[i] = new WQKms(wqkmsName(i));
-        }
-        return wqkms;
-    }
-
-
-    /** Calculate an extreme curve (extrapolate). */
-    protected CalculationResult innerCalculate(
-        WstValueTable wst,
-        Function      function
-    ) {
-        RangeWithValues range = null;
-
-        double [] chiSqr = { 0d };
-
-        KMIndex<Curve> curves = new KMIndex<Curve>();
-        WQKms [] wqkms = allocWQKms();
-
-        boolean debug = log.isDebugEnabled();
-
-        from = DoubleUtil.round(from);
-        to = DoubleUtil.round(to);
-
-        for (double km = from; km <= to; km = DoubleUtil.round(km+step)) {
-
-            if (debug) {
-                log.debug("km: " + km);
-            }
-
-            boolean foundRange = false;
-
-            if (range == null || !range.inside(km)) {
-                for (RangeWithValues r: ranges) {
-                    if (r.inside(km)) {
-                        range = r;
-                        foundRange = true;
-                        break;
-                    }
-                }
-                // TODO: i18n
-                if (!foundRange) {
-                    addProblem(km, "extreme.no.range.inner");
-                    continue;
-                }
-            }
-
-            double [][] wqs = wst.interpolateTabulated(km);
-            if (wqs == null) {
-                // TODO: i18n
-                addProblem(km, "extreme.no.raw.data");
-                continue;
-            }
-
-            // XXX: This should not be necessary for model data.
-            if (!DoubleUtil.isValid(wqs)) {
-                // TODO: i18n
-                addProblem(km, "extreme.invalid.data");
-                continue;
-            }
-
-            double [][] fitWQs = extractPointsToFit(wqs);
-            if (fitWQs == null) {
-                // TODO: i18n
-                addProblem(km, "extreme.too.less.points");
-                continue;
-            }
-
-            double [] coeffs = doFitting(function, fitWQs, chiSqr);
-            if (coeffs == null) {
-                // TODO: i18n
-                addProblem(km, "extreme.fitting.failed");
-                continue;
-            }
-
-            Curve curve = new Curve(
-                wqs[1], wqs[0],
-                function.getName(),
-                coeffs,
-                chiSqr[0]);
-
-            curves.add(km, curve);
-
-            double [] values = range.getValues();
-
-            int V = Math.min(values.length, wqkms.length);
-            for (int i = 0; i < V; ++i) {
-                double q = values[i];
-                double w = curve.value(q);
-                if (Double.isNaN(w)) {
-                    // TODO: i18n
-                    addProblem(km, "extreme.evaluate.failed", values[i]);
-                }
-                else {
-                    wqkms[i].add(w, q, km);
-                }
-            }
-        }
-
-        ExtremeResult result = new ExtremeResult(curves, wqkms);
-        return new CalculationResult(result, this);
-    }
-
-    protected double [] doFitting(
-        Function    function,
-        double [][] wqs,
-        double []   chiSqr
-    ) {
-        LevenbergMarquardtOptimizer lmo = null;
-
-        double [] coeffs = null;
-
-        double [] ws = wqs[0];
-        double [] qs = wqs[1];
-
-        for (double tolerance = 1e-10; tolerance < 1e-3; tolerance *= 10d) {
-            lmo = new LevenbergMarquardtOptimizer();
-            lmo.setCostRelativeTolerance(tolerance);
-            lmo.setOrthoTolerance(tolerance);
-            lmo.setParRelativeTolerance(tolerance);
-
-            CurveFitter cf = new CurveFitter(lmo);
-
-            for (int i = 0; i < ws.length; ++i) {
-                cf.addObservedPoint(qs[i], ws[i]);
-            }
-
-            try {
-                coeffs = cf.fit(function, function.getInitialGuess());
-                break;
-            }
-            catch (MathException me) {
-                if (log.isDebugEnabled()) {
-                    log.debug("tolerance " + tolerance + " + failed.");
-                }
-            }
-        }
-        if (coeffs != null) {
-            chiSqr[0] = lmo.getChiSquare();
-        }
-        return coeffs;
-    }
-
-    protected double [][] extractPointsToFit(double [][] wqs) {
-
-        double [] ws = wqs[0];
-        double [] qs = wqs[1];
-
-        int N = Math.min(ws.length, qs.length);
-
-        if (N < 2) {
-            log.warn("Too less points for fitting");
-            return null;
-        }
-
-        double q2 = qs[N-1];
-        double w2 = ws[N-1];
-        double q1 = qs[N-2];
-        double w1 = ws[N-2];
-
-        boolean ascending = w2 > w1;
-
-        TDoubleArrayList ows = new TDoubleArrayList();
-        TDoubleArrayList oqs = new TDoubleArrayList();
-
-        oqs.add(q2); oqs.add(q1);
-        ows.add(w2); ows.add(w1);
-
-        int lastDir = -2;
-
-        for (int i = N-3; i >= 0; --i) {
-            double q = qs[i];
-            double w = ws[i];
-
-            if ((ascending && w > w1) || (!ascending && w < w1)) {
-                break;
-            }
-
-            int dir = Line2D.relativeCCW(q2, w2, q1, w1, q, w);
-            //int dir = Utils.relativeCCW(q2, w2, q1, w1, q, w);
-            if (lastDir == -2) {
-                lastDir = dir;
-            }
-            else if (lastDir != dir) {
-                break;
-            }
-
-            oqs.add(q);
-            ows.add(w);
-            w2 = w1;
-            q2 = q1;
-            w1 = w;
-            q1 = q;
-        }
-
-        oqs.reverse();
-        ows.reverse();
-
-        boolean debug = log.isDebugEnabled();
-        if (debug) {
-            log.debug("from table: " + N);
-            log.debug("after trim: " + oqs.size());
-        }
-
-        cutPercent(ows, oqs);
-
-        if (debug) {
-            log.debug("after percent cut: " + oqs.size());
-        }
-
-        return new double [][] {
-            ows.toNativeArray(),
-            oqs.toNativeArray()
-        };
-    }
-
-
-    protected void cutPercent(TDoubleArrayList ws, TDoubleArrayList qs) {
-        int N = qs.size();
-        if (percent <= 0d || N == 0) {
-            return;
-        }
-
-        double minQ = qs.getQuick(0);
-        double maxQ = qs.getQuick(N-1);
-        double factor = Math.min(Math.max(0d, percent/100d), 1d);
-        double cutQ = Linear.weight(factor, minQ, maxQ);
-        int cutIndex = 0;
-        for (; cutIndex < N; ++cutIndex) {
-            double q = qs.getQuick(cutIndex);
-            if (minQ < maxQ) {
-                if (q > cutQ) {
-                    break;
-                }
-            }
-            else {
-                if (q < cutQ) {
-                    break;
-                }
-            }
-        }
-        ws.remove(0, cutIndex);
-        qs.remove(0, cutIndex);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/extreme/ExtremeCurveFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,126 +0,0 @@
-package de.intevation.flys.artifacts.model.extreme;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.flys.artifacts.access.ExtremeAccess;
-
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.fixings.FixingsFacet;
-
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-import de.intevation.flys.utils.KMIndex;
-import de.intevation.flys.utils.DoubleUtil;
-
-import org.apache.log4j.Logger;
-
-/**
- * Facet to show the W|Q values.
- */
-public class ExtremeCurveFacet
-extends      FixingsFacet
-implements   FacetTypes {
-
-    /** House logger. */
-    private static Logger logger = Logger.getLogger(ExtremeCurveFacet.class);
-
-
-    /** Trivial Constructor. */
-    public ExtremeCurveFacet() {
-    }
-
-
-    /**
-     * @param description Description of the facet.
-     */
-    public ExtremeCurveFacet(String description) {
-        super(0, EXTREME_WQ_CURVE, description, ComputeType.ADVANCE, null, null);
-    }
-
-    /**
-     * @param description Description of the facet.
-     * @param showBase if true, gimme different name.
-     */
-    public ExtremeCurveFacet(String description, boolean showBase) {
-        super(0, EXTREME_WQ_CURVE_BASE, description, ComputeType.ADVANCE, null, null);
-        if (!showBase) {
-            this.name = EXTREME_WQ_CURVE;
-        }
-    }
-
-
-    public ExtremeCurveFacet(int index, String description) {
-        super(index, EXTREME_WQ_CURVE, description, ComputeType.ADVANCE, null, null);
-    }
-
-
-    /**
-     * Returns the data (a curve/function) this facet provides at km given in context.
-     *
-     * @param artifact the owner artifact.
-     * @param context  the CallContext.
-     *
-     * @return the data.
-     */
-    @Override
-    public Object getData(Artifact artifact, CallContext context) {
-        logger.debug("ExtremeCurveFacet.getData");
-        if (artifact instanceof FLYSArtifact) {
-            FLYSArtifact flys = (FLYSArtifact)artifact;
-            CalculationResult res =
-                (CalculationResult) flys.compute(context,
-                                                 ComputeType.ADVANCE,
-                                                 false);
-
-            ExtremeResult result = (ExtremeResult) res.getData();
-            double currentKm = getCurrentKm(context);
-
-            KMIndex<Curve> curves = result.getCurves();
-
-            KMIndex.Entry<Curve> curveEntry = curves.search(currentKm);
-
-            if (curveEntry != null) {
-                logger.debug("A curve at km = " + currentKm);
-                Curve c = curveEntry.getValue();
-                // Find segment in which the curr. km is located.
-                ExtremeAccess access = new ExtremeAccess(flys, context);
-
-                double[] ds = access.getValuesForRange(currentKm);
-
-                if (ds != null) {
-                    double m = DoubleUtil.maxInArray(ds);
-                    // Add 5 percent.
-                    m *= 1.05d;
-                    c.setSuggestedMaxQ(m);
-                }
-
-                return c;
-            }
-            else {
-                logger.debug("No curve at km = " + currentKm);
-                return null;
-            }
-        }
-        else {
-            logger.debug("Not an instance of FLYSArtifact / WINFOArtifact.");
-            return null;
-        }
-    }
-
-
-    /**
-     * Create a deep copy of this Facet.
-     * @return a deep copy.
-     */
-    @Override
-    public ExtremeCurveFacet deepCopy() {
-        ExtremeCurveFacet copy = new ExtremeCurveFacet();
-        copy.set(this);
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/extreme/ExtremeResult.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,84 +0,0 @@
-package de.intevation.flys.artifacts.model.extreme;
-
-import java.io.Serializable;
-
-import de.intevation.flys.artifacts.model.WQKms;
-import de.intevation.flys.artifacts.model.WQKmsResult;
-
-import de.intevation.flys.utils.KMIndex;
-
-/** Result from an extreme value (extrapolation) calculation. */
-public class ExtremeResult
-implements   Serializable, WQKmsResult
-{
-    /** Curves that refer to actual values and a function for extrapolation. */
-    protected KMIndex<Curve> curves;
-
-    protected WQKms [] wqkms;
-
-    public ExtremeResult() {
-    }
-
-    public ExtremeResult(KMIndex<Curve> curves, WQKms [] wqkms) {
-        this.curves = curves;
-        this.wqkms = wqkms;
-    }
-
-    /**
-     * Gets the curves for this instance.
-     *
-     * @return The curves.
-     */
-    public KMIndex<Curve> getCurves() {
-        return this.curves;
-    }
-
-    /**
-     * Sets the curves for this instance.
-     *
-     * @param curves The curves.
-     */
-    public void setCurves(KMIndex<Curve> curves) {
-        this.curves = curves;
-    }
-
-    /**
-     * Gets the wqkms for this instance.
-     *
-     * @return The wqkms.
-     */
-    @Override
-    public WQKms[] getWQKms() {
-        return this.wqkms;
-    }
-
-    /**
-     * Gets the wqkms for this instance.
-     *
-     * @param index The index to get.
-     * @return The wqkms.
-     */
-    public WQKms getWQKms(int index) {
-        return this.wqkms[index];
-    }
-
-    /**
-     * Sets the wqkms for this instance.
-     *
-     * @param wqkms The wqkms.
-     */
-    public void setWQKms(WQKms[] wqkms) {
-        this.wqkms = wqkms;
-    }
-
-    /**
-     * Sets the wqkms for this instance.
-     *
-     * @param index The index to set.
-     * @param wqkms The wqkms.
-     */
-    public void setWQKms(int index, WQKms wqkms) {
-        this.wqkms[index] = wqkms;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/AnalysisPeriod.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,98 +0,0 @@
-package de.intevation.flys.artifacts.model.fixings;
-
-import de.intevation.flys.artifacts.model.DateRange;
-
-import java.io.Serializable;
-
-public class AnalysisPeriod
-implements   Serializable
-{
-    protected DateRange dateRange;
-    protected QWD []    qwds;
-    protected QWD []    qSectorAverages;
-    protected double [] qSectorStdDevs;
-
-    public AnalysisPeriod() {
-    }
-
-    public AnalysisPeriod(DateRange dateRange) {
-        this.dateRange = dateRange;
-    }
-
-    public AnalysisPeriod(DateRange dateRange, QWD [] qwds) {
-        this(dateRange);
-        this.dateRange = dateRange;
-        this.qwds      = qwds;
-    }
-
-    public AnalysisPeriod(
-        DateRange dateRange,
-        QWD []    qwds,
-        QWD []    qSectorAverages,
-        double [] qSectorStdDevs
-    ) {
-        this(dateRange, qwds);
-        this.qSectorAverages = qSectorAverages;
-        this.qSectorStdDevs  = qSectorStdDevs;
-    }
-
-    public DateRange getDateRange() {
-        return dateRange;
-    }
-
-    public void setDateRange(DateRange dateRange) {
-        this.dateRange = dateRange;
-    }
-
-    public QWD [] getQWDs() {
-        return qwds;
-    }
-
-    public void setQWDs(QWD [] qwds) {
-        this.qwds = qwds;
-    }
-
-    public QWD [] getQSectorAverages() {
-        return qSectorAverages;
-    }
-
-    public void setQSectorAverages(QWD [] qSectorAverages) {
-        this.qSectorAverages = qSectorAverages;
-    }
-
-    public QWD getQSectorAverage(int i) {
-        return qSectorAverages[i];
-    }
-
-    public double [] getQSectorStdDevs() {
-        return qSectorStdDevs;
-    }
-
-    public void setQSectorStdDevs(double [] qSectorStdDevs) {
-        this.qSectorStdDevs = qSectorStdDevs;
-    }
-
-    public double getQSectorStdDev(int i) {
-        return qSectorStdDevs[i];
-    }
-
-    public double getMaxQ() {
-        double maxQ = -Double.MAX_VALUE;
-        if (qwds != null) {
-            for (QWD qwd: qwds) {
-                if (qwd.getQ() > maxQ) {
-                    maxQ = qwd.getQ();
-                }
-            }
-        }
-        if (qSectorAverages != null) {
-            for (QWD qwd: qSectorAverages) {
-                if (qwd != null && qwd.getQ() > maxQ) {
-                    maxQ = qwd.getQ();
-                }
-            }
-        }
-        return maxQ;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/Fitting.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,242 +0,0 @@
-package de.intevation.flys.artifacts.model.fixings;
-
-import gnu.trove.TDoubleArrayList;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.commons.math.MathException;
-import org.apache.commons.math.optimization.fitting.CurveFitter;
-import org.apache.commons.math.optimization.general.LevenbergMarquardtOptimizer;
-import org.apache.commons.math.stat.descriptive.moment.StandardDeviation;
-import org.apache.log4j.Logger;
-
-import de.intevation.flys.artifacts.math.GrubbsOutlier;
-import de.intevation.flys.artifacts.math.fitting.Function;
-
-public class Fitting
-{
-    private static Logger log = Logger.getLogger(Fitting.class);
-
-    /** Use instance of this factory to find meta infos for outliers. */
-    public interface QWDFactory {
-
-        QWD create(double q, double w);
-
-    } // interface QWFactory
-
-    public static final QWDFactory QWD_FACTORY = new QWDFactory() {
-        @Override
-        public QWD create(double q, double w) {
-            return new QWD(q, w);
-        }
-    };
-
-    protected boolean        checkOutliers;
-    protected Function       function;
-    protected QWDFactory     qwdFactory;
-    protected double         chiSqr;
-    protected double []      parameters;
-    protected ArrayList<QWI> removed;
-    protected QWD []         referenced;
-    protected double         standardDeviation;
-
-
-    public Fitting() {
-        removed = new ArrayList<QWI>();
-    }
-
-    public Fitting(Function function) {
-        this(function, QWD_FACTORY);
-    }
-
-    public Fitting(Function function, QWDFactory qwdFactory) {
-        this(function, qwdFactory, false);
-    }
-
-    public Fitting(
-        Function   function,
-        QWDFactory qwdFactory,
-        boolean    checkOutliers
-    ) {
-        this();
-        this.function      = function;
-        this.qwdFactory    = qwdFactory;
-        this.checkOutliers = checkOutliers;
-    }
-
-    public Function getFunction() {
-        return function;
-    }
-
-    public void setFunction(Function function) {
-        this.function = function;
-    }
-
-    public boolean getCheckOutliers() {
-        return checkOutliers;
-    }
-
-    public void setCheckOutliers(boolean checkOutliers) {
-        this.checkOutliers = checkOutliers;
-    }
-
-    public double getChiSquare() {
-        return chiSqr;
-    }
-
-    public void reset() {
-        chiSqr     = 0.0;
-        parameters = null;
-        removed.clear();
-        referenced = null;
-        standardDeviation = 0.0;
-    }
-
-    public boolean hasOutliers() {
-        return !removed.isEmpty();
-    }
-
-    public List<QWI> getOutliers() {
-        return removed;
-    }
-
-    public QWI [] outliersToArray() {
-        return removed.toArray(new QWI[removed.size()]);
-    }
-
-    public QWD [] referencedToArray() {
-        return referenced != null ? (QWD [])referenced.clone() : null;
-    }
-
-    public double getMaxQ() {
-        double maxQ = -Double.MAX_VALUE;
-        if (referenced != null) {
-            for (QWI qw: referenced) {
-                if (qw.getQ() > maxQ) {
-                    maxQ = qw.getQ();
-                }
-            }
-        }
-        return maxQ;
-    }
-
-    public double [] getParameters() {
-        return parameters;
-    }
-
-    public double getStandardDeviation() {
-        return standardDeviation;
-    }
-
-    public boolean fit(double [] qs, double [] ws) {
-
-        TDoubleArrayList xs = new TDoubleArrayList(qs.length);
-        TDoubleArrayList ys = new TDoubleArrayList(ws.length);
-
-        for (int i = 0; i < qs.length; ++i) {
-            if (!Double.isNaN(qs[i]) && !Double.isNaN(ws[i])) {
-                xs.add(qs[i]);
-                ys.add(ws[i]);
-            }
-            else {
-                log.warn("remove invalid value " + qs[i] + " " + ws[i]);
-            }
-        }
-
-        if (xs.size() < 2) {
-            log.warn("Too less points.");
-            return false;
-        }
-
-        List<Double> inputs = new ArrayList<Double>(xs.size());
-
-        de.intevation.flys.artifacts.math.Function instance = null;
-
-        LevenbergMarquardtOptimizer lmo = null;
-
-        for (;;) {
-            parameters = null;
-            for (double tolerance = 1e-10; tolerance < 1e-3; tolerance *= 10d) {
-
-                lmo = new LevenbergMarquardtOptimizer();
-                lmo.setCostRelativeTolerance(tolerance);
-                lmo.setOrthoTolerance(tolerance);
-                lmo.setParRelativeTolerance(tolerance);
-
-                CurveFitter cf = new CurveFitter(lmo);
-
-                for (int i = 0, N = xs.size(); i < N; ++i) {
-                    cf.addObservedPoint(xs.getQuick(i), ys.getQuick(i));
-                }
-
-                try {
-                    parameters = cf.fit(function, function.getInitialGuess());
-                    break;
-                }
-                catch (MathException me) {
-                    if (log.isDebugEnabled()) {
-                        log.debug("tolerance " + tolerance + " + failed.");
-                    }
-                }
-            }
-            if (parameters == null) {
-                return false;
-            }
-
-            // This is the paraterized function for a given km.
-            instance = function.instantiate(parameters);
-
-            if (!checkOutliers) {
-                break;
-            }
-
-            inputs.clear();
-
-            for (int i = 0, N = xs.size(); i < N; ++i) {
-                double y = instance.value(xs.getQuick(i));
-                if (Double.isNaN(y)) {
-                    y = Double.MAX_VALUE;
-                }
-                inputs.add(Double.valueOf(ys.getQuick(i) - y));
-            }
-
-            Integer outlier = GrubbsOutlier.findOutlier(inputs);
-
-            if (outlier == null) {
-                break;
-            }
-
-            int idx = outlier.intValue();
-            removed.add(
-                qwdFactory.create(
-                    xs.getQuick(idx), ys.getQuick(idx)));
-            xs.remove(idx);
-            ys.remove(idx);
-        }
-
-        StandardDeviation stdDev = new StandardDeviation();
-
-        referenced = new QWD[xs.size()];
-        for (int i = 0; i < referenced.length; ++i) {
-            QWD qwd = qwdFactory.create(xs.getQuick(i), ys.getQuick(i));
-
-            if (qwd == null) {
-                log.warn("QW creation failed!");
-            }
-            else {
-                referenced[i] = qwd;
-                double dw = (qwd.getW() - instance.value(qwd.getQ()))*100.0;
-                qwd.setDeltaW(dw);
-                stdDev.increment(dw);
-            }
-        }
-
-        standardDeviation = stdDev.getResult();
-
-        chiSqr = lmo.getChiSquare();
-
-        return true;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixAnalysisCalculation.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,293 +0,0 @@
-package de.intevation.flys.artifacts.model.fixings;
-
-import de.intevation.flys.artifacts.access.FixAnalysisAccess;
-
-import de.intevation.flys.artifacts.math.fitting.Function;
-
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.DateRange;
-
-import de.intevation.flys.artifacts.model.FixingsOverview.AndFilter;
-import de.intevation.flys.artifacts.model.FixingsOverview.DateRangeFilter;
-
-import de.intevation.flys.artifacts.model.FixingsOverview.Fixing.Filter;
-
-import de.intevation.flys.artifacts.model.FixingsOverview.Fixing;
-import de.intevation.flys.artifacts.model.FixingsOverview.IdsFilter;
-import de.intevation.flys.artifacts.model.FixingsOverview.KmFilter;
-import de.intevation.flys.artifacts.model.FixingsOverview.SectorFilter;
-
-import de.intevation.flys.artifacts.model.FixingsOverview;
-import de.intevation.flys.artifacts.model.Parameters;
-import de.intevation.flys.artifacts.model.Range;
-
-import de.intevation.flys.utils.DateAverager;
-import de.intevation.flys.utils.KMIndex;
-
-import gnu.trove.TIntIntHashMap;
-
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-
-import org.apache.commons.math.stat.descriptive.moment.StandardDeviation;
-
-import org.apache.log4j.Logger;
-
-public class FixAnalysisCalculation
-extends      FixCalculation
-{
-    private static Logger log = Logger.getLogger(FixAnalysisCalculation.class);
-
-    protected DateRange    referencePeriod;
-    protected DateRange [] analysisPeriods;
-
-    public FixAnalysisCalculation() {
-    }
-
-    public FixAnalysisCalculation(FixAnalysisAccess access) {
-        super(access);
-
-        DateRange    referencePeriod = access.getReferencePeriod();
-        DateRange [] analysisPeriods = access.getAnalysisPeriods();
-
-        if (referencePeriod == null) {
-            addProblem("fix.missing.reference.period");
-        }
-
-        if (analysisPeriods == null || analysisPeriods.length < 1) {
-            addProblem("fix.missing.analysis.periods");
-        }
-
-        if (!hasProblems()) {
-            this.referencePeriod = referencePeriod;
-            this.analysisPeriods = analysisPeriods;
-        }
-    }
-
-    @Override
-    public CalculationResult innerCalculate(
-        FixingsOverview overview,
-        Function        func
-    ) {
-        ColumnCache cc = new ColumnCache();
-
-        FitResult fitResult = doFitting(overview, cc, func);
-
-        if (fitResult == null) {
-            return new CalculationResult(this);
-        }
-
-        KMIndex<AnalysisPeriod []> analysisPeriods =
-            calculateAnalysisPeriods(
-                func,
-                fitResult.getParameters(),
-                overview,
-                cc);
-
-        analysisPeriods.sort();
-
-        FixAnalysisResult far = new FixAnalysisResult(
-            fitResult.getParameters(),
-            fitResult.getReferenced(),
-            fitResult.getOutliers(),
-            analysisPeriods);
-
-        return new CalculationResult(far, this);
-    }
-
-    @Override
-    protected Filter createFilter() {
-        Filter ids = super.createFilter();
-        DateRangeFilter rdf = new DateRangeFilter(
-            referencePeriod.getFrom(),
-            referencePeriod.getTo());
-        return new AndFilter().add(rdf).add(ids);
-    }
-
-    protected KMIndex<AnalysisPeriod []> calculateAnalysisPeriods(
-        Function        function,
-        Parameters      parameters,
-        FixingsOverview overview,
-        ColumnCache cc
-    ) {
-        Range range = new Range(from, to);
-
-        int kmIndex   = parameters.columnIndex("km");
-        int maxQIndex = parameters.columnIndex("max_q");
-
-        double [] wq = new double[2];
-
-        int [] parameterIndices =
-            parameters.columnIndices(function.getParameterNames());
-
-        double [] parameterValues = new double[parameterIndices.length];
-
-        DateAverager dateAverager = new DateAverager();
-
-        KMIndex<AnalysisPeriod []> results =
-            new KMIndex<AnalysisPeriod []>(parameters.size());
-
-        IdsFilter idsFilter = new IdsFilter(events);
-
-        TIntIntHashMap [] col2indices =
-            new TIntIntHashMap[analysisPeriods.length];
-
-        for (int i = 0; i < analysisPeriods.length; ++i) {
-            col2indices[i] = new TIntIntHashMap();
-        }
-
-        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.
-            de.intevation.flys.artifacts.math.Function instance =
-                function.instantiate(parameterValues);
-
-            KmFilter kmFilter = new KmFilter(km);
-
-            ArrayList<AnalysisPeriod> periodResults =
-                new ArrayList<AnalysisPeriod>(analysisPeriods.length);
-
-            for (int ap = 0; ap < analysisPeriods.length; ++ap) {
-                DateRange analysisPeriod = analysisPeriods[ap];
-                TIntIntHashMap col2index = col2indices[ap];
-
-                DateRangeFilter drf = new DateRangeFilter(
-                    analysisPeriod.getFrom(),
-                    analysisPeriod.getTo());
-
-                QWD []    qSectorAverages = new QWD[4];
-                double [] qSectorStdDevs  = new double[4];
-
-                ArrayList<QWD> allQWDs = new ArrayList<QWD>();
-
-                // for all Q sectors.
-                for (int qSector = qSectorStart; qSector < qSectorEnd; ++qSector) {
-
-                    Filter filter = new AndFilter()
-                        .add(kmFilter)
-                        .add(new SectorFilter(qSector))
-                        .add(drf)
-                        .add(idsFilter);
-
-                    List<Fixing.Column> metas = overview.filter(range, filter);
-
-                    if (metas.isEmpty()) {
-                        // No fixings for km and analysis period
-                        continue;
-                    }
-
-                    double sumQ = 0.0;
-                    double sumW = 0.0;
-
-                    StandardDeviation stdDev = new StandardDeviation();
-
-                    List<QWD> qwds = new ArrayList<QWD>(metas.size());
-
-                    dateAverager.clear();
-
-                    for (Fixing.Column meta: metas) {
-                        if (meta.findQSector(km) != qSector) {
-                            // Ignore not matching sectors.
-                            continue;
-                        }
-
-                        Column column = cc.getColumn(meta);
-                        if (column == null || !column.getQW(km, wq)) {
-                            continue;
-                        }
-
-                        double fw = instance.value(wq[1]);
-                        if (Double.isNaN(fw)) {
-                            continue;
-                        }
-
-                        double dw = (wq[0] - fw)*100.0;
-
-                        stdDev.increment(dw);
-
-                        Date date = column.getDate();
-                        String description = column.getDescription();
-
-                        QWD qwd = new QWD(
-                            wq[1], wq[0],
-                            description,
-                            date, true,
-                            dw, getIndex(col2index, column.getIndex()));
-
-                        qwds.add(qwd);
-
-                        sumW += wq[0];
-                        sumQ += wq[1];
-
-                        dateAverager.add(date);
-                    }
-
-                    // Calulate average per Q sector.
-                    int N = qwds.size();
-                    if (N > 0) {
-                        allQWDs.addAll(qwds);
-                        double avgW = sumW / N;
-                        double avgQ = sumQ / N;
-
-                        double avgFw = instance.value(avgQ);
-                        if (!Double.isNaN(avgFw)) {
-                            double avgDw = (avgW - avgFw)*100.0;
-                            Date avgDate = dateAverager.getAverage();
-
-                            String avgDescription = "avg.deltawt." + qSector;
-
-                            QWD avgQWD = new QWD(
-                                avgQ, avgW, avgDescription, avgDate, true, avgDw, 0);
-
-                            qSectorAverages[qSector] = avgQWD;
-                        }
-                        qSectorStdDevs[qSector] = stdDev.getResult();
-                    }
-                    else {
-                        qSectorStdDevs[qSector] = Double.NaN;
-                    }
-                } // for all Q sectors
-
-                QWD [] aqwds = allQWDs.toArray(new QWD[allQWDs.size()]);
-
-                AnalysisPeriod periodResult = new AnalysisPeriod(
-                    analysisPeriod,
-                    aqwds,
-                    qSectorAverages,
-                    qSectorStdDevs);
-                periodResults.add(periodResult);
-            }
-
-            double maxQ = -Double.MAX_VALUE;
-            for (AnalysisPeriod ap: periodResults) {
-                double q = ap.getMaxQ();
-                if (q > maxQ) {
-                    maxQ = q;
-                }
-            }
-
-            double oldMaxQ = parameters.get(row, maxQIndex);
-            if (oldMaxQ < maxQ) {
-                parameters.set(row, maxQIndex, maxQ);
-            }
-
-            results.add(km, periodResults.toArray(
-                new AnalysisPeriod[periodResults.size()]));
-        }
-
-        return results;
-    }
-
-    private static final int getIndex(TIntIntHashMap map, int colIdx) {
-        if (map.containsKey(colIdx)) {
-            return map.get(colIdx);
-        }
-        int index = map.size();
-        map.put(colIdx, index);
-        return index;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixAnalysisEventsFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,115 +0,0 @@
-package de.intevation.flys.artifacts.model.fixings;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.FacetTypes;
-
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-import de.intevation.flys.utils.KMIndex;
-
-import org.apache.log4j.Logger;
-
-/**
- * Facet to show W values for Q values at km for a date.
- *
- * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
- */
-public class FixAnalysisEventsFacet
-extends      FixingsFacet
-implements   FacetTypes {
-
-    /** House logger. */
-    private static Logger logger = Logger.getLogger(FixAnalysisEventsFacet.class);
-
-    /** Trivial Constructor. */
-    public FixAnalysisEventsFacet() {
-    }
-
-
-    /**
-     * @param name
-     */
-    public FixAnalysisEventsFacet(int index, String name, String description) {
-        super(index,
-             name,
-             description,
-             ComputeType.ADVANCE,
-             null,
-             null);
-    }
-
-
-    /**
-     * Returns the data this facet requires.
-     *
-     * @param artifact the owner artifact.
-     * @param context  the CallContext (ignored).
-     *
-     * @return the data.
-     */
-    @Override
-    public Object getData(Artifact artifact, CallContext context) {
-        logger.debug("FixAnalysisEventsFacet.getData");
-
-        if (artifact instanceof FLYSArtifact) {
-            FLYSArtifact flys = (FLYSArtifact)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 {
-            logger.debug("Not an instance of FixationArtifact.");
-            return null;
-        }
-    }
-
-
-    /**
-     * Create a deep copy of this Facet.
-     * @return a deep copy.
-     */
-    @Override
-    public FixAnalysisEventsFacet deepCopy() {
-        FixAnalysisEventsFacet copy = new FixAnalysisEventsFacet();
-        copy.set(this);
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixAnalysisPeriodsFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,111 +0,0 @@
-package de.intevation.flys.artifacts.model.fixings;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.flys.artifacts.access.FixAnalysisAccess;
-
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.DateRange;
-import de.intevation.flys.artifacts.model.FacetTypes;
-
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-import de.intevation.flys.utils.KMIndex;
-
-import org.apache.log4j.Logger;
-
-/**
- * Facet to show W values for Q values at km for a date.
- *
- * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
- */
-public class FixAnalysisPeriodsFacet
-extends      FixingsFacet
-implements   FacetTypes {
-
-    /** House logger. */
-    private static Logger logger = Logger.getLogger(FixAnalysisPeriodsFacet.class);
-
-    /** Trivial Constructor. */
-    public FixAnalysisPeriodsFacet() {
-    }
-
-
-    /**
-     * @param name
-     */
-    public FixAnalysisPeriodsFacet(int index, String name, String description) {
-        super(index,
-            name,
-            description,
-            ComputeType.ADVANCE,
-            null,
-            null);
-    }
-
-
-    /**
-     * Returns the data this facet requires.
-     *
-     * @param artifact the owner artifact.
-     * @param context  the CallContext (ignored).
-     *
-     * @return the data.
-     */
-    @Override
-    public Object getData(Artifact artifact, CallContext context) {
-        logger.debug("FixAnalysisPeriodsFacet.getData");
-
-        if (artifact instanceof FLYSArtifact) {
-            FLYSArtifact flys = (FLYSArtifact)artifact;
-            FixAnalysisAccess access = new FixAnalysisAccess(flys, context);
-
-            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) {
-                return null;
-            }
-
-            AnalysisPeriod[] periods = kmPeriodsEntry.getValue();
-
-            if (periods == null) {
-                return null;
-            }
-            DateRange[] dates = new DateRange[periods.length];
-            for (int i = 0; i < periods.length; i++) {
-                dates[i] = periods[i].getDateRange();
-            }
-            return dates;
-        }
-        else {
-            logger.debug("Not an instance of FixationArtifact.");
-            return null;
-        }
-    }
-
-
-    /**
-     * Create a deep copy of this Facet.
-     * @return a deep copy.
-     */
-    @Override
-    public FixAnalysisPeriodsFacet deepCopy() {
-        FixAnalysisPeriodsFacet copy = new FixAnalysisPeriodsFacet();
-        copy.set(this);
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixAnalysisResult.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,77 +0,0 @@
-package de.intevation.flys.artifacts.model.fixings;
-
-import java.util.Collection;
-import java.util.Date;
-import java.util.TreeSet;
-
-import de.intevation.flys.artifacts.model.Parameters;
-
-import de.intevation.flys.utils.KMIndex;
-
-public class FixAnalysisResult
-extends      FixResult
-{
-    protected KMIndex<AnalysisPeriod []> analysisPeriods;
-
-    public FixAnalysisResult() {
-    }
-
-    public FixAnalysisResult(
-        Parameters                 parameters,
-        KMIndex<QWD []>            referenced,
-        KMIndex<QWI []>            outliers,
-        KMIndex<AnalysisPeriod []> analysisPeriods
-    ) {
-        super(parameters, referenced, outliers);
-        this.analysisPeriods = analysisPeriods;
-    }
-
-    public int getUsedSectorsInAnalysisPeriods() {
-        int result = 0;
-        for (KMIndex.Entry<AnalysisPeriod []> entry: analysisPeriods) {
-            for (AnalysisPeriod period: entry.getValue()) {
-                for (int i = 0; i < 4; ++i) {
-                    result |= period.getQSectorAverage(i) != null
-                        ? (1 << i)
-                        : 0;
-                }
-                // XXX: Stop early on result == ~(~0 << 4)) ?
-            }
-        }
-        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);
-            }
-        }
-        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);
-            }
-        }
-        return dates;
-    }
-
-    public KMIndex<AnalysisPeriod []> getAnalysisPeriods() {
-        return analysisPeriods;
-    }
-
-    public void setAnalysisPeriods(KMIndex<AnalysisPeriod []> analysisPeriods) {
-        this.analysisPeriods = analysisPeriods;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixAvSectorFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,126 +0,0 @@
-package de.intevation.flys.artifacts.model.fixings;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.flys.artifacts.access.FixAnalysisAccess;
-
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.FacetTypes;
-
-import de.intevation.flys.artifacts.model.QWDDateRange;
-
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-import de.intevation.flys.utils.KMIndex;
-
-import org.apache.log4j.Logger;
-
-
-/**
- * Facet to show average W values for Q sectors.
- *
- * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
- */
-public class FixAvSectorFacet
-extends      FixingsFacet
-implements   FacetTypes {
-
-    /** House logger. */
-    private static Logger logger = Logger.getLogger(FixAvSectorFacet.class);
-
-    /** Trivial Constructor. */
-    public FixAvSectorFacet() {
-    }
-
-
-    public FixAvSectorFacet(int ndx, String name, String description) {
-        super(
-            ndx,
-            name,
-            description,
-            ComputeType.ADVANCE,
-            null,
-            null);
-    }
-
-
-    /**
-     * Returns the data this facet requires.
-     *
-     * @param artifact the owner artifact.
-     * @param context  the CallContext.
-     *
-     * @return the data as QWD array (QWD[]).
-     */
-    @Override
-    public Object getData(Artifact artifact, CallContext context) {
-        logger.debug("FixAvSectorFacet.getData");
-
-        if (artifact instanceof FLYSArtifact) {
-            FLYSArtifact flys = (FLYSArtifact)artifact;
-            FixAnalysisAccess access = new FixAnalysisAccess(flys, context);
-
-            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.warn("No analysis periods found for km '" + currentKm + "'");
-                return null;
-            }
-
-            AnalysisPeriod[] periods = kmPeriodsEntry.getValue();
-
-            if (periods == null) {
-                logger.warn("No analysis periods specified!");
-                return null;
-            }
-
-            QWD[] qwdData = null;
-            int sectorNdx = index & 3;
-            int periodNdx = index >> 2;
-
-            if (periodNdx < periods.length) {
-                qwdData = periods[periodNdx].getQSectorAverages();
-            }
-
-            if (logger.isDebugEnabled()) {
-                int resSize = qwdData != null ? qwdData.length : -1;
-                logger.debug("Found " + resSize + " result elements.");
-            }
-
-            if (qwdData == null) {
-                return null;
-            }
-            return new QWDDateRange(qwdData[sectorNdx], periods[periodNdx].getDateRange());
-        }
-        else {
-            logger.warn("Artifact is no instance of FLYSArtifact.");
-            return null;
-        }
-    }
-
-
-    /**
-     * Create a deep copy of this Facet.
-     * @return a deep copy.
-     */
-    @Override
-    public FixAvSectorFacet deepCopy() {
-        FixAvSectorFacet copy = new FixAvSectorFacet();
-        copy.set(this);
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixCalculation.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,425 +0,0 @@
-package de.intevation.flys.artifacts.model.fixings;
-
-import de.intevation.artifacts.common.utils.StringUtils;
-
-import de.intevation.flys.artifacts.access.FixAccess;
-
-import de.intevation.flys.artifacts.math.fitting.Function;
-import de.intevation.flys.artifacts.math.fitting.FunctionFactory;
-
-import de.intevation.flys.artifacts.model.Calculation;
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.FixingsColumn;
-import de.intevation.flys.artifacts.model.FixingsColumnFactory;
-
-import de.intevation.flys.artifacts.model.FixingsOverview.Fixing.Filter;
-
-import de.intevation.flys.artifacts.model.FixingsOverview.Fixing;
-import de.intevation.flys.artifacts.model.FixingsOverview.IdsFilter;
-
-import de.intevation.flys.artifacts.model.FixingsOverview;
-import de.intevation.flys.artifacts.model.FixingsOverviewFactory;
-import de.intevation.flys.artifacts.model.Parameters;
-
-import de.intevation.flys.utils.DoubleUtil;
-import de.intevation.flys.utils.KMIndex;
-
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.log4j.Logger;
-
-/** Calculation base class for fix. */
-public abstract class FixCalculation
-extends               Calculation
-{
-    private static Logger log = Logger.getLogger(FixCalculation.class);
-
-    public static final double EPSILON = 1e-4;
-
-    public static final String [] STANDARD_COLUMNS = {
-        "km", "chi_sqr", "max_q", "std-dev"
-    };
-
-    protected static class FitResult {
-
-        protected Parameters      parameters;
-        protected KMIndex<QWD []> referenced;
-        protected KMIndex<QWI []> outliers;
-
-        public FitResult() {
-        }
-
-        public FitResult(
-            Parameters      parameters,
-            KMIndex<QWD []> referenced,
-            KMIndex<QWI []> outliers
-        ) {
-            this.parameters = parameters;
-            this.referenced = referenced;
-            this.outliers   = outliers;
-        }
-
-        public Parameters getParameters() {
-            return parameters;
-        }
-
-        public KMIndex<QWD []> getReferenced() {
-            return referenced;
-        }
-
-        public KMIndex<QWI []> getOutliers() {
-            return outliers;
-        }
-    } // class FitResult
-
-    /** Helper class to bundle the meta information of a column
-     *  and the real data.
-     */
-    protected static class Column {
-
-        protected Fixing.Column meta;
-        protected FixingsColumn data;
-        protected int           index;
-
-        public Column() {
-        }
-
-        public Column(Fixing.Column meta, FixingsColumn data, int index) {
-            this.meta  = meta;
-            this.data  = data;
-            this.index = index;
-        }
-
-        public Date getDate() {
-            return meta.getStartTime();
-        }
-
-        public String getDescription() {
-            return meta.getDescription();
-        }
-
-        public int getIndex() {
-            return index;
-        }
-
-        public boolean getQW(
-            double    km,
-            double [] qs,
-            double [] ws,
-            int       index
-        ) {
-            qs[index] = data.getQ(km);
-            return data.getW(km, ws, index);
-        }
-
-        public boolean getQW(double km, double [] wq) {
-            data.getW(km, wq, 0);
-            if (Double.isNaN(wq[0])) return false;
-            wq[1] = data.getQ(km);
-            return !Double.isNaN(wq[1]);
-        }
-    } // class Column
-
-    /**
-     * Helper class to find the data belonging to meta info more quickly.
-     */
-    protected static class ColumnCache {
-
-        protected Map<Integer, Column> columns;
-
-        public ColumnCache() {
-            columns = new HashMap<Integer, Column>();
-        }
-
-        public Column getColumn(Fixing.Column meta) {
-            Integer key = meta.getId();
-            Column column = columns.get(key);
-            if (column == null) {
-                FixingsColumn data = FixingsColumnFactory
-                    .getInstance()
-                    .getColumnData(meta);
-                if (data != null) {
-                    column = new Column(meta, data, columns.size());
-                    columns.put(key, column);
-                }
-            }
-            return column;
-        }
-    } // class ColumnCache
-
-
-    protected String  river;
-    protected double  from;
-    protected double  to;
-    protected double  step;
-    protected boolean preprocessing;
-    protected String  function;
-    protected int []  events;
-    protected int     qSectorStart;
-    protected int     qSectorEnd;
-
-    public FixCalculation() {
-    }
-
-    public FixCalculation(FixAccess access) {
-        String  river         = access.getRiver();
-        Double  from          = access.getFrom();
-        Double  to            = access.getTo();
-        Double  step          = access.getStep();
-        String  function      = access.getFunction();
-        int []  events        = access.getEvents();
-        Integer qSectorStart  = access.getQSectorStart();
-        Integer qSectorEnd    = access.getQSectorEnd();
-        Boolean preprocessing = access.getPreprocessing();
-
-        if (river == null) {
-            addProblem("fix.missing.river");
-        }
-
-        if (from == null) {
-            addProblem("fix.missing.from");
-        }
-
-        if (to == null) {
-            addProblem("fix.missing.to");
-        }
-
-        if (step == null) {
-            addProblem("fix.missing.step");
-        }
-
-        if (function == null) {
-            addProblem("fix.missing.function");
-        }
-
-        if (events == null || events.length < 1) {
-            addProblem("fix.missing.events");
-        }
-
-        if (qSectorStart == null) {
-            addProblem("fix.missing.qstart.sector");
-        }
-
-        if (qSectorEnd == null) {
-            addProblem("fix.missing.qend.sector");
-        }
-
-        if (preprocessing == null) {
-            addProblem("fix.missing.preprocessing");
-        }
-
-        if (!hasProblems()) {
-            this.river         = river;
-            this.from          = from;
-            this.to            = to;
-            this.step          = step;
-            this.function      = function;
-            this.events        = events;
-            this.qSectorStart  = qSectorStart;
-            this.qSectorEnd    = qSectorEnd;
-            this.preprocessing = preprocessing;
-        }
-    }
-
-    protected static String toString(
-        String [] parameterNames,
-        double [] values
-    ) {
-        StringBuilder sb = new StringBuilder();
-        for (int i = 0; i < parameterNames.length; ++i) {
-            if (i > 0) sb.append(", ");
-            sb.append(parameterNames[i]).append(": ").append(values[i]);
-        }
-        return sb.toString();
-    }
-
-
-    /** Create filter to accept only the chosen events. 
-     *  This factored out out to be overwritten. 
-     */
-    protected Filter createFilter() {
-        return new IdsFilter(events);
-    }
-
-    protected List<Column> getEventColumns(
-        FixingsOverview overview,
-        ColumnCache     cc
-    ) {
-        FixingsColumnFactory fcf = FixingsColumnFactory.getInstance();
-
-        Filter filter = createFilter();
-
-        List<Fixing.Column> metas = overview.filter(null, filter);
-
-        List<Column> columns = new ArrayList<Column>(metas.size());
-
-        for (Fixing.Column meta: metas) {
-
-            Column data = cc.getColumn(meta);
-            if (data == null) {
-                addProblem("fix.cannot.load.data");
-            }
-            else {
-                columns.add(data);
-            }
-        }
-
-        return columns;
-    }
-
-    // Fit a function to the given points from fixation.
-    protected FitResult doFitting(
-        FixingsOverview overview,
-        ColumnCache     cc,
-        Function        func
-    ) {
-        boolean debug = log.isDebugEnabled();
-
-        final List<Column> eventColumns = getEventColumns(overview, cc);
-
-        if (eventColumns.size() < 2) {
-            addProblem("fix.too.less.data.columns");
-            return null;
-        }
-
-        final double  [] qs = new double[eventColumns.size()];
-        final double  [] ws = new double[qs.length];
-        final boolean [] interpolated = new boolean[ws.length];
-
-        Fitting.QWDFactory qwdFactory = new Fitting.QWDFactory() {
-            @Override
-            public QWD create(double q, double w) {
-                // Check all the event columns for close match
-                // and take the description and the date from meta.
-                for (int i = 0; i < qs.length; ++i) {
-                    if (Math.abs(qs[i]-q) < EPSILON
-                    &&  Math.abs(ws[i]-w) < EPSILON) {
-                        Column column = eventColumns.get(i);
-                        return new QWD(
-                            qs[i], ws[i],
-                            column.getDescription(),
-                            column.getDate(),
-                            interpolated[i],
-                            0d,
-                            column.getIndex());
-                    }
-                }
-                log.warn("cannot find column for (" + q + ", " + w + ")");
-                return new QWD(q, w);
-            }
-        };
-
-        Fitting fitting = new Fitting(func, qwdFactory, preprocessing);
-
-        String [] parameterNames = func.getParameterNames();
-
-        Parameters results =
-            new Parameters(
-                StringUtils.join(STANDARD_COLUMNS, parameterNames));
-
-        boolean invalid = false;
-
-        double [] kms = DoubleUtil.explode(from, to, step / 1000.0);
-
-        if (debug) {
-            log.debug("number of kms: " + kms.length);
-        }
-
-        KMIndex<QWI []> outliers   = new KMIndex<QWI []>();
-        KMIndex<QWD []> referenced = new KMIndex<QWD []>(kms.length);
-
-        int kmIndex             = results.columnIndex("km");
-        int chiSqrIndex         = results.columnIndex("chi_sqr");
-        int maxQIndex           = results.columnIndex("max_q");
-        int stdDevIndex         = results.columnIndex("std-dev");
-        int [] parameterIndices = results.columnIndices(parameterNames);
-
-        int numFailed = 0;
-
-        for (int i = 0; i < kms.length; ++i) {
-            double km = kms[i];
-
-            // Fill Qs and Ws from event columns.
-            for (int j = 0; j < ws.length; ++j) {
-                interpolated[j] = !eventColumns.get(j).getQW(km, qs, ws, j);
-            }
-
-            fitting.reset();
-
-            if (!fitting.fit(qs, ws)) {
-                ++numFailed;
-                addProblem(km, "fix.fitting.failed");
-                continue;
-            }
-
-            referenced.add(km, fitting.referencedToArray());
-
-            if (fitting.hasOutliers()) {
-                outliers.add(km, fitting.outliersToArray());
-            }
-
-            int row = results.newRow();
-            double [] values = fitting.getParameters();
-
-            results.set(row, kmIndex, km);
-            results.set(row, chiSqrIndex, fitting.getChiSquare());
-            results.set(row, stdDevIndex, fitting.getStandardDeviation());
-            results.set(row, maxQIndex,   fitting.getMaxQ());
-            invalid |= results.set(row, parameterIndices, values);
-
-            if (debug) {
-                log.debug("km: "+km+" " + toString(parameterNames, values));
-            }
-        }
-
-        if (debug) {
-            log.debug("success: " + (kms.length - numFailed));
-            log.debug("failed: " + numFailed);
-        }
-
-        if (invalid) {
-            addProblem("fix.invalid.values");
-            results.removeNaNs();
-        }
-
-        outliers.sort();
-        referenced.sort();
-
-        return new FitResult(
-            results,
-            referenced,
-            outliers);
-    }
-
-    public CalculationResult calculate() {
-        FixingsOverview overview =
-            FixingsOverviewFactory.getOverview(river);
-
-        if (overview == null) {
-            addProblem("fix.no.overview.available");
-        }
-
-        Function func = FunctionFactory.getInstance()
-            .getFunction(function);
-
-        if (func == null) {
-            addProblem("fix.invalid.function.name");
-        }
-
-        if (hasProblems()) {
-            return new CalculationResult(this);
-        }
-
-        return innerCalculate(overview, func);
-    }
-
-    protected abstract CalculationResult innerCalculate(
-        FixingsOverview overview,
-        Function        function
-    );
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixDerivateFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,119 +0,0 @@
-package de.intevation.flys.artifacts.model.fixings;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.flys.artifacts.access.FixAnalysisAccess;
-
-import de.intevation.flys.artifacts.math.fitting.Function;
-import de.intevation.flys.artifacts.math.fitting.FunctionFactory;
-
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.Parameters;
-
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-import org.apache.log4j.Logger;
-
-
-/**
- * Facet to show the W|Q values.
- *
- * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
- */
-public class FixDerivateFacet
-extends      FixingsFacet
-implements   FacetTypes {
-
-    /** House logger. */
-    private static Logger logger = Logger.getLogger(FixDerivateFacet.class);
-
-
-    /** Trivial Constructor. */
-    public FixDerivateFacet() {
-    }
-
-
-    /**
-     * @param name
-     */
-    public FixDerivateFacet(String name, String description) {
-        super(0, name, description, ComputeType.ADVANCE, null, null);
-    }
-
-    public FixDerivateFacet(int index, String name, String description) {
-        super(index, name, description, ComputeType.ADVANCE, null, null);
-    }
-
-
-    /**
-     * Returns the data this facet requires.
-     *
-     * @param artifact the owner artifact.
-     * @param context  the CallContext (ignored).
-     *
-     * @return the data.
-     */
-    @Override
-    public Object getData(Artifact artifact, CallContext context) {
-
-        logger.debug("FixDerivateFacet.getData");
-        if (!(artifact instanceof FLYSArtifact)) {
-            logger.debug("Not an instance of FixationArtifact.");
-            return null;
-        }
-
-        FLYSArtifact flys = (FLYSArtifact)artifact;
-        FixAnalysisAccess access = new FixAnalysisAccess(flys, context);
-
-        CalculationResult res =
-            (CalculationResult) flys.compute(context,
-                                             ComputeType.ADVANCE,
-                                             false);
-
-        FixAnalysisResult result = (FixAnalysisResult) res.getData();
-        double currentKm = getCurrentKm(context);
-
-        String function = access.getFunction();
-        Function ff = FunctionFactory.getInstance().getFunction(function);
-        Function.Derivative fd = ff.getDerivative();
-
-        Parameters params = result.getParameters();
-        double maxQ = FixFacetUtils.getMaxQ(params, currentKm);
-
-        String[] paramNames = ff.getParameterNames();
-
-        double[] coeffs = params.interpolate("km", currentKm, paramNames);
-        if (coeffs == null) {
-            logger.warn("getData: coeffs == null");
-            return null;
-        }
-
-        de.intevation.flys.artifacts.math.Function mf =
-            fd.instantiate(coeffs);
-
-        FixFunction fix = new FixFunction(
-            "",
-            fd.getDescription(),
-            mf,
-            maxQ);
-
-        return fix;
-    }
-
-
-    /**
-     * Create a deep copy of this Facet.
-     * @return a deep copy.
-     */
-    @Override
-    public FixDerivateFacet deepCopy() {
-        FixDerivateFacet copy = new FixDerivateFacet();
-        copy.set(this);
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixDeviationFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,102 +0,0 @@
-package de.intevation.flys.artifacts.model.fixings;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.access.FixAnalysisAccess;
-
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.Parameters;
-
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-import org.apache.log4j.Logger;
-
-
-/**
- * Facet to show the W|Q values.
- *
- * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
- */
-public class FixDeviationFacet
-extends      FixingsFacet
-implements   FacetTypes {
-
-    /** House logger. */
-    private static Logger logger = Logger.getLogger(FixDeviationFacet.class);
-
-    public static final String [] STD_DEV_COLUMN = { "std-dev" };
-
-    /** Trivial Constructor. */
-    public FixDeviationFacet() {
-    }
-
-
-    /**
-     * @param name
-     */
-    public FixDeviationFacet(String name, String description) {
-        super(0, name, description, ComputeType.ADVANCE, null, null);
-    }
-
-    public FixDeviationFacet(int index, String name, String description) {
-        super(index, name, description, ComputeType.ADVANCE, null, null);
-    }
-
-
-    /**
-     * Returns the data this facet requires.
-     *
-     * @param artifact the owner artifact.
-     * @param context  the CallContext (ignored).
-     *
-     * @return the data.
-     */
-    @Override
-    public Object getData(Artifact artifact, CallContext context) {
-        logger.debug("FixDeviationFacet.getData");
-        if (artifact instanceof FLYSArtifact) {
-            FLYSArtifact flys = (FLYSArtifact)artifact;
-            FixAnalysisAccess access = new FixAnalysisAccess(flys, context);
-
-            CalculationResult res =
-                (CalculationResult) flys.compute(context,
-                                                 ComputeType.ADVANCE,
-                                                 false);
-
-            FixAnalysisResult result = (FixAnalysisResult) res.getData();
-            double currentKm = getCurrentKm(context);
-
-            Parameters params = result.getParameters();
-
-            double[] stdDev =
-                params.interpolate("km", currentKm, STD_DEV_COLUMN);
-
-            if (stdDev == null) {
-                logger.warn("getData: stdDev == null at km " + currentKm);
-                return null;
-            }
-
-            return stdDev;
-        }
-        else {
-            logger.debug("Not an instance of FixationArtifact.");
-            return null;
-        }
-    }
-
-
-    /**
-     * Create a deep copy of this Facet.
-     * @return a deep copy.
-     */
-    @Override
-    public FixDerivateFacet deepCopy() {
-        FixDerivateFacet copy = new FixDerivateFacet();
-        copy.set(this);
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixEventFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,62 +0,0 @@
-package de.intevation.flys.artifacts.model.fixings;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.model.DataFacet;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.WQKmsFactory;
-import de.intevation.flys.artifacts.access.FixRealizingAccess;
-
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-
-/**
- * Facet to show W|Q|km Values.
- */
-public class FixEventFacet
-extends      DataFacet
-implements   FacetTypes {
-
-    /** House logger. */
-    private static Logger logger = Logger.getLogger(FixEventFacet.class);
-
-
-    public FixEventFacet(int index, String name, String description) {
-        super(index, name, description, ComputeType.FEED, null, null);
-    }
-
-
-    /**
-     * Returns the data this facet requires.
-     *
-     * @param artifact the owner artifact.
-     * @param context  the CallContext (ignored).
-     *
-     * @return the data.
-     */
-    @Override
-    public Object getData(Artifact artifact, CallContext context) {
-        logger.debug("FixEventFacet.getData");
-
-        FixRealizingAccess access = new FixRealizingAccess((FLYSArtifact) artifact, context);
-        int wstColID = access.getEvents()[index];
-        return WQKmsFactory.getWQKmsCID(wstColID);
-    }
-
-
-    /**
-     * Create a deep copy of this Facet.
-     * @return a deep copy.
-     */
-    @Override
-    public FixEventFacet deepCopy() {
-        FixEventFacet copy = new FixEventFacet(index, name, description);
-        copy.set(this);
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixFacetUtils.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,18 +0,0 @@
-package de.intevation.flys.artifacts.model.fixings;
-
-import de.intevation.flys.artifacts.model.Parameters;
-
-public final class FixFacetUtils {
-
-    public static final String [] MAX_Q_COLUMN = { "max_q" };
-
-    public static double getMaxQ(Parameters params, double km) {
-        double [] maxQ = params.interpolate("km", km, MAX_Q_COLUMN);
-        if (maxQ == null) {
-            return 1000d;
-        }
-        double mQ = Math.min(10000d, Math.abs(maxQ[0]));
-        return mQ + 0.05*mQ;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixFunction.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-package de.intevation.flys.artifacts.model.fixings;
-
-import de.intevation.flys.artifacts.math.Function;
-
-public class FixFunction
-{
-    protected String   name;
-    protected String   description;
-    protected Function function;
-    protected double   maxQ;
-
-    public FixFunction (
-        String   name,
-        String   description,
-        Function function,
-        double   maxQ
-    ) {
-        this.name        = name;
-        this.description = description;
-        this.function    = function;
-        this.maxQ        = maxQ;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public String getDescription() {
-        return description;
-    }
-
-    public Function getFunction() {
-        return function;
-    }
-
-    public double getMaxQ() {
-        return maxQ;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixLongitudinalAnalysisFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,108 +0,0 @@
-package de.intevation.flys.artifacts.model.fixings;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.DataFacet;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-import de.intevation.flys.utils.KMIndex;
-
-/**
- * Facet to show average W values for Q sectors.
- *
- * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
- */
-public class FixLongitudinalAnalysisFacet
-extends      DataFacet
-implements   FacetTypes {
-
-    /** House logger. */
-    private static Logger logger = Logger.getLogger(FixLongitudinalAnalysisFacet.class);
-
-    /** Trivial Constructor. */
-    public FixLongitudinalAnalysisFacet() {
-    }
-
-
-    public FixLongitudinalAnalysisFacet(
-        int ndx,
-        String name,
-        String description)
-    {
-        super(
-            ndx,
-            name,
-            description,
-            ComputeType.ADVANCE,
-            null,
-            null);
-    }
-
-
-    /**
-     * Returns the data this facet requires.
-     *
-     * @param artifact the owner artifact.
-     * @param context  the CallContext.
-     *
-     * @return the data as KMIndex.
-     */
-    @Override
-    public Object getData(Artifact artifact, CallContext context) {
-        logger.debug("FixLongitudinalAnalysisFacet.getData");
-
-        if (artifact instanceof FLYSArtifact) {
-            FLYSArtifact flys = (FLYSArtifact)artifact;
-
-            CalculationResult res =
-                (CalculationResult) flys.compute(context,
-                                                 ComputeType.ADVANCE,
-                                                 false);
-
-            FixAnalysisResult result = (FixAnalysisResult) res.getData();
-
-            KMIndex<AnalysisPeriod []> kmPeriods = result.getAnalysisPeriods();
-            if (kmPeriods == null) {
-                logger.warn("No analysis periods found.");
-                return null;
-            }
-            int periodNdx = index >> 8;
-            int qwdNdx = index & 255;
-            KMIndex<QWD> resPeriods =
-                    new KMIndex<QWD>();
-            for (KMIndex.Entry<AnalysisPeriod[]> entry: kmPeriods) {
-                AnalysisPeriod ap = entry.getValue()[periodNdx];
-                QWD[] qwds = ap.qwds;
-                for(int i = 0; i < qwds.length; i++) {
-                    if(qwds[i].getIndex() == qwdNdx) {
-                        resPeriods.add(entry.getKm(), qwds[i]);
-                    }
-                }
-            }
-
-
-            return resPeriods;
-        }
-        else {
-            logger.warn("Artifact is no instance of FLYSArtifact.");
-            return null;
-        }
-    }
-
-
-    /**
-     * Create a deep copy of this Facet.
-     * @return a deep copy.
-     */
-    @Override
-    public FixLongitudinalAnalysisFacet deepCopy() {
-        FixLongitudinalAnalysisFacet copy = new FixLongitudinalAnalysisFacet();
-        copy.set(this);
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixLongitudinalAvSectorFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,97 +0,0 @@
-package de.intevation.flys.artifacts.model.fixings;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.DataFacet;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-import de.intevation.flys.utils.KMIndex;
-
-public class FixLongitudinalAvSectorFacet
-extends DataFacet
-implements FacetTypes {
-
-    /** House logger. */
-    private static Logger logger =
-        Logger.getLogger(FixLongitudinalAvSectorFacet.class);
-
-    /** Trivial Constructor. */
-    public FixLongitudinalAvSectorFacet() {
-    }
-
-
-    public FixLongitudinalAvSectorFacet(
-        int ndx,
-        String name,
-        String description)
-    {
-        super(
-            ndx,
-            name,
-            description,
-            ComputeType.ADVANCE,
-            null,
-            null);
-    }
-
-
-    /**
-     * Returns the data this facet requires.
-     *
-     * @param artifact the owner artifact.
-     * @param context  the CallContext.
-     *
-     * @return the data as KMIndex.
-     */
-    @Override
-    public Object getData(Artifact artifact, CallContext context) {
-        logger.debug("FixLongitudinalAvSectorFacet.getData");
-
-        if (artifact instanceof FLYSArtifact) {
-            FLYSArtifact flys = (FLYSArtifact)artifact;
-
-            CalculationResult res =
-                (CalculationResult) flys.compute(context,
-                                                 ComputeType.ADVANCE,
-                                                 false);
-
-            FixAnalysisResult result = (FixAnalysisResult) res.getData();
-
-            KMIndex<AnalysisPeriod []> kmPeriods = result.getAnalysisPeriods();
-            if (kmPeriods == null) {
-                logger.warn("No analysis periods found.");
-                return null;
-            }
-            int periodNdx = index >> 2;
-            KMIndex<AnalysisPeriod> resPeriods =
-                    new KMIndex<AnalysisPeriod>();
-            for (KMIndex.Entry<AnalysisPeriod[]> entry: kmPeriods) {
-                AnalysisPeriod ap = entry.getValue()[periodNdx];
-                resPeriods.add(entry.getKm(), ap);
-            }
-
-            return resPeriods;
-        }
-        else {
-            logger.warn("Artifact is no instance of FLYSArtifact.");
-            return null;
-        }
-    }
-
-
-    /**
-     * Create a deep copy of this Facet.
-     * @return a deep copy.
-     */
-    @Override
-    public FixLongitudinalAvSectorFacet deepCopy() {
-        FixLongitudinalAvSectorFacet copy = new FixLongitudinalAvSectorFacet();
-        copy.set(this);
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixLongitudinalDeviationFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,118 +0,0 @@
-package de.intevation.flys.artifacts.model.fixings;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.flys.artifacts.access.FixAnalysisAccess;
-
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.DataFacet;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.Parameters;
-
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-import de.intevation.flys.utils.KMIndex;
-
-import org.apache.log4j.Logger;
-
-/**
- * Facet to show average W values for Q sectors.
- *
- * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
- */
-public class FixLongitudinalDeviationFacet
-extends      DataFacet
-implements   FacetTypes {
-
-    /** House logger. */
-    private static Logger logger = Logger.getLogger(FixLongitudinalDeviationFacet.class);
-
-    /** Trivial Constructor. */
-    public FixLongitudinalDeviationFacet() {
-    }
-
-
-    public FixLongitudinalDeviationFacet(
-        int ndx,
-        String name,
-        String description)
-    {
-        super(
-            ndx,
-            name,
-            description,
-            ComputeType.ADVANCE,
-            null,
-            null);
-    }
-
-
-    /**
-     * Returns the data this facet requires.
-     *
-     * @param artifact the owner artifact.
-     * @param context  the CallContext.
-     *
-     * @return the data as KMIndex.
-     */
-    @Override
-    public Object getData(Artifact artifact, CallContext context) {
-        logger.debug("FixLongitudinalDeviationFacet.getData");
-
-        if (artifact instanceof FLYSArtifact) {
-            FLYSArtifact flys = (FLYSArtifact)artifact;
-            FixAnalysisAccess access = new FixAnalysisAccess(flys, context);
-
-            CalculationResult res =
-                (CalculationResult) flys.compute(context,
-                                                 ComputeType.ADVANCE,
-                                                 false);
-
-            FixAnalysisResult result = (FixAnalysisResult) res.getData();
-
-            KMIndex<AnalysisPeriod []> kmPeriods = result.getAnalysisPeriods();
-
-            if (kmPeriods == null) {
-                logger.warn("No analysis periods found.");
-                return null;
-            }
-
-            Parameters params = result.getParameters();
-
-            KMIndex<double[]> kmIndex = new KMIndex<double[]>();
-            for (KMIndex.Entry<AnalysisPeriod[]> entry: kmPeriods) {
-                double km = entry.getKm();
-                double[] stdDev =
-                    params.interpolate("km", km, new String[] {"std-dev"});
-
-                if(stdDev == null) {
-                    logger.warn("getData: stdDev == null");
-                    continue;
-                }
-                kmIndex.add(km, stdDev);
-            }
-
-            return kmIndex;
-        }
-        else {
-            logger.warn("Artifact is no instance of FLYSArtifact.");
-            return null;
-        }
-    }
-
-
-    /**
-     * Create a deep copy of this Facet.
-     * @return a deep copy.
-     */
-    @Override
-    public FixLongitudinalDeviationFacet deepCopy() {
-        FixLongitudinalDeviationFacet copy = new FixLongitudinalDeviationFacet();
-        copy.set(this);
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixLongitudinalReferenceFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,115 +0,0 @@
-package de.intevation.flys.artifacts.model.fixings;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.flys.artifacts.access.FixAnalysisAccess;
-
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.DataFacet;
-import de.intevation.flys.artifacts.model.FacetTypes;
-
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-import de.intevation.flys.utils.KMIndex;
-
-import org.apache.log4j.Logger;
-
-
-/**
- * Facet to show average W values for Q sectors.
- *
- * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
- */
-public class FixLongitudinalReferenceFacet
-extends      DataFacet
-implements   FacetTypes {
-
-    /** House logger. */
-    private static Logger logger = Logger.getLogger(FixLongitudinalReferenceFacet.class);
-
-    /** Trivial Constructor. */
-    public FixLongitudinalReferenceFacet() {
-    }
-
-
-    public FixLongitudinalReferenceFacet(
-        int ndx,
-        String name,
-        String description)
-    {
-        super(
-            ndx,
-            name,
-            description,
-            ComputeType.ADVANCE,
-            null,
-            null);
-    }
-
-
-    /**
-     * Returns the data this facet requires.
-     *
-     * @param artifact the owner artifact.
-     * @param context  the CallContext.
-     *
-     * @return the data as KMIndex.
-     */
-    @Override
-    public Object getData(Artifact artifact, CallContext context) {
-        logger.debug("FixLongitudinalReferenceFacet.getData");
-
-        if (artifact instanceof FLYSArtifact) {
-            FLYSArtifact flys = (FLYSArtifact)artifact;
-            FixAnalysisAccess access = new FixAnalysisAccess(flys, context);
-
-            CalculationResult res =
-                (CalculationResult) flys.compute(context,
-                                                 ComputeType.ADVANCE,
-                                                 false);
-
-            FixAnalysisResult result = (FixAnalysisResult) res.getData();
-
-            KMIndex<QWD []> kmReference = result.getReferenced();
-
-            if (kmReference == null) {
-                logger.warn("No references found.");
-                return null;
-            }
-
-            int qwdNdx = index & 255;
-            KMIndex<QWD> resReference =
-                    new KMIndex<QWD>();
-            for (KMIndex.Entry<QWD[]> entry: kmReference) {
-                QWD[] qwds = entry.getValue();
-                for(int i = 0; i < qwds.length; i++) {
-                    if(qwds[i].getIndex() == qwdNdx) {
-                        resReference.add(entry.getKm(), qwds[i]);
-                    }
-                }
-            }
-            return resReference;
-        }
-        else {
-            logger.warn("Artifact is no instance of FLYSArtifact.");
-            return null;
-        }
-    }
-
-
-    /**
-     * Create a deep copy of this Facet.
-     * @return a deep copy.
-     */
-    @Override
-    public FixLongitudinalReferenceFacet deepCopy() {
-        FixLongitudinalReferenceFacet copy =
-            new FixLongitudinalReferenceFacet();
-        copy.set(this);
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixOutlierFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,101 +0,0 @@
-package de.intevation.flys.artifacts.model.fixings;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-import de.intevation.flys.utils.KMIndex;
-
-/**
- * Facet to show the outliers in a fix calculation.
- *
- * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
- */
-public class FixOutlierFacet
-extends      FixingsFacet
-implements   FacetTypes {
-
-    /** House logger. */
-    private static Logger logger = Logger.getLogger(FixOutlierFacet.class);
-
-    /** Trivial Constructor. */
-    public FixOutlierFacet() {
-    }
-
-
-    /**
-     * @param name
-     */
-    public FixOutlierFacet(String name, String description) {
-        super(0, name, description, ComputeType.ADVANCE, null, null);
-    }
-
-    public FixOutlierFacet(int index, String name, String description) {
-        super(index, name, description, ComputeType.ADVANCE, null, null);
-    }
-
-
-    /**
-     * Returns the data this facet requires.
-     *
-     * @param artifact the owner artifact; needs to be a FLYSArtifact.
-     * @param context  the CallContext; required to retrieve the value of
-     * <i>currentKm</i>.
-     *
-     * @return an array of QW objects or null.
-     */
-    @Override
-    public Object getData(Artifact artifact, CallContext context) {
-        logger.debug("FixOutlierFacet.getData");
-
-        if (artifact instanceof FLYSArtifact) {
-            FLYSArtifact flys = (FLYSArtifact)artifact;
-
-            CalculationResult res =
-                (CalculationResult) flys.compute(context,
-                                                 ComputeType.ADVANCE,
-                                                 false);
-
-            FixResult result = (FixResult) res.getData();
-            double currentKm = getCurrentKm(context);
-
-            KMIndex<QWI []>       kmQWs    = result.getOutliers();
-            KMIndex.Entry<QWI []> qwsEntry = kmQWs.binarySearch(currentKm);
-
-            QWI [] qws = null;
-            if (qwsEntry != null) {
-                qws = qwsEntry.getValue();
-
-                if (logger.isDebugEnabled()) {
-                    logger.debug("Found " + (qws != null ? qws.length : 0)
-                        + " KMIndex.Entry for km " + currentKm);
-                }
-            }
-            else {
-                logger.debug("Found no KMIndex.Entry for km " + currentKm);
-            }
-
-            return qws;
-        }
-
-        logger.warn("Not an instance of FLYSArtifact.");
-        return null;
-    }
-
-
-    /**
-     * Create a deep copy of this Facet.
-     * @return a deep copy.
-     */
-    @Override
-    public FixOutlierFacet deepCopy() {
-        FixOutlierFacet copy = new FixOutlierFacet();
-        copy.set(this);
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixRealizingCalculation.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,164 +0,0 @@
-package de.intevation.flys.artifacts.model.fixings;
-
-import de.intevation.flys.artifacts.access.FixRealizingAccess;
-
-import de.intevation.flys.artifacts.math.fitting.Function;
-
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.FixingsOverview;
-import de.intevation.flys.artifacts.model.RiverFactory;
-import de.intevation.flys.artifacts.model.Segment;
-import de.intevation.flys.artifacts.model.WQKms;
-import de.intevation.flys.artifacts.model.Parameters;
-
-import de.intevation.flys.model.River;
-
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-/** Calculation for FixRealize (german: ausgel. WSPL). */
-public class FixRealizingCalculation
-extends      FixCalculation
-{
-    private static Logger log =
-        Logger.getLogger(FixRealizingCalculation.class);
-
-    protected boolean       isQ;
-    protected List<Segment> segments;
-
-    public FixRealizingCalculation() {
-    }
-
-    public FixRealizingCalculation(FixRealizingAccess access) {
-        super(access);
-
-        Boolean       isQ      = access.isQ();
-        List<Segment> segments = access.getSegments();
-
-        if (isQ == null) {
-            // TODO: i18n
-            addProblem("fix.realize.missing.is.q");
-        }
-
-        if (segments == null || segments.isEmpty()) {
-            // TODO: i18n
-            addProblem("fix.realize.missing.segments");
-        }
-
-        River r = RiverFactory.getRiver(river);
-
-        if (r == null) {
-            // TODO: i18n
-            addProblem("fix.no.such.river");
-        }
-
-        if (!hasProblems()) {
-            this.isQ      = isQ;
-            this.segments = segments;
-
-            // Convert from W to Q
-            Segment.setReferencePointConvertQ(segments, r, isQ, this);
-        }
-    }
-
-    @Override
-    protected CalculationResult innerCalculate(
-        FixingsOverview overview,
-        Function        func
-    ) {
-        ColumnCache cc = new ColumnCache();
-        FitResult fitResult = doFitting(overview, cc, func);
-
-        if (fitResult == null) {
-            return new CalculationResult(this);
-        }
-
-        Segment segment = segments.get(0);
-        int numResults = segment.numValues();
-
-        WQKms [] results = new WQKms[numResults];
-        for (int i = 0; i < results.length; ++i) {
-            results[i] = new WQKms();
-        }
-
-        Parameters parameters = fitResult.getParameters();
-
-        int kmIndex = parameters.columnIndex("km");
-        int [] parameterIndices =
-            parameters.columnIndices(func.getParameterNames());
-
-        double [] parameterValues = new double[parameterIndices.length];
-
-        for (int row = 0, R = parameters.size(); row < R; ++row) {
-            double km = parameters.get(row, kmIndex);
-
-            if (!segment.inside(km)) {
-                Segment nextSeg = null;
-                for (Segment seg: segments) {
-                    if (seg.inside(km)) {
-                        nextSeg = seg;
-                        break;
-                    }
-                }
-                if (nextSeg == null) {
-                    // TODO: i18n
-                    addProblem(km, "fix.cannot.find.segment");
-                    continue;
-                }
-                segment = nextSeg;
-            }
-
-            parameters.get(row, parameterIndices, parameterValues);
-
-            de.intevation.flys.artifacts.math.Function instance =
-                func.instantiate(parameterValues);
-
-            double [] values = segment.getValues();
-            for (int i = 0; i < numResults; ++i) {
-                double q = values[i];
-                double w = instance.value(q);
-
-                if (Double.isNaN(w)) {
-                    // TODO: i18n
-                    addProblem(km, "fix.cannot.calculate.function", q);
-                }
-                else {
-                    results[i].add(w, q, km);
-                }
-            }
-        }
-
-        // Name the curves.
-        for (int i = 0; i < results.length; ++i) {
-            results[i].setName(createName(i));
-        }
-
-        FixRealizingResult frr = new FixRealizingResult(
-            parameters,
-            fitResult.getReferenced(),
-            fitResult.getOutliers(),
-            results);
-
-        return new CalculationResult(frr, this);
-    }
-
-    // TODO: issue1109/2
-    protected String createName(int index) {
-        // TODO: i18n
-        StringBuilder sb = new StringBuilder(isQ ? "Q" : "W");
-        sb.append(" benutzerdefiniert (");
-        for (int i = 0, N = segments.size(); i < N; ++i) {
-            if (i > 0) {
-                sb.append("; ");
-            }
-            Segment segment = segments.get(i);
-            double [] backup = segment.getBackup();
-            double [] values = segment.getValues();
-            sb.append((backup != null ? backup : values)[index]);
-        }
-        sb.append(')');
-        return sb.toString();
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixRealizingResult.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-package de.intevation.flys.artifacts.model.fixings;
-
-import de.intevation.flys.artifacts.model.WQKms;
-
-import de.intevation.flys.artifacts.model.Parameters;
-import de.intevation.flys.artifacts.model.WQKmsResult;
-
-import de.intevation.flys.utils.KMIndex;
-
-/** Result of a FixRealizing Calculation. */
-public class FixRealizingResult
-extends      FixResult
-implements   WQKmsResult
-{
-    public WQKms [] wqkms;
-
-    public FixRealizingResult() {
-    }
-
-    public FixRealizingResult(
-        Parameters      parameters,
-        KMIndex<QWD []> referenced,
-        KMIndex<QWI []> outliers,
-        WQKms []        wqkms
-    ) {
-        super(parameters, referenced, outliers);
-        this.wqkms = wqkms;
-    }
-
-    @Override
-    public WQKms [] getWQKms() {
-        return wqkms;
-    }
-
-    public void setWQKms(WQKms [] wqkms) {
-        this.wqkms = wqkms;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixReferenceEventsFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,102 +0,0 @@
-package de.intevation.flys.artifacts.model.fixings;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-import de.intevation.flys.utils.KMIndex;
-
-
-/**
- * Facet to show W values for Q values at km for a date.
- *
- * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
- */
-public class FixReferenceEventsFacet
-extends      FixingsFacet
-implements   FacetTypes {
-
-    /** House logger. */
-    private static Logger logger = Logger.getLogger(FixReferenceEventsFacet.class);
-
-    /** Trivial Constructor. */
-    public FixReferenceEventsFacet() {
-    }
-
-
-    /**
-     * @param name
-     */
-    public FixReferenceEventsFacet(int index, String name, String description) {
-        super(index,
-             name,
-             description,
-             ComputeType.ADVANCE,
-             null,
-             null);
-    }
-
-
-    /**
-     * Returns the data this facet requires.
-     *
-     * @param artifact the owner artifact.
-     * @param context  the CallContext (ignored).
-     *
-     * @return the data.
-     */
-    @Override
-    public Object getData(Artifact artifact, CallContext context) {
-        logger.debug("FixReferenceEventsFacet.getData");
-
-        if (artifact instanceof FLYSArtifact) {
-            FLYSArtifact flys = (FLYSArtifact)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 {
-            logger.debug("Not an instance of FixationArtifact.");
-            return null;
-        }
-    }
-
-
-    /**
-     * Create a deep copy of this Facet.
-     * @return a deep copy.
-     */
-    @Override
-    public FixReferenceEventsFacet deepCopy() {
-        FixReferenceEventsFacet copy = new FixReferenceEventsFacet();
-        copy.set(this);
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixResult.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-package de.intevation.flys.artifacts.model.fixings;
-
-import de.intevation.flys.artifacts.model.Parameters;
-
-import de.intevation.flys.utils.KMIndex;
-
-import java.io.Serializable;
-
-public class FixResult
-implements   Serializable
-{
-    protected Parameters      parameters;
-    protected KMIndex<QWD []> referenced;
-    protected KMIndex<QWI []> outliers;
-
-    public FixResult() {
-    }
-
-    public FixResult(
-        Parameters      parameters,
-        KMIndex<QWD []> referenced,
-        KMIndex<QWI []> outliers
-    ) {
-        this.parameters = parameters;
-        this.referenced = referenced;
-        this.outliers   = outliers;
-    }
-
-    public KMIndex<QWD []> getReferenced() {
-        return referenced;
-    }
-
-    public void setReferenced(KMIndex<QWD []> referenced) {
-        this.referenced = referenced;
-    }
-
-    public KMIndex<QWI []> getOutliers() {
-        return outliers;
-    }
-
-    public void setOutliers(KMIndex<QWI []> outliers) {
-        this.outliers = outliers;
-    }
-
-    public Parameters getParameters() {
-        return parameters;
-    }
-
-    public void setParameters(Parameters parameters) {
-        this.parameters = parameters;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixWQCurveFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,125 +0,0 @@
-package de.intevation.flys.artifacts.model.fixings;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.flys.artifacts.access.FixAnalysisAccess;
-
-import de.intevation.flys.artifacts.math.fitting.Function;
-import de.intevation.flys.artifacts.math.fitting.FunctionFactory;
-
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.Parameters;
-
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-import org.apache.log4j.Logger;
-
-/**
- * Facet to show the W|Q values.
- *
- * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
- */
-public class FixWQCurveFacet
-extends      FixingsFacet
-implements   FacetTypes {
-
-    /** House logger. */
-    private static Logger logger = Logger.getLogger(FixWQCurveFacet.class);
-
-
-    /** Trivial Constructor. */
-    public FixWQCurveFacet() {
-    }
-
-
-    /**
-     * @param description Description of the facet.
-     */
-    public FixWQCurveFacet(String description) {
-        super(0, FIX_WQ_CURVE, description, ComputeType.ADVANCE, null, null);
-    }
-
-    public FixWQCurveFacet(int index, String description) {
-        super(index, FIX_WQ_CURVE, description, ComputeType.ADVANCE, null, null);
-    }
-
-
-    /**
-     * Returns the data this facet provides at given km, a function.
-     *
-     * @param artifact the owner artifact.
-     * @param context  the CallContext.
-     *
-     * @return the data.
-     */
-    @Override
-    public Object getData(Artifact artifact, CallContext context) {
-        logger.debug("getData");
-        if (artifact instanceof FLYSArtifact) {
-            FLYSArtifact flys = (FLYSArtifact)artifact;
-            FixAnalysisAccess access = new FixAnalysisAccess(flys, context);
-
-            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;
-            }
-
-            de.intevation.flys.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("Not an instance of FLYSArtifact / FixationArtifact.");
-            return null;
-        }
-    }
-
-    /**
-     * Create a deep copy of this Facet.
-     * @return a deep copy.
-     */
-    @Override
-    public FixWQCurveFacet deepCopy() {
-        FixWQCurveFacet copy = new FixWQCurveFacet();
-        copy.set(this);
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixWaterlevelFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-package de.intevation.flys.artifacts.model.fixings;
-
-import de.intevation.artifactdatabase.state.Facet;
-
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.WQKms;
-import de.intevation.flys.artifacts.model.WaterlevelFacet;
-
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-/** Waterlevel from fix realize compute. */
-public class FixWaterlevelFacet
-extends      WaterlevelFacet
-{
-    public FixWaterlevelFacet() {
-    }
-
-    public FixWaterlevelFacet(int index, String name, String description) {
-        super(index, name, description, ComputeType.ADVANCE, null, null);
-    }
-
-    public FixWaterlevelFacet(
-        int         index,
-        String      name,
-        String      description,
-        ComputeType type,
-        String      stateID,
-        String      hash
-    ) {
-        super(index, name, description, type, hash, stateID);
-    }
-
-    @Override
-    protected WQKms [] getWQKms(CalculationResult res) {
-        FixRealizingResult fr = (FixRealizingResult)res.getData();
-        return fr != null ? fr.getWQKms() : null;
-    }
-
-    /** Copy deeply. */
-    @Override
-    public Facet deepCopy() {
-        FixWaterlevelFacet copy = new FixWaterlevelFacet();
-        copy.set(this);
-        copy.type    = type;
-        copy.hash    = hash;
-        copy.stateId = stateId;
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixingsFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,52 +0,0 @@
-package de.intevation.flys.artifacts.model.fixings;
-
-import de.intevation.artifacts.CallContext;
-import de.intevation.flys.artifacts.model.DataFacet;
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-/**
- * Facet to access the current Km from the context safely
- *
- * @author <a href="mailto:bjoern.ricks@intevation.de">Björn Ricks</a>
- */
-public class FixingsFacet extends DataFacet {
-
-    public static final Double INVALID_KM = Double.valueOf(-1d);
-    public static final String CURRENT_KM = "currentKm";
-
-    public FixingsFacet() {
-    }
-
-    public  FixingsFacet(String name, String description) {
-        super(0, name, description, ComputeType.ADVANCE, null, null);
-    }
-
-    public FixingsFacet(
-            int         index,
-            String      name,
-            String      description,
-            ComputeType type,
-            String      hash,
-            String      stateId
-            ) {
-        super(index, name, description, type, hash, stateId);
-    }
-
-    /**
-     * Returns the current km from the context.
-     * If the context is null or doesn't contain a currentKm then a double value of -1 will
-     *  be returned.
-     * @param context The CallContext instance
-     * @return the current km as double
-     */
-    protected double getCurrentKm(CallContext context) {
-        if (context == null) {
-            return INVALID_KM;
-        }
-        Double dkm = (Double)context.getContextValue(CURRENT_KM);
-        if (dkm == null) {
-            return INVALID_KM;
-        }
-        return dkm.doubleValue();
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/QWD.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +0,0 @@
-package de.intevation.flys.artifacts.model.fixings;
-
-import java.util.Date;
-
-public class QWD
-extends      QWI
-{
-    protected double deltaW;
-
-    public QWD() {
-    }
-
-    public QWD(double q, double w) {
-        super(q, w);
-    }
-
-    public QWD(
-        double  q,
-        double  w,
-        String  description,
-        Date    date,
-        boolean interpolated,
-        double  deltaW,
-        int     index
-    ) {
-        super(q, w, description, date, interpolated, index);
-        this.deltaW = deltaW;
-    }
-
-    public double getDeltaW() {
-        return deltaW;
-    }
-
-    public void setDeltaW(double deltaW) {
-        this.deltaW = deltaW;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/QWI.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +0,0 @@
-package de.intevation.flys.artifacts.model.fixings;
-
-import de.intevation.flys.artifacts.model.QW;
-
-import java.util.Date;
-
-public class QWI
-extends      QW
-{
-    protected String  description;
-    protected Date    date;
-    protected boolean interpolated;
-    protected int     index;
-
-    public QWI() {
-    }
-
-    public QWI(double q, double w) {
-        super(q, w);
-    }
-
-    public QWI(
-        double  q,
-        double  w,
-        String  description,
-        Date    date,
-        boolean interpolated,
-        int     index
-    ) {
-        super(q, w);
-        this.description  = description;
-        this.date         = date;
-        this.interpolated = interpolated;
-        this.index        = index;
-    }
-
-    public Date getDate() {
-        return date;
-    }
-
-    public void setDate(Date date) {
-        this.date = date;
-    }
-
-    public String getDescription() {
-        return description;
-    }
-
-    public void setDescription(String description) {
-        this.description = description;
-    }
-
-    public boolean getInterpolated() {
-        return interpolated;
-    }
-
-    public void setInterpolated(boolean interpolated) {
-        this.interpolated = interpolated;
-    }
-
-    public int getIndex() {
-        return index;
-    }
-
-    public void setIndex(int index) {
-        this.index = index;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/map/HWS.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,153 +0,0 @@
-package de.intevation.flys.artifacts.model.map;
-
-import org.geotools.feature.simple.SimpleFeatureBuilder;
-import org.opengis.feature.simple.SimpleFeature;
-import org.opengis.feature.simple.SimpleFeatureType;
-
-import com.vividsolutions.jts.geom.Geometry;
-
-import de.intevation.flys.artifacts.model.NamedObjectImpl;
-import de.intevation.flys.utils.GeometryUtils;
-
-
-public class HWS
-extends NamedObjectImpl
-{
-
-    public enum TYPE {LINE, POINT};
-
-    private Geometry geom;
-    private String id;
-    private int kind;
-    private int official;
-    private String fedState;
-    private String description;
-    private TYPE type;
-
-    public HWS() {
-        this.geom = null;
-        // TODO Auto-generated constructor stub
-    }
-
-    public HWS(String name) {
-        super(name);
-        this.geom = null;
-    }
-
-    public HWS(
-        String name,
-        Geometry geom,
-        String id,
-        int kind,
-        int official,
-        String fedState,
-        String description,
-        TYPE type
-    ) {
-        super(name);
-        this.geom = geom;
-        this.id = id;
-        this.kind = kind;
-        this.official = official;
-        this.fedState = fedState;
-        this.description = description;
-        this.type = type;
-    }
-
-    public Geometry getGeom() {
-        return geom;
-    }
-
-    public void setGeom(Geometry geom) {
-        this.geom = geom;
-    }
-
-    public String getId() {
-        return id;
-    }
-
-    public void setId(String id) {
-        this.id = id;
-    }
-
-    public int getKind() {
-        return kind;
-    }
-
-    public void setKind(int kind) {
-        this.kind = kind;
-    }
-
-    public boolean isOfficial() {
-        return official == 1;
-    }
-
-    public void setOfficial(boolean o) {
-        this.official = o ? 1 : 0;
-    }
-
-    public String getFedState() {
-        return fedState;
-    }
-
-    public void setFedState(String fedState) {
-        this.fedState = fedState;
-    }
-
-    public String getDescription() {
-        return description;
-    }
-
-    public void setDescription(String description) {
-        this.description = description;
-    }
-
-    public TYPE getType() {
-        return type;
-    }
-
-    public void setType(TYPE type) {
-        this.type = type;
-    }
-
-    public SimpleFeatureType getFeatureType() {
-        int srid = this.geom.getSRID();
-        String srs = "EPSG:" + srid;
-
-        Object[][] attrs = new Object[5][];
-        attrs[0] = new Object[] { "name", String.class };
-        attrs[1] = new Object[] { "description", String.class };
-        attrs[2] = new Object[] { "TYP", String.class };
-        attrs[3] = new Object[] { "fed_state", String.class };
-        attrs[4] = new Object[] { "official", Integer.class };
-        SimpleFeatureType ft =
-            GeometryUtils.buildFeatureType(
-                "hws", srs, this.geom.getClass(), attrs);
-        return ft;
-    }
-
-    public SimpleFeature getFeature() {
-        SimpleFeatureType ft = getFeatureType();
-
-        SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(ft);
-        featureBuilder.add(this.geom);
-        featureBuilder.add(this.name);
-        featureBuilder.add(this.description);
-        if (this.kind == 1) {
-            featureBuilder.add("Rohr 1");
-        }
-        else if (this.kind == 2) {
-            featureBuilder.add("Damm");
-        }
-        else if (this.kind == 3) {
-            featureBuilder.add("Graben");
-        }
-        else {
-            featureBuilder.add("");
-        }
-        featureBuilder.add(this.fedState);
-        featureBuilder.add(this.official);
-
-        return featureBuilder.buildFeature(null);
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/map/HWSContainer.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,93 +0,0 @@
-package de.intevation.flys.artifacts.model.map;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-public class HWSContainer
-{
-    private static Logger logger = Logger.getLogger(HWSContainer.class);
-    private String river;
-    private HWS.TYPE type;
-    private List<HWS> hws;
-
-    public HWSContainer() {
-        river = null;
-        hws = new ArrayList<HWS>();
-    }
-
-    public HWSContainer(String river, HWS.TYPE type, List<HWS> hws) {
-        this.river = river;
-        this.hws = hws;
-        this.type = type;
-    }
-
-    public void setRiver(String river) {
-        this.river = river;
-    }
-
-    public String getRiver() {
-        return this.river;
-    }
-
-    public HWS.TYPE getType() {
-        return type;
-    }
-
-    public void setType(HWS.TYPE type) {
-        this.type = type;
-    }
-
-    public List<HWS> getHws() {
-        return hws;
-    }
-
-    public void addHws(HWS hws) {
-        logger.debug("add hws: " + hws.getName());
-        this.hws.add(hws);
-    }
-
-    public void addHws(List<HWS> hws) {
-        this.hws.addAll(hws);
-    }
-
-    public List<HWS> getOfficialHWS() {
-        if (hws == null || hws.size() == 0) {
-            return new ArrayList<HWS>();
-        }
-        List<HWS> results = new ArrayList<HWS>();
-        for (HWS h: hws) {
-            if (h.isOfficial()) {
-                results.add(h);
-            }
-        }
-        return results;
-    }
-
-    public List<HWS> getHws(String name) {
-        logger.debug("find: " + name + " in " + hws.size() + " elements");
-        if (hws == null || hws.size() == 0) {
-            return new ArrayList<HWS>();
-        }
-        List<HWS> results = new ArrayList<HWS>();
-        for (HWS h: hws) {
-            if (h.getName().equals(name)) {
-                results.add(h);
-            }
-        }
-        logger.debug("found: " + results.size());
-        return results;
-    }
-
-    public List<HWS> getHws(List<String> list) {
-        if (hws == null || hws.size() == 0) {
-            return new ArrayList<HWS>();
-        }
-        List<HWS> results = new ArrayList<HWS>();
-        for (String name : list) {
-            results.addAll(getHws(name));
-        }
-        return results;
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/map/HWSFactory.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,179 +0,0 @@
-package de.intevation.flys.artifacts.model.map;
-
-import java.util.List;
-
-import net.sf.ehcache.Cache;
-import net.sf.ehcache.Element;
-
-import org.apache.log4j.Logger;
-import org.hibernate.SQLQuery;
-import org.hibernate.Session;
-import org.hibernate.type.StandardBasicTypes;
-import org.hibernatespatial.GeometryUserType;
-
-import com.vividsolutions.jts.geom.Geometry;
-
-import de.intevation.flys.artifacts.cache.CacheFactory;
-import de.intevation.flys.backend.SessionHolder;
-
-
-public class HWSFactory
-{
-    /** Private logger to use here. */
-    private static Logger log = Logger.getLogger(HWSFactory.class);
-
-    private static final int HWS_LINES = 0;
-    private static final int HWS_POINTS = 1;
-
-    public static final String SQL_SELECT_LINES =
-        "SELECT hl.name, hl.geom, hl.id, hl.kind_id, hl.official, fs.name AS fed, hl.description " +
-        "   FROM hws_lines hl" +
-        "       JOIN rivers r ON hl.river_id = r.id" +
-        "       LEFT JOIN fed_states fs ON hl.fed_state_id = fs.id" +
-        "   WHERE r.name = :river";
-
-    public static final String SQL_SELECT_POINTS =
-        "SELECT hp.name, hp.geom, hp.id, hp.kind_id, hp.official, fs.name AS fed, hp.description " +
-        "   FROM hws_points hp" +
-        "       JOIN rivers r ON hp.river_id = r.id" +
-        "       LEFT JOIN fed_states fs ON hp.fed_state_id = fs.id" +
-        "   WHERE r.name = :river";
-
-
-    private HWSFactory() {
-    }
-
-
-    public static HWSContainer getHWSLines(String river) {
-        log.debug("HWSFactory.getHWS");
-        Cache cache = CacheFactory.getCache(StaticHWSCacheKey.CACHE_NAME);
-
-        StaticHWSCacheKey cacheKey;
-
-        if (cache != null) {
-            cacheKey = new StaticHWSCacheKey(river, HWS_LINES);
-            Element element = cache.get(cacheKey);
-            if (element != null) {
-                log.debug("Got static hws values from cache");
-                return (HWSContainer)element.getValue();
-            }
-        }
-        else {
-            cacheKey = null;
-        }
-
-        HWSContainer values = getHWSLinesUncached(river);
-
-        if (values != null && cacheKey != null) {
-            log.debug("Store static hws values in cache.");
-            Element element = new Element(cacheKey, values);
-            cache.put(element);
-        }
-        return values;
-    }
-
-    public static HWSContainer getHWSPoints(String river) {
-        log.debug("HWSFactory.getHWS");
-        Cache cache = CacheFactory.getCache(StaticHWSCacheKey.CACHE_NAME);
-
-        StaticHWSCacheKey cacheKey;
-
-        if (cache != null) {
-            cacheKey = new StaticHWSCacheKey(river, HWS_LINES);
-            Element element = cache.get(cacheKey);
-            if (element != null) {
-                log.debug("Got static hws values from cache");
-                return (HWSContainer)element.getValue();
-            }
-        }
-        else {
-            cacheKey = null;
-        }
-
-        HWSContainer values = getHWSPointsUncached(river);
-
-        if (values != null && cacheKey != null) {
-            log.debug("Store static hws values in cache.");
-            Element element = new Element(cacheKey, values);
-            cache.put(element);
-        }
-        return values;
-    }
-
-    private static HWSContainer getHWSLinesUncached(String river) {
-        if (log.isDebugEnabled()) {
-            log.debug("HWSFactory.getHWSLinesUncached");
-        }
-
-        Session session = SessionHolder.HOLDER.get();
-        SQLQuery sqlQuery = null;
-        HWSContainer container = new HWSContainer();
-        container.setRiver(river);
-        container.setType(HWS.TYPE.LINE);
-        sqlQuery = session.createSQLQuery(SQL_SELECT_LINES)
-            .addScalar("name", StandardBasicTypes.STRING)
-            .addScalar("geom", GeometryUserType.TYPE)
-            .addScalar("id", StandardBasicTypes.STRING)
-            .addScalar("kind_id", StandardBasicTypes.INTEGER)
-            .addScalar("official", StandardBasicTypes.INTEGER)
-            .addScalar("fed", StandardBasicTypes.STRING)
-            .addScalar("description", StandardBasicTypes.STRING);
-
-        sqlQuery.setString("river", river);
-        List<Object []> resultsLines = sqlQuery.list();
-
-        for (Object [] row: resultsLines) {
-            container.addHws(
-                new HWS(
-                    (String) row[0],
-                    (Geometry) row[1],
-                    (String) row[2],
-                    (Integer) row[3],
-                    (Integer) row[4],
-                    (String) row[5],
-                    (String) row[6],
-                    HWS.TYPE.LINE));
-        }
-
-        return container;
-    }
-
-    private static HWSContainer getHWSPointsUncached(String river) {
-        if (log.isDebugEnabled()) {
-            log.debug("HWSFactory.getHWSLinesUncached");
-        }
-
-        Session session = SessionHolder.HOLDER.get();
-        SQLQuery sqlQuery = null;
-        HWSContainer container = new HWSContainer();
-        container.setRiver(river);
-        container.setType(HWS.TYPE.LINE);
-        sqlQuery = session.createSQLQuery(SQL_SELECT_POINTS)
-            .addScalar("name", StandardBasicTypes.STRING)
-            .addScalar("geom", GeometryUserType.TYPE)
-            .addScalar("id", StandardBasicTypes.STRING)
-            .addScalar("kind_id", StandardBasicTypes.INTEGER)
-            .addScalar("official", StandardBasicTypes.INTEGER)
-            .addScalar("fed", StandardBasicTypes.STRING)
-            .addScalar("description", StandardBasicTypes.STRING);
-
-        sqlQuery.setString("river", river);
-        List<Object []> resultsPoints = sqlQuery.list();
-
-        for (int i = 0; i < resultsPoints.size(); i++) {
-            Object[] row = resultsPoints.get(i);
-            container.addHws(
-                new HWS(
-                    (String) row[0],
-                    (Geometry) row[1],
-                    (String) row[2],
-                    (Integer) row[3],
-                    (Integer) row[4],
-                    (String) row[5],
-                    (String) row[6],
-                    HWS.TYPE.POINT));
-        }
-
-        return container;
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/map/StaticHWSCacheKey.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-package de.intevation.flys.artifacts.model.map;
-
-
-public class StaticHWSCacheKey
-{
-    public static final String CACHE_NAME = "hws-value-table-static";
-
-    private String river;
-    private int type;
-
-    public StaticHWSCacheKey(String river, int type) {
-        this.river = river;
-        this.type = type;
-    }
-
-    public int hashCode() {
-        return river.hashCode() | (type << 8);
-    }
-
-    public boolean equals(Object other) {
-        if (!(other instanceof StaticHWSCacheKey)) {
-            return false;
-        }
-        StaticHWSCacheKey o = (StaticHWSCacheKey) other;
-        return this.river == o.river;
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/map/WMSDBLayerFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,106 +0,0 @@
-package de.intevation.flys.artifacts.model.map;
-
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-
-public class WMSDBLayerFacet extends WMSLayerFacet {
-
-    protected String data;
-    protected String filter;
-    protected String labelItem;
-    protected String geometryType;
-    protected String connection;
-    protected String connectionType;
-
-
-    public WMSDBLayerFacet() {
-        super();
-    }
-
-
-    public WMSDBLayerFacet(int index, String name, String description) {
-        this(index, name, description, ComputeType.FEED, null, null);
-    }
-
-
-    public WMSDBLayerFacet(
-        int         index,
-        String      name,
-        String      description,
-        ComputeType type,
-        String      stateId,
-        String      hash
-
-    ) {
-        super(index, name, description, type, stateId, hash);
-    }
-
-
-    public WMSDBLayerFacet(
-        int         index,
-        String      name,
-        String      description,
-        ComputeType type,
-        String      stateId,
-        String      hash,
-        String      url
-    ) {
-        super(index, name, description, type, stateId, hash, url);
-    }
-
-
-    public void setFilter(String filter) {
-        this.filter = filter;
-    }
-
-    public String getFilter() {
-        return filter;
-    }
-
-    public void setData(String data) {
-        this.data = data;
-    }
-
-    public String getData() {
-        return data;
-    }
-
-    public void setGeometryType(String geometryType) {
-        this.geometryType = geometryType;
-    }
-
-    public String getGeometryType() {
-        return geometryType;
-    }
-
-    public void setConnection(String connection) {
-        this.connection = connection;
-    }
-
-    public String getConnection() {
-        return connection;
-    }
-
-    public void setConnectionType(String connectionType) {
-        this.connectionType = connectionType;
-    }
-
-    public String getConnectionType() {
-        return connectionType;
-    }
-
-    public void setLabelItem(String labelItem) {
-        this.labelItem = labelItem;
-    }
-
-    public String getLabelItem() {
-        return labelItem;
-    }
-
-
-    @Override
-    public boolean isQueryable() {
-        return true;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/map/WMSLayerFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,184 +0,0 @@
-package de.intevation.flys.artifacts.model.map;
-
-import com.vividsolutions.jts.geom.Envelope;
-
-import de.intevation.artifactdatabase.state.DefaultFacet;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.ArtifactNamespaceContext;
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-import de.intevation.flys.utils.GeometryUtils;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-
-
-public class WMSLayerFacet
-extends      DefaultFacet
-{
-    protected ComputeType  type;
-    protected List<String> layers;
-    protected String       stateId;
-    protected String       hash;
-    protected String       url;
-    protected Envelope     extent;
-    protected Envelope     originalExtent;
-    protected String       srid;
-
-
-    private static final Logger logger = Logger.getLogger(WMSLayerFacet.class);
-
-    public WMSLayerFacet() {
-    }
-
-
-    public WMSLayerFacet(int index, String name, String description) {
-        this(index, name, description, ComputeType.FEED, null, null);
-    }
-
-
-    public WMSLayerFacet(
-        int         index,
-        String      name,
-        String      description,
-        ComputeType type,
-        String      stateId,
-        String      hash
-
-    ) {
-        super(index, name, description);
-        this.layers  = new ArrayList<String>();
-        this.type    = type;
-        this.stateId = stateId;
-        this.hash    = hash;
-    }
-
-
-    public WMSLayerFacet(
-        int         index,
-        String      name,
-        String      description,
-        ComputeType type,
-        String      stateId,
-        String      hash,
-        String      url
-    ) {
-        this(index, name, description, type, stateId, hash);
-        this.url = url;
-    }
-
-
-    public void addLayer(String name) {
-        if (name != null && name.length() > 0) {
-            layers.add(name);
-        }
-    }
-
-
-    public List<String> getLayers() {
-        return layers;
-    }
-
-
-    public void removeLayer(String layer) {
-        if (layers != null) {
-            layers.remove(layer);
-        }
-    }
-
-
-    public void setExtent(Envelope extent) {
-        if (extent != null) {
-            this.extent = extent;
-        }
-        else {
-            logger.debug("setExtent(): extent is null");
-        }
-    }
-
-
-    public Envelope getExtent() {
-        return extent;
-    }
-
-
-    public void setOriginalExtent(Envelope originalExtent) {
-        this.originalExtent = originalExtent;
-    }
-
-
-    public Envelope getOriginalExtent() {
-        return originalExtent;
-    }
-
-
-    public void setSrid(String srid) {
-        if (srid != null) {
-            this.srid = srid;
-        }
-    }
-
-
-    public String getSrid() {
-        return srid;
-    }
-
-
-    @Override
-    public Object getData(Artifact artifact, CallContext context) {
-        return null;
-    }
-
-
-    @Override
-    public Node toXML(Document doc) {
-        ElementCreator ec = new ElementCreator(
-            doc,
-            ArtifactNamespaceContext.NAMESPACE_URI,
-            ArtifactNamespaceContext.NAMESPACE_PREFIX);
-
-        Element facet = ec.create("facet");
-        ec.addAttr(facet, "description", description, true);
-        ec.addAttr(facet, "index", String.valueOf(index), true);
-        ec.addAttr(facet, "name", name, true);
-        ec.addAttr(facet, "url", url, true);
-        ec.addAttr(facet, "layers", layers.get(0), true);
-        ec.addAttr(facet, "srid", srid != null ? srid : "", true);
-        ec.addAttr(facet, "extent", originalExtent != null
-            ? GeometryUtils.jtsBoundsToOLBounds(originalExtent)
-            : "", true);
-        ec.addAttr(facet, "queryable", String.valueOf(isQueryable()), true);
-
-        return facet;
-    }
-
-
-    public boolean isQueryable() {
-        return false;
-    }
-
-
-    @Override
-    public Facet deepCopy() {
-        WMSLayerFacet copy = new WMSLayerFacet();
-        copy.set(this);
-
-        copy.type    = type;
-        copy.layers  = new ArrayList<String>(layers);
-        copy.stateId = stateId;
-        copy.hash    = hash;
-        copy.url     = url;
-        copy.extent  = extent;
-        copy.srid    = srid;
-
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/map/WSPLGENCalculation.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,78 +0,0 @@
-package de.intevation.flys.artifacts.model.map;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.CallMeta;
-import de.intevation.flys.artifacts.model.Calculation;
-
-
-public class WSPLGENCalculation extends Calculation {
-
-    private static final Logger log = Logger.getLogger(WSPLGENCalculation.class);
-
-    protected Map<Integer, String> errors;
-    protected Map<Integer, String> warnings;
-
-
-    public WSPLGENCalculation() {
-        errors   = new HashMap<Integer, String>();
-        warnings = new HashMap<Integer, String>();
-    }
-
-
-    public void addError(Integer key, String msg) {
-        log.debug("New error: (" + key + ") " + msg);
-        errors.put(key, msg);
-    }
-
-
-    public void addWarning(Integer key, String msg) {
-        log.debug("New warning: (" + key + ") " + msg);
-        warnings.put(key, msg);
-    }
-
-
-    public int numErrors() {
-        return errors.size();
-    }
-
-
-    public int numWarnings() {
-        return warnings.size();
-    }
-
-
-    @Override
-    public void toXML(Document document, CallMeta meta) {
-        Element root = document.createElement("problems");
-
-        if (numErrors() > 0) {
-            for (Map.Entry<Integer, String> entry: errors.entrySet()) {
-                Element problem = document.createElement("problem");
-                problem.setAttribute("error", String.valueOf(entry.getKey()));
-                problem.setTextContent(entry.getValue());
-
-                root.appendChild(problem);
-            }
-        }
-
-        if (numWarnings() > 0) {
-            for (Map.Entry<Integer, String> entry: warnings.entrySet()) {
-                Element problem = document.createElement("problem");
-                problem.setAttribute("error", String.valueOf(entry.getKey()));
-                problem.setTextContent(entry.getValue());
-
-                root.appendChild(problem);
-            }
-        }
-
-        document.appendChild(root);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/map/WSPLGENJob.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,473 +0,0 @@
-package de.intevation.flys.artifacts.model.map;
-
-import java.io.IOException;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.wsplgen.FacetCreator;
-
-
-public class WSPLGENJob {
-
-    public static final String GEL_SPERRE   = "SPERRE";
-    public static final String GEL_NOSPERRE = "NOSPERRE";
-
-
-    protected FLYSArtifact artifact;
-
-    protected CallContext callContext;
-
-    protected WSPLGENCalculation calculation;
-
-    protected FacetCreator facetCreator;
-
-    protected File workingDir;
-
-    protected String dgm;
-    protected String pro;
-    protected String wsp;
-    protected String wspTag;
-    protected String axis;
-    protected String area;
-    protected String gel;
-    protected String outFile;
-
-    protected List<String> lin;
-
-    protected int out;
-
-    protected double start;
-    protected double end;
-    protected double from;
-    protected double to;
-    protected double diff;
-    protected double dist;
-
-
-
-    public WSPLGENJob(
-        FLYSArtifact       flys,
-        File               workingDir,
-        FacetCreator       facetCreator,
-        CallContext        context,
-        WSPLGENCalculation calculation)
-    {
-        this.artifact     = flys;
-        this.workingDir   = workingDir;
-        this.facetCreator = facetCreator;
-        this.callContext  = context;
-        this.calculation  = calculation;
-
-        out   = -1;
-        start = Double.NaN;
-        end   = Double.NaN;
-        from  = Double.NaN;
-        to    = Double.NaN;
-        diff  = Double.NaN;
-        dist  = Double.NaN;
-        lin   = new ArrayList<String>(3);
-    }
-
-
-    public File getWorkingDir() {
-        return workingDir;
-    }
-
-
-    public FLYSArtifact getArtifact() {
-        return artifact;
-    }
-
-
-    public FacetCreator getFacetCreator() {
-        return facetCreator;
-    }
-
-
-    public WSPLGENCalculation getCalculation() {
-        return calculation;
-    }
-
-
-    public CallContext getCallContext() {
-        return callContext;
-    }
-
-
-    public void setWsp(String wsp) {
-        this.wsp = wsp;
-    }
-
-
-    public String getWsp() {
-        return wsp;
-    }
-
-
-    public void setWspTag(String wspTag) {
-        this.wspTag = wspTag;
-    }
-
-
-    public String getWspTag() {
-        return wspTag;
-    }
-
-
-    public void addLin(String lin) {
-        this.lin.add(lin);
-    }
-
-
-    public List<String> getLin() {
-        return lin;
-    }
-
-
-    public void setAxis(String axis) {
-        this.axis = axis;
-    }
-
-
-    public String getAxis() {
-        return axis;
-    }
-
-
-    public void setArea(String area) {
-        this.area = area;
-    }
-
-
-    public String getArea() {
-        return area;
-    }
-
-
-    public void setOut(int out) {
-        this.out = out;
-    }
-
-
-    public int getOut() {
-        return out;
-    }
-
-
-    public void setOutFile(String outFile) {
-        this.outFile = outFile;
-    }
-
-
-    public String getOutFile() {
-        return outFile;
-    }
-
-
-    public void setStart(double start) {
-        this.start = start;
-    }
-
-
-    public double getStart() {
-        return start;
-    }
-
-
-    public void setEnd(double end) {
-        this.end = end;
-    }
-
-
-    public double getEnd() {
-        return end;
-    }
-
-
-    public void setPro(String pro) {
-        this.pro = pro;
-    }
-
-
-    public String getPro() {
-        return pro;
-    }
-
-
-    public void setDgm(String dgm) {
-        this.dgm = dgm;
-    }
-
-
-    public String getDgm() {
-        return dgm;
-    }
-
-
-    public void setFrom(double from) {
-        this.from = from;
-    }
-
-
-    public double getFrom() {
-        return from;
-    }
-
-
-    public void setTo(double to) {
-        this.to = to;
-    }
-
-
-    public double getTo() {
-        return to;
-    }
-
-
-    public void setDiff(double diff) {
-        this.diff = diff;
-    }
-
-
-    public double getDiff() {
-        return diff;
-    }
-
-
-    public void setDist(double dist) {
-        this.dist = dist;
-    }
-
-
-    public double getDist() {
-        return dist;
-    }
-
-
-    public void setGel(String gel) {
-        if (gel == null || gel.length() == 0) {
-            return;
-        }
-
-        if (gel.equals(GEL_SPERRE) || gel.equals(GEL_NOSPERRE)) {
-            this.gel = gel;
-        }
-    }
-
-
-    public String getGel() {
-        return gel;
-    }
-
-
-    public void toFile(File file)
-    throws IOException, IllegalArgumentException
-    {
-        PrintWriter writer = null;
-
-        try {
-            writer =
-                new PrintWriter(
-                    new OutputStreamWriter(
-                        new FileOutputStream(file)));
-
-            write(writer);
-        }
-        finally {
-            if (writer != null) {
-                writer.flush();
-                writer.close();
-            }
-        }
-    }
-
-
-    protected void write(PrintWriter writer)
-    throws IOException, IllegalArgumentException
-    {
-        writeWsp(writer);    // required
-        writeWspTag(writer); // required
-        writeLin(writer);
-        writeAxis(writer);
-        writeArea(writer);
-        writeOut(writer);
-        writeOutFile(writer);
-        writeRange(writer);
-        writeDelta(writer);
-        writeGel(writer);
-        writeDist(writer);
-        writePro(writer);
-        writeDgm(writer);    // required
-    }
-
-
-    protected void writeWsp(PrintWriter writer)
-    throws IllegalArgumentException
-    {
-        String wsp = getWsp();
-
-        if (wsp != null && wsp.length() > 0) {
-            writer.println("-WSP=\"" + wsp + "\"");
-            return;
-        }
-
-        throw new IllegalArgumentException("Required WSP missing!");
-    }
-
-    protected void writeWspTag(PrintWriter writer)
-    throws IllegalArgumentException
-    {
-        String wspTag = getWspTag();
-
-        if (wspTag != null && wspTag.length() > 0) {
-            writer.println("-WSPTAG=\"" + wspTag + "\"");
-            return;
-        }
-
-        throw new IllegalArgumentException("Required WSPTAG missing!");
-    }
-
-    protected void writeLin(PrintWriter writer)
-    throws IllegalArgumentException
-    {
-        List<String> lins = getLin();
-
-        if (lins != null && !lins.isEmpty()) {
-            for (String lin: lins) {
-                writer.println("-LIN=\"" + lin + "\"");
-            }
-        }
-    }
-
-    protected void writeAxis(PrintWriter writer)
-    throws IllegalArgumentException
-    {
-        String axis = getAxis();
-
-        if (axis != null && axis.length() > 0) {
-            writer.println("-ACHSE=\"" + axis + "\"");
-        }
-    }
-
-    protected void writeGel(PrintWriter writer)
-    throws IllegalArgumentException
-    {
-        if (area != null && area.length() > 0) {
-            writer.println("-GEL=" + getGel());
-        }
-    }
-
-    protected void writeArea(PrintWriter writer)
-    throws IllegalArgumentException
-    {
-        String area = getArea();
-
-        if (area != null && area.length() > 0) {
-            writer.println("-GEBIET=\"" + area + "\"");
-        }
-    }
-
-
-    protected void writeOut(PrintWriter writer)
-    throws IllegalArgumentException
-    {
-        int out = getOut();
-
-        if (out >= 0) {
-            writer.println("-OUTPUT=" + String.valueOf(out));
-        }
-    }
-
-    protected void writeOutFile(PrintWriter writer)
-    throws IllegalArgumentException
-    {
-        String outFile = getOutFile();
-
-        if (outFile != null && outFile.length() > 0) {
-            writer.println("-AUSGABE=\""+ outFile + "\"");
-        }
-    }
-
-    protected void writeRange(PrintWriter writer)
-    throws IllegalArgumentException
-    {
-        StringBuilder sb = new StringBuilder("-STRECKE=");
-
-        double start = getStart();
-        double end   = getEnd();
-
-        if (Double.isNaN(start) && Double.isNaN(end)) {
-            return;
-        }
-
-        if (! Double.isNaN(getStart())) {
-            sb.append(getStart());
-        }
-
-        sb.append(",");
-
-        if (! Double.isNaN(getEnd())) {
-            sb.append(getEnd());
-        }
-
-        writer.println(sb.toString());
-    }
-
-    protected void writeDelta(PrintWriter writer)
-    throws IllegalArgumentException
-    {
-        StringBuilder sb = new StringBuilder("-DELTA=");
-        if (! Double.isNaN(from)) {
-            sb.append(from);
-        }
-
-        sb.append(",");
-
-        if (! Double.isNaN(to)) {
-            sb.append(to);
-        }
-
-        sb.append(",");
-
-        if (! Double.isNaN(diff)) {
-            sb.append(diff);
-        }
-
-        writer.println(sb.toString());
-    }
-
-    protected void writeDist(PrintWriter writer)
-    throws IllegalArgumentException
-    {
-        if (! Double.isNaN(getDist())) {
-            writer.println("-DIST=" + String.valueOf(getDist()));
-        }
-    }
-
-    protected void writePro(PrintWriter writer)
-    throws IllegalArgumentException
-    {
-        if (pro != null && pro.length() > 0) {
-            writer.println("-PRO=\"" + getPro() + "\"");
-        }
-    }
-
-    protected void writeDgm(PrintWriter writer)
-    throws IllegalArgumentException
-    {
-        if (dgm != null && dgm.length() > 0) {
-            writer.println("-DGM=\"" + getDgm() + "\"");
-            return;
-        }
-
-        throw new IllegalArgumentException("Required DGM missing!");
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/map/WSPLGENLayerFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-package de.intevation.flys.artifacts.model.map;
-
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-public class WSPLGENLayerFacet
-extends      WMSLayerFacet
-{
-    public WSPLGENLayerFacet() {
-    }
-
-
-    public WSPLGENLayerFacet(int index, String name, String description) {
-        this(index, name, description, ComputeType.FEED, null, null);
-    }
-
-
-    public WSPLGENLayerFacet(
-        int         index,
-        String      name,
-        String      description,
-        ComputeType type,
-        String      stateId,
-        String      hash
-    ) {
-        super(index, name, description, type, stateId, hash);
-    }
-
-    public WSPLGENLayerFacet(
-        int         index,
-        String      name,
-        String      description,
-        ComputeType type,
-        String      stateId,
-        String      hash,
-        String      url
-    ) {
-        super(index, name, description, type, stateId, hash, url);
-    }
-
-
-    @Override
-    public boolean isQueryable() {
-        return true;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/map/WSPLGENReportFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-package de.intevation.flys.artifacts.model.map;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.artifactdatabase.state.Facet;
-
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.ReportFacet;
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-
-/**
- * This facet is used to provide WSPLGEN reports <b>only</b>.
- */
-public class WSPLGENReportFacet extends ReportFacet {
-
-    private static Logger logger = Logger.getLogger(WSPLGENReportFacet.class);
-
-
-    protected CalculationResult result;
-
-
-    public WSPLGENReportFacet() {
-    }
-
-
-    public WSPLGENReportFacet(
-        ComputeType       type,
-        String            hash,
-        String            stateId,
-        CalculationResult result
-    ) {
-        super(type, hash, stateId);
-        this.result = result;
-    }
-
-
-    @Override
-    public Object getData(Artifact artifact, CallContext context) {
-        return result.getReport();
-    }
-
-
-    @Override
-    public Facet deepCopy() {
-        WSPLGENReportFacet copy = new WSPLGENReportFacet();
-        copy.set(this);
-        copy.type    = type;
-        copy.hash    = hash;
-        copy.stateId = stateId;
-        copy.result  = result;
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedDensityFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,60 +0,0 @@
-package de.intevation.flys.artifacts.model.minfo;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.DataFacet;
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-
-/**
- * Facet for serving bed density data.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class BedDensityFacet extends DataFacet {
-
-    private static final long serialVersionUID = 1L;
-
-    private static Logger logger = Logger.getLogger(BedDensityFacet.class);
-
-    public BedDensityFacet() {
-    }
-
-    public BedDensityFacet(int idx, String name, String description,
-        ComputeType type, String stateId, String hash) {
-        super(idx, name, description, type, hash, stateId);
-    }
-
-    public Object getData(Artifact artifact, CallContext context) {
-        logger.debug("Get data for bed density at index: " + index);
-
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-
-        CalculationResult res = (CalculationResult) flys.compute(context, hash,
-            stateId, type, false);
-
-        int ndx = index >> 8;
-        Object[] data =
-            ((BedQualityResult[]) res.getData())[ndx].getParameters(); // TODO CAST TO SPECIFIC CLASS
-
-        int ndy = index & 255;
-        return data != null && data.length > ndy ? data[ndy] : null;
-    }
-
-    /** Copy deeply. */
-    @Override
-    public Facet deepCopy() {
-        BedDensityFacet copy = new BedDensityFacet();
-        copy.set(this);
-        copy.type = type;
-        copy.hash = hash;
-        copy.stateId = stateId;
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedDiameterFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-package de.intevation.flys.artifacts.model.minfo;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.DataFacet;
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-
-/**
- * Facet for serving bed diameter data.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class BedDiameterFacet extends DataFacet {
-
-    private static final long serialVersionUID = 1L;
-
-    private static Logger logger = Logger.getLogger(BedDiameterFacet.class);
-
-    public BedDiameterFacet() {
-    }
-
-    public BedDiameterFacet(int idx, String name, String description,
-        ComputeType type, String stateId, String hash) {
-        super(idx, name, description, type, hash, stateId);
-    }
-
-    public Object getData(Artifact artifact, CallContext context) {
-        logger.debug("Get data for bed diameter at index: " + index);
-
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-
-        CalculationResult res = (CalculationResult) flys.compute(context, hash,
-            stateId, type, false);
-
-        int ndx = index >> 8;
-        Object[] data = ((BedQualityResult[]) res.getData())[ndx].getBedResults(); // TODO CAST TO SPECIFIC CLASS
-
-        int ndy = index & 255;
-        return data != null && data.length > ndy ? data[ndy] : null;
-    }
-
-    /** Copy deeply. */
-    @Override
-    public Facet deepCopy() {
-        BedDiameterFacet copy = new BedDiameterFacet();
-        copy.set(this);
-        copy.type = type;
-        copy.hash = hash;
-        copy.stateId = stateId;
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedDiameterResult.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-package de.intevation.flys.artifacts.model.minfo;
-
-import gnu.trove.TDoubleArrayList;
-
-
-public class BedDiameterResult
-extends BedQualityDiameterResult
-{
-    protected TDoubleArrayList diameterCap;
-    protected TDoubleArrayList diameterSub;
-
-    public BedDiameterResult (
-        String type,
-        TDoubleArrayList diameterCap,
-        TDoubleArrayList diameterSub,
-        TDoubleArrayList km
-    ) {
-        super(type, km);
-        this.diameterCap = diameterCap;
-        this.diameterSub = diameterSub;
-    }
-
-    public double getDiameterCap(int ndx) {
-        if (diameterCap != null) {
-            return this.diameterCap.get(ndx);
-        }
-        return Double.NaN;
-    }
-
-    public double getDiameterSub(int ndx) {
-        if (diameterSub != null) {
-            return this.diameterSub.get(ndx);
-        }
-        return Double.NaN;
-    }
-
-    public double getDiameterCap(double km) {
-        if (kms.indexOf(km) >= 0) {
-            return diameterCap.get(kms.indexOf(km));
-        }
-        return Double.NaN;
-    }
-
-    public double getDiameterSub(double km) {
-        if (kms.indexOf(km) >= 0) {
-            return diameterSub.get(kms.indexOf(km));
-        }
-        return Double.NaN;
-    }
-
-    public double[][] getDiameterCapData() {
-        return new double[][] {
-            kms.toNativeArray(),
-            diameterCap.toNativeArray()
-        };
-    }
-
-    public double[][] getDiameterSubData() {
-        return new double[][] {
-            kms.toNativeArray(),
-            diameterSub.toNativeArray()
-        };
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedDiffCalculation.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,187 +0,0 @@
-package de.intevation.flys.artifacts.model.minfo;
-
-import gnu.trove.TDoubleArrayList;
-
-import java.util.Date;
-import java.util.LinkedList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.access.BedDifferencesAccess;
-import de.intevation.flys.artifacts.model.Calculation;
-import de.intevation.flys.artifacts.model.CalculationResult;
-
-
-public class BedDiffCalculation
-extends Calculation
-{
-
-    private static final Logger logger = Logger
-        .getLogger(BedDiffCalculation.class);
-
-    protected String river;
-    protected String yearEpoch;
-    protected FLYSArtifact[][] artifacts;
-
-    public BedDiffCalculation() {
-    }
-
-    public CalculationResult calculate(BedDifferencesAccess access) {
-        logger.info("BedDiffCalculation.calculate");
-
-        String river = access.getRiver();
-        String yearEpoch = access.getYearEpoch();
-        FLYSArtifact[][] artifacts = access.getDifferenceArtifacts();
-
-        logger.debug("got artifacts: " + artifacts.length + "; " + artifacts[0].length);
-        if (river == null) {
-            // TODO: i18n
-            addProblem("minfo.missing.river");
-        }
-
-        if (yearEpoch == null) {
-            addProblem("minfo.missing.year_epoch");
-        }
-
-        if (artifacts == null) {
-            addProblem("minfo.missing.differences");
-        }
-
-        if (!hasProblems()) {
-            this.river = river;
-            this.yearEpoch = yearEpoch;
-            this.artifacts = artifacts;
-            return internalCalculate();
-        }
-
-        return new CalculationResult();
-    }
-
-    private CalculationResult internalCalculate() {
-
-        if (yearEpoch.equals("year")) {
-            List<BedDiffYearResult> results =
-                new LinkedList<BedDiffYearResult>();
-
-            for (int i = 0; i < artifacts.length; i++) {
-                BedHeight[] pair =
-                    getHeightPair(artifacts[i][0], artifacts[i][1], "single");
-                BedDiffYearResult res = calculateYearDifference(pair);
-                results.add(res);
-            }
-            return new CalculationResult(
-                results.toArray(new BedDiffYearResult[results.size()]), this);
-        }
-        if (yearEpoch.equals("epoch")) {
-            List<BedDiffEpochResult> results =
-                new LinkedList<BedDiffEpochResult>();
-            for (int i = 0; i < artifacts.length; i++) {
-                BedHeight[] pair =
-                    getHeightPair(artifacts[i][0], artifacts[i][1], "epoch");
-                BedDiffEpochResult res = calculateEpochDifference(pair);
-                results.add(res);
-            }
-            return new CalculationResult(
-                results.toArray(new BedDiffEpochResult[results.size()]), this);
-        }
-
-       return new CalculationResult();
-    }
-
-    private BedHeight[] getHeightPair(
-        FLYSArtifact art1,
-        FLYSArtifact art2,
-        String type
-    ) {
-        int id1 = BedDifferencesAccess.getHeightId(art1);
-        int id2 = BedDifferencesAccess.getHeightId(art2);
-
-        BedHeight[] heights = new BedHeight[2];
-        heights[0] = BedHeightFactory.getHeight(type, id1, 0);
-        heights[1] = BedHeightFactory.getHeight(type, id2, 0);
-        return heights;
-    }
-
-    private BedDiffEpochResult calculateEpochDifference(BedHeight[] pair) {
-
-        TDoubleArrayList stations = pair[0].getStations();
-        TDoubleArrayList diffRes = new TDoubleArrayList();
-        TDoubleArrayList kms = new TDoubleArrayList();
-        TDoubleArrayList heights1 = new TDoubleArrayList();
-        TDoubleArrayList heights2 = new TDoubleArrayList();
-
-        for (int i = 0; i < stations.size(); i++) {
-            if (!Double.isNaN(pair[0].getHeight(stations.get(i))) &&
-                !Double.isNaN(pair[1].getHeight(stations.get(i)))) {
-                double hDiff =
-                    pair[0].getHeight(stations.get(i)) -
-                    pair[1].getHeight(stations.get(i));
-                diffRes.add(hDiff);
-                kms.add(stations.get(i));
-                heights1.add(pair[0].getHeight(stations.get(i)));
-                heights2.add(pair[1].getHeight(stations.get(i)));
-            }
-        }
-        Date start = ((BedHeightEpoch)pair[0]).getStart();
-        Date end = ((BedHeightEpoch)pair[1]).getEnd();
-        return new BedDiffEpochResult(kms, diffRes, heights1, heights2, start, end);
-    }
-
-    private BedDiffYearResult calculateYearDifference(BedHeight[] pair) {
-
-        TDoubleArrayList stations = pair[0].getStations();
-        TDoubleArrayList diffRes = new TDoubleArrayList();
-        TDoubleArrayList kms = new TDoubleArrayList();
-        TDoubleArrayList morphs = new TDoubleArrayList();
-        TDoubleArrayList absolute = new TDoubleArrayList();
-        TDoubleArrayList gap = new TDoubleArrayList();
-        TDoubleArrayList heights1 = new TDoubleArrayList();
-        TDoubleArrayList heights2 = new TDoubleArrayList();
-
-        BedHeightSingle s1 = (BedHeightSingle)pair[0];
-        BedHeightSingle s2 = (BedHeightSingle)pair[1];
-        int range = s1.getYear() - s2.getYear();
-        if (range  < 0) {
-            range = range * -1;
-        }
-        for (int i = 0; i < stations.size(); i++) {
-            if (!Double.isNaN(s1.getHeight(stations.get(i))) &&
-                !Double.isNaN(s2.getHeight(stations.get(i)))) {
-                double hDiff =
-                    s1.getHeight(stations.get(i)) -
-                    s2.getHeight(stations.get(i));
-                diffRes.add(hDiff);
-                double km = stations.get(i);
-                kms.add(km);
-                if (s1.getMorphWidth(km) >
-                    s2.getMorphWidth(km)) {
-                    morphs.add(s1.getMorphWidth(km));
-                }
-                else {
-                    morphs.add(s2.getMorphWidth(km));
-                }
-                if (s1.getDataGap(km) > s2.getDataGap(km)) {
-                    gap.add(s1.getDataGap(km));
-                }
-                else {
-                    gap.add(s2.getDataGap(km));
-                }
-                absolute.add((hDiff / range) * 100);
-                heights1.add(s1.getHeight(km));
-                heights2.add(s2.getHeight(km));
-            }
-        }
-        return new BedDiffYearResult(
-            kms,
-            diffRes,
-            heights1,
-            heights2,
-            morphs,
-            absolute,
-            gap,
-            s1.getYear(),
-            s2.getYear());
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedDiffEpochFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,52 +0,0 @@
-package de.intevation.flys.artifacts.model.minfo;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.DataFacet;
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-
-public class BedDiffEpochFacet
-extends DataFacet
-{
-    private static Logger logger = Logger.getLogger(BedDensityFacet.class);
-
-    public BedDiffEpochFacet() {
-    }
-
-    public BedDiffEpochFacet(int idx, String name, String description,
-        ComputeType type, String stateId, String hash) {
-        super(idx, name, description, type, hash, stateId);
-    }
-
-    public Object getData(Artifact artifact, CallContext context) {
-        logger.debug("Get data for bed density at index: " + index);
-
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-
-        CalculationResult res = (CalculationResult) flys.compute(context, hash,
-            stateId, type, false);
-
-        BedDiffEpochResult[] data =
-            (BedDiffEpochResult[]) res.getData();
-
-        return data != null && data.length > index ? data[index] : null;
-    }
-
-    /** Copy deeply. */
-    @Override
-    public Facet deepCopy() {
-        BedDiffEpochFacet copy = new BedDiffEpochFacet();
-        copy.set(this);
-        copy.type = type;
-        copy.hash = hash;
-        copy.stateId = stateId;
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedDiffEpochFilterFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,80 +0,0 @@
-package de.intevation.flys.artifacts.model.minfo;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.access.RiverAccess;
-import de.intevation.flys.artifacts.context.FLYSContext;
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.DataFacet;
-import de.intevation.flys.artifacts.math.MovingAverage;
-import de.intevation.flys.artifacts.model.ZoomScale;
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-
-public class BedDiffEpochFilterFacet
-extends DataFacet
-{
-    private static Logger logger = Logger.getLogger(BedDensityFacet.class);
-
-    public BedDiffEpochFilterFacet() {
-    }
-
-    public BedDiffEpochFilterFacet(int idx, String name, String description,
-        ComputeType type, String stateId, String hash) {
-        super(idx, name, description, type, hash, stateId);
-    }
-
-    public Object getData(Artifact artifact, CallContext context) {
-        logger.debug("Get data for bed density at index: " + index);
-
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-
-        CalculationResult res = (CalculationResult) flys.compute(context, hash,
-            stateId, type, false);
-
-        BedDiffEpochResult[] data =
-            (BedDiffEpochResult[]) res.getData(); // TODO CAST TO SPECIFIC CLASS
-        Double start = (Double)context.getContextValue("startkm");
-        Double end = (Double)context.getContextValue("endkm");
-
-        if(start != null && end != null) {
-            FLYSContext fc = (FLYSContext)context.globalContext();
-            ZoomScale scales = (ZoomScale)fc.get("zoomscale");
-            RiverAccess access = new RiverAccess((FLYSArtifact)artifact);
-            String river = access.getRiver();
-
-            double radius = scales.getRadius(river, start, end);
-            BedDiffEpochResult oldData = data[index];
-            BedDiffEpochResult newData = new BedDiffEpochResult();
-            newData.setStart(oldData.getStart());
-            newData.setEnd(oldData.getEnd());
-            double[][] diffs = MovingAverage.weighted(oldData.getDifferencesData(), radius);
-            double[][] heights1 = MovingAverage.weighted(oldData.getHeights1Data(), radius);
-            double[][] heights2 = MovingAverage.weighted(oldData.getHeights2Data(), radius);
-            for(int j = 0; j < diffs[0].length; j++) {
-                newData.addKm(diffs[0][j]);
-                newData.addDifference(diffs[1][j]);
-                newData.addHeight1(heights1[1][j]);
-                newData.addHeight2(heights2[1][j]);
-            }
-            return newData;
-        }
-        return data != null && data.length > index ? data[index] : null;
-    }
-
-    /** Copy deeply. */
-    @Override
-    public Facet deepCopy() {
-        BedDiffEpochFilterFacet copy = new BedDiffEpochFilterFacet();
-        copy.set(this);
-        copy.type = type;
-        copy.hash = hash;
-        copy.stateId = stateId;
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedDiffEpochResult.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-package de.intevation.flys.artifacts.model.minfo;
-
-import java.util.Date;
-
-import gnu.trove.TDoubleArrayList;
-
-
-public class BedDiffEpochResult
-extends BedDifferencesResult
-{
-
-    protected Date start;
-    protected Date end;
-
-    public BedDiffEpochResult () {
-        super();
-    }
-
-    public BedDiffEpochResult (
-        TDoubleArrayList kms,
-        TDoubleArrayList differences,
-        TDoubleArrayList heights1,
-        TDoubleArrayList heights2,
-        Date start,
-        Date end
-    ) {
-        super(kms, differences, heights1, heights2);
-        this.start = start;
-        this.end = end;
-    }
-
-    public Date getStart() {
-        return this.start;
-    }
-
-    public void setStart(Date value) {
-        this.start = value;
-    }
-
-    public void setEnd(Date value) {
-        this.end = value;
-    }
-
-    public Date getEnd() {
-        return this.end;
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedDiffYearFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,52 +0,0 @@
-package de.intevation.flys.artifacts.model.minfo;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.DataFacet;
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-
-public class BedDiffYearFacet
-extends DataFacet
-{
-    private static Logger logger = Logger.getLogger(BedDiffYearFacet.class);
-
-    public BedDiffYearFacet() {
-    }
-
-    public BedDiffYearFacet(int idx, String name, String description,
-        ComputeType type, String stateId, String hash) {
-        super(idx, name, description, type, hash, stateId);
-    }
-
-    public Object getData(Artifact artifact, CallContext context) {
-        logger.debug("Get data for bed density at index: " + index);
-
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-
-        CalculationResult res = (CalculationResult) flys.compute(context, hash,
-            stateId, type, false);
-
-        BedDiffYearResult[] data =
-            (BedDiffYearResult[]) res.getData(); // TODO CAST TO SPECIFIC CLASS
-
-        return data != null && data.length > index ? data[index] : null;
-    }
-
-    /** Copy deeply. */
-    @Override
-    public Facet deepCopy() {
-        BedDiffYearFacet copy = new BedDiffYearFacet();
-        copy.set(this);
-        copy.type = type;
-        copy.hash = hash;
-        copy.stateId = stateId;
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedDiffYearFilterFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,83 +0,0 @@
-package de.intevation.flys.artifacts.model.minfo;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.access.RiverAccess;
-import de.intevation.flys.artifacts.context.FLYSContext;
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.DataFacet;
-import de.intevation.flys.artifacts.math.MovingAverage;
-import de.intevation.flys.artifacts.model.ZoomScale;
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-
-public class BedDiffYearFilterFacet
-extends DataFacet
-{
-    private static Logger logger = Logger.getLogger(BedDiffYearFilterFacet.class);
-
-    public BedDiffYearFilterFacet() {
-    }
-
-    public BedDiffYearFilterFacet(int idx, String name, String description,
-        ComputeType type, String stateId, String hash) {
-        super(idx, name, description, type, hash, stateId);
-    }
-
-    public Object getData(Artifact artifact, CallContext context) {
-        logger.debug("Get data for bed density at index: " + index);
-
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-
-        CalculationResult res = (CalculationResult) flys.compute(context, hash,
-            stateId, type, false);
-
-        BedDiffYearResult[] data =
-            (BedDiffYearResult[]) res.getData(); // TODO CAST TO SPECIFIC CLASS
-        Double start = (Double)context.getContextValue("startkm");
-        Double end = (Double)context.getContextValue("endkm");
-        if(start != null && end != null) {
-            FLYSContext fc = (FLYSContext)context.globalContext();
-            ZoomScale scales = (ZoomScale)fc.get("zoomscale");
-            RiverAccess access = new RiverAccess((FLYSArtifact)artifact);
-            String river = access.getRiver();
-
-            double radius = scales.getRadius(river, start, end);
-            BedDiffYearResult oldData = data[index];
-            BedDiffYearResult newData = new BedDiffYearResult();
-            newData.setStart(oldData.getStart());
-            newData.setEnd(oldData.getEnd());
-            double[][] diffs = MovingAverage.weighted(oldData.getDifferencesData(), radius);
-            double[][] heights1 = MovingAverage.weighted(oldData.getHeights1Data(), radius);
-            double[][] heights2 = MovingAverage.weighted(oldData.getHeights2Data(), radius);
-            double[][] morph = oldData.getMorphWidthData();
-            double[][] year = MovingAverage.weighted(oldData.getHeightPerYearData(), radius);
-            for(int j = 0; j < diffs[0].length; j++) {
-                newData.addKm(diffs[0][j]);
-                newData.addBedHeights(year[1][j]);
-                newData.addMorphWidth(morph[1][j]);
-                newData.addDifference(diffs[1][j]);
-                newData.addHeight1(heights1[1][j]);
-                newData.addHeight2(heights2[1][j]);
-            }
-            return newData;
-        }
-        return data != null && data.length > index ? data[index] : null;
-    }
-
-    /** Copy deeply. */
-    @Override
-    public Facet deepCopy() {
-        BedDiffYearFilterFacet copy = new BedDiffYearFilterFacet();
-        copy.set(this);
-        copy.type = type;
-        copy.hash = hash;
-        copy.stateId = stateId;
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedDiffYearResult.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,93 +0,0 @@
-package de.intevation.flys.artifacts.model.minfo;
-
-import gnu.trove.TDoubleArrayList;
-
-
-public class BedDiffYearResult
-extends BedDifferencesResult
-{
-
-    protected TDoubleArrayList bedHeights;
-    protected TDoubleArrayList dataGap;
-    protected TDoubleArrayList morphWidth;
-    protected int start;
-    protected int end;
-
-    public BedDiffYearResult () {
-        super();
-        this.bedHeights = new TDoubleArrayList();
-        this.dataGap = new TDoubleArrayList();
-        this.morphWidth = new TDoubleArrayList();
-        this.start = -1;
-        this.end = -1;
-    }
-
-    public BedDiffYearResult(
-        TDoubleArrayList kms,
-        TDoubleArrayList differences,
-        TDoubleArrayList heights1,
-        TDoubleArrayList heights2,
-        TDoubleArrayList morphWidth,
-        TDoubleArrayList bedHeights,
-        TDoubleArrayList dataGap,
-        int start,
-        int end
-    ) {
-        super(kms, differences, heights1, heights2);
-        this.bedHeights = bedHeights;
-        this.dataGap = dataGap;
-        this.morphWidth = morphWidth;
-        this.start = start;
-        this.end = end;
-    }
-
-    public TDoubleArrayList getBedHeights() {
-        return this.bedHeights;
-    }
-
-    public TDoubleArrayList getDataGap() {
-        return this.dataGap;
-    }
-
-    public TDoubleArrayList getMorphWidth() {
-        return this.morphWidth;
-    }
-
-    public int getStart() {
-        return this.start;
-    }
-
-    public void setStart(int value) {
-        this.start = value;
-    }
-
-    public void setEnd(int value) {
-        this.end = value;
-    }
-
-    public int getEnd() {
-        return this.end;
-    }
-
-    public void addMorphWidth(double value) {
-        this.morphWidth.add(value);
-    }
-
-    public void addBedHeights(double value) {
-        this.bedHeights.add(value);
-    }
-
-    public double[][] getMorphWidthData() {
-        return new double[][] {
-            kms.toNativeArray(),
-            morphWidth.toNativeArray()
-        };
-    }
-
-    public double[][] getHeightPerYearData() {
-        return new double[][] {
-            kms.toNativeArray(),
-            bedHeights.toNativeArray()
-        };
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedDifferencesResult.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,88 +0,0 @@
-package de.intevation.flys.artifacts.model.minfo;
-
-import gnu.trove.TDoubleArrayList;
-
-import java.io.Serializable;
-
-
-public class BedDifferencesResult
-implements Serializable
-{
-
-    protected TDoubleArrayList kms;
-    protected TDoubleArrayList differences;
-    protected TDoubleArrayList height1;
-    protected TDoubleArrayList height2;
-
-    public BedDifferencesResult () {
-        kms = new TDoubleArrayList();
-        differences = new TDoubleArrayList();
-        height1 = new TDoubleArrayList();
-        height2 = new TDoubleArrayList();
-    }
-
-    public BedDifferencesResult(
-        TDoubleArrayList kms,
-        TDoubleArrayList differences,
-        TDoubleArrayList heights1,
-        TDoubleArrayList heights2
-    ) {
-        this.kms = kms;
-        this.differences = differences;
-        this.height1 = heights1;
-        this.height2 = heights2;
-    }
-
-    public TDoubleArrayList getKms() {
-        return this.kms;
-    }
-
-    public TDoubleArrayList getDifferences() {
-        return this.differences;
-    }
-
-    public void addKm(double value) {
-        this.kms.add(value);
-    }
-
-    public void addDifference(double value) {
-        this.differences.add(value);
-    }
-
-    public void addHeight1(double value) {
-        this.height1.add(value);
-    }
-
-    public void addHeight2(double value) {
-        this.height2.add(value);
-    }
-
-    public double[][] getDifferencesData() {
-        return new double[][] {
-            kms.toNativeArray(),
-            differences.toNativeArray()
-        };
-    }
-
-    public TDoubleArrayList getHeights1() {
-        return this.height1;
-    }
-
-    public TDoubleArrayList getHeights2() {
-        return this.height2;
-    }
-
-    public double[][] getHeights1Data() {
-        return new double[][] {
-            kms.toNativeArray(),
-            height1.toNativeArray()
-        };
-    }
-
-    public double[][] getHeights2Data() {
-        return new double[][] {
-            kms.toNativeArray(),
-            height2.toNativeArray()
-        };
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedHeight.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,109 +0,0 @@
-package de.intevation.flys.artifacts.model.minfo;
-
-import org.apache.log4j.Logger;
-
-import gnu.trove.TDoubleArrayList;
-import de.intevation.flys.artifacts.model.NamedObjectImpl;
-
-public class BedHeight
-extends NamedObjectImpl
-{
-    private static Logger log = Logger.getLogger(BedHeight.class);
-
-    protected TDoubleArrayList heights;
-    protected TDoubleArrayList station;
-
-    public BedHeight() {
-        heights = new TDoubleArrayList();
-        station = new TDoubleArrayList();
-    }
-
-    public BedHeight(String name) {
-        super(name);
-        heights = new TDoubleArrayList();
-        station = new TDoubleArrayList();
-    }
-
-    public BedHeight(int capacity) {
-        this(capacity, "");
-    }
-
-    public BedHeight(int capacity, String name) {
-        super(name);
-        heights = new TDoubleArrayList(capacity);
-        station = new TDoubleArrayList(capacity);
-    }
-
-    public void add(double value, double station) {
-        this.heights.add(value);
-        this.station.add(station);
-    }
-
-    public int size() {
-        return heights.size();
-    }
-
-    public double getHeight(int idx) {
-        return heights.getQuick(idx);
-    }
-
-    public double [] getHeights() {
-        return heights.toNativeArray();
-    }
-
-    public double [] get(int idx) {
-        return get(idx, new double [3]);
-    }
-
-    public double [] get(int idx, double [] dst) {
-        dst[0] = heights.getQuick(idx);
-        dst[1] = station.getQuick(idx);
-        return dst;
-    }
-
-   public double minHeights() {
-        return heights.min();
-    }
-
-    public TDoubleArrayList getStations() {
-        return this.station;
-    }
-
-    public double getHeight(double station) {
-        if (this.station.indexOf(station) >= 0) {
-            return this.heights.get(this.station.indexOf(station));
-        }
-        return Double.NaN;
-    }
-
-
-    public static void removeNaNs(TDoubleArrayList [] arrays) {
-
-        int dest = 0;
-
-        int A = arrays.length;
-        int N = arrays[0].size();
-
-        OUTER: for (int i = 0; i < N; ++i) {
-            for (int j = 0; j < A; ++j) {
-                TDoubleArrayList a = arrays[j];
-                double v = a.getQuick(i);
-                if (Double.isNaN(v)) {
-                    continue OUTER;
-                }
-                a.setQuick(dest, v);
-            }
-            ++dest;
-        }
-
-        if (dest < N) {
-            for (int i = 0; i < A; ++i) {
-                arrays[i].remove(dest, N-dest);
-            }
-        }
-    }
-
-    public void removeNaNs() {
-        removeNaNs(new TDoubleArrayList [] { heights });
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedHeightEpoch.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-package de.intevation.flys.artifacts.model.minfo;
-
-import java.util.Date;
-
-
-public class BedHeightEpoch
-extends BedHeight
-{
-
-    protected Date start;
-    protected Date end;
-
-    public BedHeightEpoch() {
-        this.start = new Date();
-        this.end = new Date();
-    }
-
-    public BedHeightEpoch(String name) {
-        super(name);
-        this.start = new Date();
-        this.end = new Date();
-    }
-
-    public void add(
-        double value,
-        double station,
-        Date start,
-        Date end
-    ) {
-        super.add(value, station);
-        this.start = start;
-        this.end = end;
-    }
-
-    public Date getStart() {
-        return start;
-    }
-
-    public Date getEnd() {
-        return end;
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedHeightFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-package de.intevation.flys.artifacts.model.minfo;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.access.BedHeightAccess;
-import de.intevation.flys.artifacts.model.BlackboardDataFacet;
-import de.intevation.flys.artifacts.model.FacetTypes;
-
-public class BedHeightFacet
-extends      BlackboardDataFacet
-implements   FacetTypes {
-
-    private String type;
-
-    public BedHeightFacet(String name, String description, String type) {
-        this.name = name;
-        this.description = description;
-        this.type = type;
-        this.index = 0;
-    }
-
-    /**
-     * Returns the data this facet requires.
-     *
-     * @param artifact the owner artifact.
-     * @param context  the CallContext (ignored).
-     *
-     * @return the data.
-     */
-    @Override
-    public Object getData(Artifact artifact, CallContext context) {
-        BedHeightAccess access = new BedHeightAccess((FLYSArtifact)artifact, context);
-        if (type.equals("singlevalues")) {
-            return access.getSingleValues();
-        }
-        return access.getHeight();
-    }
-    /**
-     * Create a deep copy of this Facet.
-     * @return a deep copy.
-     */
-    @Override
-    public BedHeightFacet deepCopy() {
-        BedHeightFacet copy = new BedHeightFacet(name, description, type);
-        copy.set(this);
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedHeightFactory.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,178 +0,0 @@
-package de.intevation.flys.artifacts.model.minfo;
-
-
-import java.util.Date;
-import java.util.List;
-
-import net.sf.ehcache.Cache;
-import net.sf.ehcache.Element;
-
-import org.apache.log4j.Logger;
-import org.hibernate.SQLQuery;
-import org.hibernate.Session;
-import org.hibernate.type.StandardBasicTypes;
-
-import de.intevation.flys.artifacts.cache.CacheFactory;
-import de.intevation.flys.artifacts.model.StaticBedHeightCacheKey;
-import de.intevation.flys.backend.SessionHolder;
-
-public class BedHeightFactory {
-    /** Private logger to use here. */
-    private static Logger log = Logger.getLogger(BedHeightFactory.class);
-
-    /** Query to get km and ws for wst_id and column_pos. */
-    public static final String SQL_SELECT_SINGLE =
-        "SELECT bhsv.height, bhsv.station, bhsv.data_gap, bhsv.sounding_width, bhs.year " +
-        "   FROM bed_height_single bhs" +
-        "       JOIN bed_height_single_values bhsv on bhsv.bed_height_single_id = bhs.id" +
-        "   WHERE bhs.id = :height_id";
-
-    /** Query to get name for wst_id and column_pos. */
-    public static final String SQL_SELECT_EPOCH =
-        "SELECT bv.height, bv.station, ti.start_time, ti.stop_time" +
-        "   FROM bed_height_epoch b" +
-        "       JOIN bed_height_epoch_values bv ON b.id = bv.bed_height_epoch_id" +
-        "       JOIN time_intervals ti ON b.time_interval_id = ti.id" +
-        "   WHERE b.id = :height_id";
-
-    /** Query to get name (description) for wst_id. */
-    public static final String SQL_SELECT_DESCR_SINGLE =
-        "SELECT description FROM bed_height_single "+
-        "WHERE id = :height_id";
-
-    /** Query to get name (description) for wst_id. */
-    public static final String SQL_SELECT_DESCR_EPOCH =
-        "SELECT description from bed_height_epoch "+
-        "WHERE id = :height_id";
-
-
-    private BedHeightFactory() {
-    }
-
-
-    /**
-     * Get WKms for given column and wst_id, caring about the cache.
-     */
-    public static BedHeight getHeight(String type, int height_id, int time) {
-        log.debug("BedHeightFactory.getHeight");
-        Cache cache = CacheFactory.getCache(StaticBedHeightCacheKey.CACHE_NAME);
-
-        StaticBedHeightCacheKey cacheKey;
-
-        if (cache != null) {
-            cacheKey = new StaticBedHeightCacheKey(height_id, time);
-            Element element = cache.get(cacheKey);
-            if (element != null) {
-                log.debug("Got static bedheight values from cache");
-                return (BedHeight)element.getValue();
-            }
-        }
-        else {
-            cacheKey = null;
-        }
-
-        BedHeight values = getBedHeightUncached(type, height_id, time);
-
-        if (values != null && cacheKey != null) {
-            log.debug("Store static bed height values in cache.");
-            Element element = new Element(cacheKey, values);
-            cache.put(element);
-        }
-        return values;
-    }
-
-    /** Get name for a WKms. */
-    public static String getHeightName(String type, int height_id) {
-        log.debug("BedHeightFactory.getHeightName height_id/" + height_id);
-
-        String name = null;
-        Session session = SessionHolder.HOLDER.get();
-
-        SQLQuery nameQuery = null;
-        if (type.equals("single")) {
-            nameQuery = session.createSQLQuery(SQL_SELECT_DESCR_SINGLE)
-                .addScalar("description", StandardBasicTypes.STRING);
-            nameQuery.setInteger("height_id", height_id);
-        }
-        else if (type.equals("epoch")) {
-            nameQuery = session.createSQLQuery(SQL_SELECT_DESCR_EPOCH)
-                .addScalar("description", StandardBasicTypes.STRING);
-            nameQuery.setInteger("height_id", height_id);
-        }
-        else {
-            return "none";
-        }
-        List<String> names = nameQuery.list();
-        if (!names.isEmpty()) {
-            name = names.get(0);
-        }
-
-        return name;
-    }
-
-
-    /**
-     * Get WKms from db.
-     * @param column the position columns value
-     * @param wst_id database id of the wst
-     * @return according WKms.
-     */
-    public static BedHeight getBedHeightUncached(
-        String type,
-        int height_id,
-        int time)
-    {
-        if (log.isDebugEnabled()) {
-            log.debug("BedHeightFactory.getBedHeightUncached");
-        }
-
-        Session session = SessionHolder.HOLDER.get();
-        SQLQuery sqlQuery = null;
-        if (type.equals("single")) {
-            BedHeightSingle height =
-                new BedHeightSingle(getHeightName(type, height_id));
-            sqlQuery = session.createSQLQuery(SQL_SELECT_SINGLE)
-                .addScalar("height", StandardBasicTypes.DOUBLE)
-                .addScalar("station", StandardBasicTypes.DOUBLE)
-                .addScalar("data_gap", StandardBasicTypes.DOUBLE)
-                .addScalar("sounding_width", StandardBasicTypes.DOUBLE)
-                .addScalar("year", StandardBasicTypes.INTEGER);
-            sqlQuery.setInteger("height_id", height_id);
-            List<Object []> results = sqlQuery.list();
-
-            for (int i = 0; i < results.size(); i++) {
-                Object[] row = results.get(i);
-                log.debug("got station: " + (Double)row[1]);
-                height.add(
-                    (Double) row[0],
-                    (Double) row[1],
-                    (Double) row[2],
-                    (Double) row[3],
-                    (Integer) row[4]);
-            }
-            return height;
-        }
-        else if (type.equals("epoch")) {
-            BedHeightEpoch height =
-                new BedHeightEpoch(getHeightName(type, height_id));
-            sqlQuery = session.createSQLQuery(SQL_SELECT_EPOCH)
-                .addScalar("height", StandardBasicTypes.DOUBLE)
-                .addScalar("station", StandardBasicTypes.DOUBLE)
-                .addScalar("start_time", StandardBasicTypes.DATE)
-                .addScalar("stop_time", StandardBasicTypes.DATE);
-            sqlQuery.setInteger("height_id", height_id);
-            List<Object []> results = sqlQuery.list();
-
-            for (Object [] row: results) {
-                height.add(
-                    (Double) row[0],
-                    (Double) row[1],
-                    (Date)   row[2],
-                    (Date)   row[3]);
-            }
-            return height;
-        }
-        return new BedHeight();
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedHeightSingle.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,70 +0,0 @@
-package de.intevation.flys.artifacts.model.minfo;
-
-import gnu.trove.TDoubleArrayList;
-
-
-public class BedHeightSingle
-extends BedHeight
-{
-
-    protected int year;
-    protected TDoubleArrayList data_gap;
-    protected TDoubleArrayList morphWidth;
-
-    public BedHeightSingle() {
-        super();
-        this.year = -1;
-        data_gap = new TDoubleArrayList();
-        morphWidth = new TDoubleArrayList();
-    }
-
-    public BedHeightSingle(String name) {
-        super(name);
-        this.year = -1;
-        data_gap = new TDoubleArrayList();
-        morphWidth = new TDoubleArrayList();
-    }
-
-    public void add(
-        double value,
-        double station,
-        double gap,
-        double width,
-        int year
-    ) {
-        super.add(value, station);
-        this.year = year;
-        this.data_gap.add(gap);
-        this.morphWidth.add(width);
-    }
-
-    public int getYear() {
-        return this.year;
-    }
-
-    public double getMorphWidth(int idx) {
-        return this.morphWidth.get(idx);
-    }
-
-    public double getDataGap(int idx) {
-        return this.data_gap.get(idx);
-    }
-
-    public double getMorphWidth(double station) {
-        if (this.station.indexOf(station) >= 0) {
-            return this.morphWidth.get(this.station.indexOf(station));
-        }
-        return Double.NaN;
-    }
-
-    public double getDataGap(double station) {
-        if (this.station.indexOf(station) >= 0) {
-            return this.getDataGap(this.station.indexOf(station));
-        }
-        return Double.NaN;
-    }
-
-    public double[] getMorphWidths() {
-        return this.morphWidth.toNativeArray();
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedOverview.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,200 +0,0 @@
-package de.intevation.flys.artifacts.model.minfo;
-
-import java.io.Serializable;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-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.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-import de.intevation.flys.utils.KMIndex;
-
-public class BedOverview
-implements Serializable
-{
-    /**
-     * Serial version UId.
-     */
-    private static final long serialVersionUID = -7967134407371364911L;
-
-    public interface Filter {
-        boolean accept(KMIndex<List<Date>> entry);
-
-    } // interface Filter
-
-
-    public static final Filter ACCEPT = new Filter() {
-        public boolean accept(KMIndex<List<Date>> entry) {
-            return true;
-        }
-    };
-
-    public static class KmFilter implements Filter {
-
-        protected double km;
-
-        public KmFilter (double km) {
-            this.km = km;
-        }
-        public boolean accept(KMIndex<List<Date>> list) {
-            for (KMIndex.Entry<List<Date>> e: list){
-                if (e.getKm() == km) {
-                    return true;
-                }
-            }
-            return false;
-        }
-    };
-
-    public static class DateFilter implements Filter {
-
-        protected Date date;
-
-        public DateFilter (Date date) {
-            this.date = date;
-        }
-
-        public boolean accept(KMIndex<List<Date>> list) {
-            for (KMIndex.Entry<List<Date>> e: list){
-                if (e.getValue().equals(this.date)) {
-                    return true;
-                }
-            }
-            return false;
-        }
-    };
-
-    private static Logger log = Logger.getLogger(BedOverview.class);
-
-    public static final double EPSILON = 1e-4;
-
-    public static final String DATE_FORMAT = "dd.MM.yyyy";
-
-    public static final String SQL_SQ =
-        "SELECT" +
-        "    so.km    AS km," +
-        "    so.datum AS datum " +
-        "FROM sohltest so " +
-        "    JOIN station s" +
-        "       ON so.stationid = s.stationid " +
-        "    JOIN gewaesser g " +
-        "       ON s.gewaesserid = g.gewaesserid " +
-        "WHERE" +
-        "    g.name = :name AND" +
-        "    so.km IS NOT NULL " +
-        "ORDER by" +
-        "    so.km, so.datum";
-
-    protected String       riverName;
-
-    protected KMIndex<List<Date>> entries;
-
-    public BedOverview() {
-        entries = new KMIndex<List<Date>>();
-    }
-
-    public BedOverview(String riverName) {
-        this();
-        this.riverName = riverName;
-    }
-
-    private static final boolean epsilonEquals(double a, double b) {
-        return Math.abs(a - b) < EPSILON;
-    }
-
-    protected void loadData(Session session) {
-        SQLQuery query = session.createSQLQuery(SQL_SQ)
-            .addScalar("km",    StandardBasicTypes.DOUBLE)
-            .addScalar("datum", StandardBasicTypes.DATE);
-
-        query.setString("name", riverName);
-
-        List<Object []> list = query.list();
-
-        if (list.isEmpty()) {
-            log.warn("No river '" + riverName + "' found.");
-        }
-
-        Double prevKm = -Double.MAX_VALUE;
-        List<Date> dates = new ArrayList<Date>();
-
-        for (Object [] row: list) {
-            Double km = (Double)row[0];
-            if (!epsilonEquals(km, prevKm) && !dates.isEmpty()) {
-                entries.add(prevKm, dates);
-                dates = new ArrayList<Date>();
-            }
-            dates.add((Date)row[1]);
-            prevKm = km;
-        }
-
-        if (!dates.isEmpty()) {
-            entries.add(prevKm, dates);
-        }
-    }
-
-    public boolean load(Session session) {
-
-        loadData(session);
-
-        return true;
-    }
-
-
-    public void generateOverview(Document document) {
-        generateOverview(document, ACCEPT);
-    }
-
-    public KMIndex<List<Date>> filter(Filter f) {
-        // TODO: Apply filter
-        return entries;
-    }
-
-    public void generateOverview(
-        Document document,
-        Filter   filter
-    ) {
-        KMIndex<List<Date>> filtered = filter(ACCEPT);
-
-        Element sqElement = document.createElement("bed");
-
-        Element riverElement = document.createElement("river");
-
-        riverElement.setAttribute("name", riverName);
-
-        sqElement.appendChild(riverElement);
-
-        SimpleDateFormat df = new SimpleDateFormat(DATE_FORMAT);
-
-        Element kmE = document.createElement("km");
-
-        for (KMIndex.Entry<List<Date>> e: filtered) {
-
-            List<Date> dates = e.getValue();
-
-            if (!dates.isEmpty()) {
-                Element dEs = document.createElement("dates");
-
-                for (Date d: dates) {
-                    Element dE = document.createElement("date");
-
-                    dE.setAttribute("value", df.format(d));
-
-                    dEs.appendChild(dE);
-                }
-
-                kmE.appendChild(dEs);
-            }
-        }
-
-        sqElement.appendChild(kmE);
-
-        document.appendChild(sqElement);
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedOverviewFactory.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,70 +0,0 @@
-package de.intevation.flys.artifacts.model.minfo;
-
-import net.sf.ehcache.Cache;
-import net.sf.ehcache.Element;
-
-import org.apache.log4j.Logger;
-import org.hibernate.Session;
-
-import de.intevation.flys.artifacts.cache.CacheFactory;
-import de.intevation.flys.backend.SedDBSessionHolder;
-
-public class BedOverviewFactory {
-
-    private static Logger log = Logger.getLogger(BedOverviewFactory.class);
-
-    public static final String CACHE_NAME = "sq-overviews";
-
-    private BedOverviewFactory() {
-    }
-
-
-    public static BedOverview getOverview(String river) {
-
-        boolean debug = log.isDebugEnabled();
-
-        if (debug) {
-            log.debug(
-                "Looking for bed overview for river '" + river + "'");
-        }
-
-        Cache cache = CacheFactory.getCache(CACHE_NAME);
-
-        if (cache == null) {
-            if (debug) {
-                log.debug("Cache not configured.");
-            }
-            return getUncached(river);
-        }
-
-        String key = "bed-over-" + river;
-
-        Element element = cache.get(key);
-
-        if (element != null) {
-            if (debug) {
-                log.debug("Overview found in cache");
-            }
-            return (BedOverview)element.getValue();
-        }
-
-        BedOverview overview = getUncached(river);
-
-        if (overview != null) {
-            if (debug) {
-                log.debug("Store overview in cache.");
-            }
-            cache.put(new Element(key, overview));
-        }
-
-        return overview;
-    }
-
-    public static BedOverview getUncached(String river) {
-        BedOverview overview = new BedOverview(river);
-
-        Session session = SedDBSessionHolder.HOLDER.get();
-
-        return overview.load(session) ? overview : null;
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedParametersResult.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,106 +0,0 @@
-package de.intevation.flys.artifacts.model.minfo;
-
-import gnu.trove.TDoubleArrayList;
-
-import java.io.Serializable;
-
-
-public class BedParametersResult
-implements Serializable
-{
-    protected TDoubleArrayList porosityCap;
-    protected TDoubleArrayList porositySub;
-    protected TDoubleArrayList loadDensityCap;
-    protected TDoubleArrayList loadDensitySub;
-    protected TDoubleArrayList kms;
-
-    public BedParametersResult() {
-
-    }
-
-    public BedParametersResult(
-        TDoubleArrayList kms,
-        TDoubleArrayList porosityCap,
-        TDoubleArrayList porositySub,
-        TDoubleArrayList densityCap,
-        TDoubleArrayList densitySub
-    ) {
-        this.kms = kms;
-        this.porosityCap = porosityCap;
-        this.porositySub = porositySub;
-        this.loadDensityCap = densityCap;
-        this.loadDensitySub = densitySub;
-    }
-
-    public double getPorosityCap(int ndx) {
-        return porosityCap.get(ndx);
-    }
-
-    public double getPorositySub(int ndx) {
-        return porositySub.get(ndx);
-    }
-
-    public double getLoadDensityCap(int ndx) {
-        return loadDensityCap.get(ndx);
-    }
-
-    public double getLoadDensitySub(int ndx) {
-        return loadDensitySub.get(ndx);
-    }
-
-    public double getPorosityCap(double km) {
-        if (kms.indexOf(km) >= 0) {
-            return porosityCap.get(kms.indexOf(km));
-        }
-        return Double.NaN;
-    }
-
-    public double getPorositySub(double km) {
-        if (kms.indexOf(km) >= 0) {
-            return porositySub.get(kms.indexOf(km));
-        }
-        return Double.NaN;
-    }
-
-    public double getLoadDensityCap(double km) {
-        if (kms.indexOf(km) >= 0) {
-            return loadDensityCap.get(kms.indexOf(km));
-        }
-        return Double.NaN;
-    }
-
-    public double getLoadDensitySub(double km) {
-        if (kms.indexOf(km) >= 0) {
-            return loadDensitySub.get(kms.indexOf(km));
-        }
-        return Double.NaN;
-    }
-
-    public double[][] getPorosityCapData() {
-        return new double[][] {
-            kms.toNativeArray(),
-            porosityCap.toNativeArray()
-        };
-    }
-
-    public double[][] getPorositySubData() {
-        return new double[][] {
-            kms.toNativeArray(),
-            porositySub.toNativeArray()
-        };
-    }
-
-    public double[][] getDensityCapData() {
-        return new double[][] {
-            kms.toNativeArray(),
-            loadDensityCap.toNativeArray()
-        };
-    }
-
-    public double[][] getDensitySubData() {
-        return new double[][] {
-            kms.toNativeArray(),
-            loadDensitySub.toNativeArray()
-        };
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedPorosityFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-package de.intevation.flys.artifacts.model.minfo;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.DataFacet;
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-
-/**
- * Facet for serving bed porosity data.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class BedPorosityFacet extends DataFacet {
-
-    private static final long serialVersionUID = 1L;
-
-    private static Logger logger = Logger.getLogger(BedPorosityFacet.class);
-
-    public BedPorosityFacet() {
-    }
-
-    public BedPorosityFacet(int idx, String name, String description,
-        ComputeType type, String stateId, String hash) {
-        super(idx, name, description, type, hash, stateId);
-    }
-
-    public Object getData(Artifact artifact, CallContext context) {
-        logger.debug("Get data for bed porosity at index: " + index);
-
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-
-        CalculationResult res = (CalculationResult) flys.compute(context, hash,
-            stateId, type, false);
-
-        int ndx = index >> 8;
-        Object[] data = ((BedQualityResult[]) res.getData())[ndx].getParameters(); // TODO CAST TO SPECIFIC CLASS
-
-        int ndy = index & 255;
-        return data != null && data.length > ndy ? data[ndy] : null;
-    }
-
-    /** Copy deeply. */
-    @Override
-    public Facet deepCopy() {
-        BedPorosityFacet copy = new BedPorosityFacet();
-        copy.set(this);
-        copy.type = type;
-        copy.hash = hash;
-        copy.stateId = stateId;
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedQualityCalculation.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,332 +0,0 @@
-package de.intevation.flys.artifacts.model.minfo;
-
-import gnu.trove.TDoubleArrayList;
-
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.flys.artifacts.access.BedQualityAccess;
-import de.intevation.flys.artifacts.model.Calculation;
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.DateRange;
-import de.intevation.flys.backend.SedDBSessionHolder;
-
-
-public class BedQualityCalculation extends Calculation {
-
-    private static final Logger logger = Logger
-        .getLogger(BedQualityCalculation.class);
-
-    protected String river;
-    protected double from;
-    protected double to;
-    protected List<String> bedDiameter;
-    protected List<String> bedloadDiameter;
-    protected List<DateRange> ranges;
-
-    public BedQualityCalculation() {
-    }
-
-    public CalculationResult calculate(BedQualityAccess access) {
-        logger.info("BedQualityCalculation.calculate");
-
-        String river = access.getRiver();
-        Double from = access.getFrom();
-        Double to = access.getTo();
-        List<String> bedDiameter = access.getBedDiameter();
-        List<String> bedloadDiameter = access.getBedloadDiameter();
-        List<DateRange> ranges = access.getDateRanges();
-
-        if (river == null) {
-            // TODO: i18n
-            addProblem("minfo.missing.river");
-        }
-
-        if (from == null) {
-            // TODO: i18n
-            addProblem("minfo.missing.from");
-        }
-
-        if (to == null) {
-            // TODO: i18n
-            addProblem("minfo.missing.to");
-        }
-
-        if (ranges == null) {
-            // TODO: i18n
-            addProblem("minfo.missing.periods");
-        }
-
-        if (!hasProblems()) {
-            this.river = river;
-            this.from = from;
-            this.to = to;
-            this.ranges = ranges;
-            this.bedDiameter = bedDiameter;
-            this.bedloadDiameter = bedloadDiameter;
-
-            SedDBSessionHolder.acquire();
-            try {
-                return internalCalculate();
-            }
-            finally {
-                SedDBSessionHolder.release();
-            }
-        }
-
-        return new CalculationResult();
-    }
-
-    protected CalculationResult internalCalculate() {
-
-        List<BedQualityResult> results = new LinkedList<BedQualityResult>();
-        // Calculate for all time periods.
-        for (DateRange dr : ranges) {
-            QualityMeasurements loadMeasurements =
-                QualityMeasurementFactory.getBedloadMeasurements(
-                    river,
-                    from,
-                    to,
-                    dr.getFrom(),
-                    dr.getTo());
-            QualityMeasurements bedMeasurements =
-                QualityMeasurementFactory.getBedMeasurements(
-                    river,
-                    from,
-                    to,
-                    dr.getFrom(),
-                    dr.getTo());
-            BedQualityResult result = new BedQualityResult();
-            result.setDateRange(dr);
-            if (bedDiameter != null) {
-                result.add(calculateBedParameter(bedMeasurements, dr));
-                for (String bd : bedDiameter) {
-                    BedDiameterResult bedResult =
-                        calculateBed(bedMeasurements, bd, dr);
-
-                    // Avoid adding empty result sets.
-                    if (!bedResult.isEmpty()) {
-                        result.add(bedResult);
-                    }
-                }
-            }
-            if (bedloadDiameter != null) {
-                for (String bld : bedloadDiameter) {
-                    BedloadDiameterResult loadResult =
-                        calculateBedload(loadMeasurements, bld, dr);
-                    result.add(loadResult);
-                }
-            }
-            results.add(result);
-        }
-
-        return new CalculationResult(
-            results.toArray(new BedQualityResult[results.size()]), this);
-    }
-
-    private BedParametersResult calculateBedParameter(
-        QualityMeasurements qm,
-        DateRange dr
-    ) {
-        List<Double> kms = qm.getKms();
-        QualityMeasurements capFiltered = filterCapMeasurements(qm);
-        QualityMeasurements subFiltered = filterSubMeasurements(qm);
-        TDoubleArrayList location = new TDoubleArrayList();
-        TDoubleArrayList porosityCap = new TDoubleArrayList();
-        TDoubleArrayList porositySub = new TDoubleArrayList();
-        TDoubleArrayList densityCap = new TDoubleArrayList();
-        TDoubleArrayList densitySub = new TDoubleArrayList();
-
-        for(double km : kms) {
-            double[] pCap = calculatePorosity(capFiltered, km);
-            double[] pSub = calculatePorosity(subFiltered, km);
-            double[] dCap = calculateDensity(capFiltered, pCap);
-            double[] dSub = calculateDensity(subFiltered, pSub);
-
-            double pCapRes = 0d;
-            double pSubRes = 0d;
-            double dCapRes = 0d;
-            double dSubRes = 0d;
-            for (int i = 0; i < pCap.length; i++) {
-                pCapRes += pCap[i];
-                dCapRes += dCap[i];
-            }
-            for (int i = 0; i < pSub.length; i++) {
-                pSubRes += pSub[i];
-                dSubRes += dSub[i];
-            }
-            location.add(km);
-            porosityCap.add((pCapRes / pCap.length) * 100 );
-            porositySub.add((pSubRes / pSub.length) * 100);
-            densityCap.add((dCapRes / dCap.length) / 1000);
-            densitySub.add((dSubRes / dSub.length) / 1000);
-
-        }
-
-        return new BedParametersResult(
-            location,
-            porosityCap,
-            porositySub,
-            densityCap,
-            densitySub);
-    }
-
-    protected BedDiameterResult calculateBed(
-        QualityMeasurements qm,
-        String diameter,
-        DateRange range
-    ) {
-        List<Double> kms = qm.getKms();
-        TDoubleArrayList location = new TDoubleArrayList();
-        TDoubleArrayList avDiameterCap = new TDoubleArrayList();
-        TDoubleArrayList avDiameterSub = new TDoubleArrayList();
-        for (double km : kms) {
-            //Filter cap and sub measurements.
-            QualityMeasurements capFiltered = filterCapMeasurements(qm);
-            QualityMeasurements subFiltered = filterSubMeasurements(qm);
-
-            List<QualityMeasurement> cm = capFiltered.getMeasurements(km);
-            List<QualityMeasurement> sm = subFiltered.getMeasurements(km);
-
-            double avCap = calculateAverage(cm, diameter);
-            double avSub = calculateAverage(sm, diameter);
-            location.add(km);
-            avDiameterCap.add(avCap * 1000);// bring to mm.
-            avDiameterSub.add(avSub * 1000);
-        }
-        return new BedDiameterResult(
-            diameter,
-            avDiameterCap,
-            avDiameterSub,
-            location);
-    }
-
-    private double[] calculateDensity(
-        QualityMeasurements capFiltered,
-        double[] porosity
-    ) {
-        double[] density = new double[porosity.length];
-        for (int i = 0; i < porosity.length; i++) {
-            density[i] = (1 - porosity[i]) * 2650;
-        }
-        return density;
-    }
-
-    private double[] calculatePorosity(
-        QualityMeasurements capFiltered,
-        double km
-    ) {
-        List<QualityMeasurement> list = capFiltered.getMeasurements(km);
-        double[] results = new double[list.size()];
-        int i = 0;
-        for (QualityMeasurement qm : list) {
-            double deviation = calculateDeviation(qm);
-            double p = calculateP(qm);
-            double porosity = 0.353 - 0.068 * deviation + 0.146 * p;
-            results[i] = porosity;
-            i++;
-        }
-
-        return results;
-    }
-
-    protected BedloadDiameterResult calculateBedload(
-        QualityMeasurements qm,
-        String diameter,
-        DateRange range
-    ) {
-        List<Double> kms = qm.getKms();
-        TDoubleArrayList location = new TDoubleArrayList();
-        TDoubleArrayList avDiameter = new TDoubleArrayList();
-        for (double km : kms) {
-            List<QualityMeasurement> measurements = qm.getMeasurements(km);
-            double mid = calculateAverage(measurements, diameter);
-            location.add(km);
-            avDiameter.add(mid);
-        }
-        return new BedloadDiameterResult(
-            diameter,
-            avDiameter,
-            location,
-            range);
-    }
-
-    protected double calculateAverage(
-        List<QualityMeasurement> list,
-        String diameter
-    ) {
-        double av = 0;
-        for (QualityMeasurement qm : list) {
-            av += qm.getDiameter(diameter);
-        }
-        return av/list.size();
-    }
-
-    protected QualityMeasurements filterCapMeasurements(
-        QualityMeasurements qms
-    ) {
-        List<QualityMeasurement> result = new LinkedList<QualityMeasurement>();
-        for (QualityMeasurement qm : qms.getMeasurements()) {
-            if (qm.getDepth1() == 0d && qm.getDepth2() <= 0.3) {
-                result.add(qm);
-            }
-        }
-        return new QualityMeasurements(result);
-    }
-
-    protected QualityMeasurements filterSubMeasurements(
-        QualityMeasurements qms
-    ) {
-        List<QualityMeasurement> result = new LinkedList<QualityMeasurement>();
-        for (QualityMeasurement qm : qms.getMeasurements()) {
-            if (qm.getDepth1() > 0d && qm.getDepth2() <= 0.5) {
-                result.add(qm);
-            }
-        }
-        return new QualityMeasurements(result);
-    }
-
-    public double calculateDeviation(QualityMeasurement qm) {
-        Map<String, Double> dm = qm.getAllDiameter();
-        double phiM = 0;
-        double[] phis = new double[dm.size()];
-        double[] ps = new double[dm.size()];
-        int i = 0;
-        for (String key : dm.keySet()) {
-            double d = dm.get(key);
-            double phi = -Math.log(d)/Math.log(2);
-            phis[i] = phi;
-            double p = calculateWeight(qm, key);
-            ps[i] = p;
-            phiM += phi * p;
-            i++;
-        }
-
-        double sig = 0d;
-        for (i = 0; i < dm.size(); i++) {
-            sig += ps[i] * Math.exp(phis[i] - phiM);
-        }
-        double deviation = Math.sqrt(sig);
-        return deviation;
-    }
-
-    protected double calculateP(QualityMeasurement qm) {
-        return calculateWeight(qm, "dmin");
-    }
-
-    public double calculateWeight(QualityMeasurement qm, String diameter) {
-        Map<String, Double> dm = qm.getAllDiameter();
-        double value = qm.getDiameter(diameter);
-
-        double sum = 0d;
-        for (Double d : dm.values()) {
-            sum =+ d.doubleValue();
-        }
-        double weight = sum/100*value;
-        return weight;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedQualityDiameterResult.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +0,0 @@
-package de.intevation.flys.artifacts.model.minfo;
-
-import gnu.trove.TDoubleArrayList;
-
-import java.io.Serializable;
-
-public class BedQualityDiameterResult implements Serializable {
-
-    public static enum DIAMETER_TYPE {
-        D90,
-        D84,
-        D80,
-        D75,
-        D70,
-        D60,
-        D50,
-        D40,
-        D30,
-        D25,
-        D20,
-        D16,
-        D10,
-        DMIN,
-        DMAX
-    }
-
-    protected DIAMETER_TYPE type;
-    protected TDoubleArrayList kms;
-    protected boolean empty;
-
-    public BedQualityDiameterResult () {
-        empty = true;
-    }
-
-    public BedQualityDiameterResult (
-        String type,
-        TDoubleArrayList km
-    ) {
-        if (km.size() > 0) {
-            empty = false;
-        }
-        this.type = DIAMETER_TYPE.valueOf(type.toUpperCase());
-        this.kms = km;
-    }
-
-    public DIAMETER_TYPE getType() {
-        return this.type;
-    }
-
-    public TDoubleArrayList getKms() {
-        return this.kms;
-    }
-
-    public void setType(DIAMETER_TYPE type) {
-        this.type = type;
-    }
-
-    public boolean isEmpty() {
-        return empty;
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedQualityResult.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +0,0 @@
-package de.intevation.flys.artifacts.model.minfo;
-
-import java.io.Serializable;
-import java.util.LinkedList;
-import java.util.List;
-
-import de.intevation.flys.artifacts.model.DateRange;
-
-public class BedQualityResult
-implements Serializable
-{
-
-    protected List<BedDiameterResult> bedResults;
-    protected List<BedloadDiameterResult> bedloadResults;
-    protected List<BedParametersResult> bedParameters;
-    protected DateRange dateRange;
-
-    public BedQualityResult () {
-        bedResults = new LinkedList<BedDiameterResult>();
-        bedloadResults = new LinkedList<BedloadDiameterResult>();
-        bedParameters = new LinkedList<BedParametersResult>();
-    };
-
-    public BedQualityResult (
-        List<BedDiameterResult> bedResults,
-        List<BedloadDiameterResult> bedloadResults,
-        List<BedParametersResult> bedParameters,
-        DateRange range
-    ) {
-        this.dateRange = range;
-        this.bedResults = bedResults;
-        this.bedloadResults = bedloadResults;
-        this.bedParameters = bedParameters;
-    }
-
-    public BedParametersResult[] getParameters() {
-        return bedParameters.toArray(
-            new BedParametersResult[bedParameters.size()]);
-    }
-
-    public BedDiameterResult[] getBedResults() {
-        return bedResults.toArray(new BedDiameterResult[bedResults.size()]);
-    }
-
-    public BedloadDiameterResult[] getBedloadResults() {
-        return bedloadResults.toArray(
-            new BedloadDiameterResult[bedloadResults.size()]);
-    }
-
-    public void add(BedloadDiameterResult result) {
-        bedloadResults.add(result);
-    }
-
-    public void add(BedDiameterResult result) {
-        bedResults.add(result);
-    }
-
-    public void add(BedParametersResult result) {
-        bedParameters.add(result);
-    }
-
-    public DateRange getDateRange() {
-        return dateRange;
-    }
-
-    public void setDateRange(DateRange range) {
-        this.dateRange = range;
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedloadDiameterFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +0,0 @@
-package de.intevation.flys.artifacts.model.minfo;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.DataFacet;
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-
-/**
- * Facet for serving bedload diameter data.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class BedloadDiameterFacet extends DataFacet {
-
-    private static final long serialVersionUID = 1L;
-
-    private static Logger logger = Logger.getLogger(BedloadDiameterFacet.class);
-
-    public BedloadDiameterFacet() {
-        // required for clone operation deepCopy()
-    }
-
-    public BedloadDiameterFacet(int idx, String name, String description,
-        ComputeType type, String stateId, String hash) {
-        super(idx, name, description, type, hash, stateId);
-    }
-
-    public Object getData(Artifact artifact, CallContext context) {
-        logger.debug("Get data for bedload diameter at index: " + index);
-
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-
-        CalculationResult res = (CalculationResult) flys.compute(context, hash,
-            stateId, type, false);
-
-        int ndx = index >> 8;
-        Object[] data =
-            ((BedQualityResult[]) res.getData())[ndx].getBedloadResults(); // TODO CAST TO SPECIFIC CLASS
-
-        int ndy = index & 255;
-        return data != null && data.length > ndy ? data[ndy] : null;
-    }
-
-    /** Copy deeply. */
-    @Override
-    public Facet deepCopy() {
-        BedloadDiameterFacet copy = new BedloadDiameterFacet();
-        copy.set(this);
-        copy.type = type;
-        copy.hash = hash;
-        copy.stateId = stateId;
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedloadDiameterResult.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-package de.intevation.flys.artifacts.model.minfo;
-
-import de.intevation.flys.artifacts.model.DateRange;
-import gnu.trove.TDoubleArrayList;
-
-
-public class BedloadDiameterResult
-extends BedQualityDiameterResult
-{
-    protected TDoubleArrayList diameter;
-
-    public BedloadDiameterResult(
-        String type,
-        TDoubleArrayList diameter,
-        TDoubleArrayList km,
-        DateRange range
-    ) {
-        super (type, km);
-        this.diameter = diameter;
-    }
-
-    public double getDiameter(int ndx) {
-        if (diameter != null) {
-            return this.diameter.get(ndx);
-        }
-        return Double.NaN;
-    }
-
-    public double getDiameter(double km) {
-        if (kms.indexOf(km) >= 0) {
-            return diameter.get(kms.indexOf(km));
-        }
-        return Double.NaN;
-    }
-
-    public double[][] getDiameterData() {
-        return new double[][] {
-            kms.toNativeArray(),
-            diameter.toNativeArray()
-        };
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedloadOverview.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,199 +0,0 @@
-package de.intevation.flys.artifacts.model.minfo;
-
-import java.io.Serializable;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-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.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-import de.intevation.flys.utils.KMIndex;
-
-public class BedloadOverview implements Serializable {
-
-    private static Logger log = Logger.getLogger(BedloadOverview.class);
-
-    /**
-     * Serial version UId.
-     */
-    private static final long serialVersionUID = -7607668985959407096L;
-
-    public interface Filter {
-        boolean accept(KMIndex<List<Date>> entry);
-
-    } // interface Filter
-
-
-    public static final Filter ACCEPT = new Filter() {
-        public boolean accept(KMIndex<List<Date>> entry) {
-            return true;
-        }
-    };
-
-    public static class KmFilter implements Filter {
-
-        protected double km;
-
-        public KmFilter (double km) {
-            this.km = km;
-        }
-        public boolean accept(KMIndex<List<Date>> list) {
-            for (KMIndex.Entry<List<Date>> e: list){
-                if (e.getKm() == km) {
-                    return true;
-                }
-            }
-            return false;
-        }
-    };
-
-    public static class DateFilter implements Filter {
-
-        protected Date date;
-
-        public DateFilter (Date date) {
-            this.date = date;
-        }
-
-        public boolean accept(KMIndex<List<Date>> list) {
-            for (KMIndex.Entry<List<Date>> e: list){
-                if (e.getValue().equals(this.date)) {
-                    return true;
-                }
-            }
-            return false;
-        }
-    };
-
-    public static final double EPSILON = 1e-4;
-
-    public static final String DATE_FORMAT = "dd.MM.yyyy";
-
-    public static final String SQL_SQ =
-        "SELECT" +
-        "    m.km    AS km," +
-        "    m.datum AS datum " +
-        "FROM messung m " +
-        "    JOIN station s" +
-        "       ON m.stationid = s.stationid " +
-        "    JOIN gewaesser g " +
-        "       ON s.gewaesserid = g.gewaesserid " +
-        "WHERE" +
-        "    g.name = :name AND " +
-        "    m.km IS NOT NULL " +
-        "ORDER by" +
-        "    m.km, m.datum";
-
-    protected String       riverName;
-
-    protected KMIndex<List<Date>> entries;
-
-    public BedloadOverview() {
-        entries = new KMIndex<List<Date>>();
-    }
-
-    public BedloadOverview(String riverName) {
-        this();
-        this.riverName = riverName;
-    }
-
-    private static final boolean epsilonEquals(double a, double b) {
-        return Math.abs(a - b) < EPSILON;
-    }
-
-    protected void loadData(Session session) {
-        SQLQuery query = session.createSQLQuery(SQL_SQ)
-            .addScalar("km",    StandardBasicTypes.DOUBLE)
-            .addScalar("datum", StandardBasicTypes.DATE);
-
-        query.setString("name", riverName);
-
-        List<Object []> list = query.list();
-
-        if (list.isEmpty()) {
-            log.warn("No river '" + riverName + "' found.");
-        }
-
-        Double prevKm = -Double.MAX_VALUE;
-        List<Date> dates = new ArrayList<Date>();
-
-        for (Object [] row: list) {
-            Double km = (Double)row[0];
-            if (!epsilonEquals(km, prevKm) && !dates.isEmpty()) {
-                entries.add(prevKm, dates);
-                dates = new ArrayList<Date>();
-            }
-            dates.add((Date)row[1]);
-            prevKm = km;
-        }
-
-        if (!dates.isEmpty()) {
-            entries.add(prevKm, dates);
-        }
-    }
-
-    public boolean load(Session session) {
-
-        loadData(session);
-
-        return true;
-    }
-
-
-    public void generateOverview(Document document) {
-        generateOverview(document, ACCEPT);
-    }
-
-    public KMIndex<List<Date>> filter(Filter f) {
-        // TODO: Apply filter
-        return entries;
-    }
-
-    public void generateOverview(
-        Document document,
-        Filter   filter
-    ) {
-        KMIndex<List<Date>> filtered = filter(ACCEPT);
-
-        Element sqElement = document.createElement("bedload");
-
-        Element riverElement = document.createElement("river");
-
-        riverElement.setAttribute("name", riverName);
-
-        sqElement.appendChild(riverElement);
-
-        SimpleDateFormat df = new SimpleDateFormat(DATE_FORMAT);
-
-        Element kmE = document.createElement("km");
-
-        for (KMIndex.Entry<List<Date>> e: filtered) {
-
-            List<Date> dates = e.getValue();
-
-            if (!dates.isEmpty()) {
-                Element dEs = document.createElement("dates");
-
-                for (Date d: dates) {
-                    Element dE = document.createElement("date");
-
-                    dE.setAttribute("value", df.format(d));
-
-                    dEs.appendChild(dE);
-                }
-
-                kmE.appendChild(dEs);
-            }
-        }
-
-        sqElement.appendChild(kmE);
-
-        document.appendChild(sqElement);
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/BedloadOverviewFactory.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,70 +0,0 @@
-package de.intevation.flys.artifacts.model.minfo;
-
-import net.sf.ehcache.Cache;
-import net.sf.ehcache.Element;
-
-import org.apache.log4j.Logger;
-import org.hibernate.Session;
-
-import de.intevation.flys.artifacts.cache.CacheFactory;
-import de.intevation.flys.backend.SedDBSessionHolder;
-
-public class BedloadOverviewFactory {
-
-    private static Logger log = Logger.getLogger(BedloadOverviewFactory.class);
-
-    public static final String CACHE_NAME = "sq-overviews";
-
-    private BedloadOverviewFactory() {
-    }
-
-
-    public static BedloadOverview getOverview(String river) {
-
-        boolean debug = log.isDebugEnabled();
-
-        if (debug) {
-            log.debug(
-                "Looking for bedload overview for river '" + river + "'");
-        }
-
-        Cache cache = CacheFactory.getCache(CACHE_NAME);
-
-        if (cache == null) {
-            if (debug) {
-                log.debug("Cache not configured.");
-            }
-            return getUncached(river);
-        }
-
-        String key = "bedload-over-" + river;
-
-        Element element = cache.get(key);
-
-        if (element != null) {
-            if (debug) {
-                log.debug("Overview found in cache");
-            }
-            return (BedloadOverview)element.getValue();
-        }
-
-        BedloadOverview overview = getUncached(river);
-
-        if (overview != null) {
-            if (debug) {
-                log.debug("Store overview in cache.");
-            }
-            cache.put(new Element(key, overview));
-        }
-
-        return overview;
-    }
-
-    public static BedloadOverview getUncached(String river) {
-        BedloadOverview overview = new BedloadOverview(river);
-
-        Session session = SedDBSessionHolder.HOLDER.get();
-
-        return overview.load(session) ? overview : null;
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/FlowVelocityMeasurementFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-package de.intevation.flys.artifacts.model.minfo;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.flys.artifacts.FlowVelocityMeasurementArtifact;
-import de.intevation.flys.artifacts.model.BlackboardDataFacet;
-import de.intevation.flys.artifacts.model.FacetTypes;
-
-
-/** Facet to show measured flow velocity. */
-public class FlowVelocityMeasurementFacet
-extends      BlackboardDataFacet
-implements   FacetTypes {
-
-    public FlowVelocityMeasurementFacet(String description) {
-        this(FLOW_VELOCITY_MEASUREMENT, description);
-    }
-
-
-    public FlowVelocityMeasurementFacet(String name, String description) {
-        this.name = name;
-        this.description = description;
-        this.index = 0;
-    }
-
-
-    /**
-     * Returns the data this facet requires.
-     *
-     * @param artifact the owner artifact.
-     * @param context  the CallContext (ignored).
-     *
-     * @return the data.
-     */
-    @Override
-    public Object getData(Artifact artifact, CallContext context) {
-        FlowVelocityMeasurementArtifact staticData =
-            (FlowVelocityMeasurementArtifact) artifact;
-        return staticData.getFlowVelocityMeasurementValue();
-    }
-
-
-    /**
-     * Create a deep copy of this Facet.
-     * @return a deep copy.
-     */
-    @Override
-    public FlowVelocityMeasurementFacet deepCopy() {
-        FlowVelocityMeasurementFacet copy = new FlowVelocityMeasurementFacet(description);
-        copy.set(this);
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/FlowVelocityMeasurementFactory.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-package de.intevation.flys.artifacts.model.minfo;
-
-import java.util.List;
-
-import org.apache.log4j.Logger;
-import org.hibernate.SQLQuery;
-import org.hibernate.Session;
-
-import de.intevation.flys.model.FlowVelocityMeasurementValue;
-import de.intevation.flys.backend.SessionHolder;
-
-
-public class FlowVelocityMeasurementFactory
-{
-    /** Private logger to use here. */
-    private static Logger log = Logger.getLogger(FlowVelocityMeasurementFactory.class);
-
-    /** Query to get description and start year, given name and a km range. */
-    public static final String SQL_SELECT_ONE =
-        "SELECT station, datetime, w, q, v, description " +
-        "   FROM flow_velocity_measure_values" +
-        "   WHERE id = :id";
-
-
-    private FlowVelocityMeasurementFactory() {
-    }
-
-
-    public static FlowVelocityMeasurementValue.FastFlowVelocityMeasurementValue
-        getFlowVelocityMeasurement(int id)
-    {
-        log.debug("FlowVelocityMeasurementFactory.getFlowVelocityMeasurementValue");
-        Session session = SessionHolder.HOLDER.get();
-        SQLQuery sqlQuery = null;
-        sqlQuery = session.createSQLQuery(SQL_SELECT_ONE);
-        sqlQuery.setParameter("id", id);
-
-        List<Object []> results = sqlQuery.list();
-        if (results.size() > 0) {
-            Object[] row = results.get(0);
-            if (row == null || row.length < 6) {
-                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());
-        }
-        return null;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/MorphologicWidth.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,48 +0,0 @@
-package de.intevation.flys.artifacts.model.minfo;
-
-import java.util.HashMap;
-import java.util.Map;
-
-
-public class MorphologicWidth
-{
-
-    private Map<Double, Double> pairs;
-
-
-    public MorphologicWidth() {
-        pairs = new HashMap<Double, Double>();
-    }
-
-    public void add(double station, double width) {
-        this.pairs.put(station, width);
-    }
-
-    public Map<Double, Double> getAll() {
-        return this.pairs;
-    }
-
-    public double[][] getAsArray() {
-        double [][] array = new double[2][pairs.size()];
-        Double[] kms = pairs.keySet().toArray(new Double[pairs.size()]);
-        Double[] width = pairs.values().toArray(new Double[pairs.size()]);
-        int realIndex = 0;
-        for (int i = 0; i < kms.length; i++) {
-            if (kms[i] == null || width[i] == null) {
-                continue;
-            }
-            array[0][realIndex] = kms[i];
-            array[1][realIndex] = width[i];
-            realIndex++;
-        }
-        return array;
-    }
-
-
-    public Double getWidth(double station) {
-        if (this.pairs.containsKey(station)) {
-            return this.pairs.get(station);
-        }
-        return null;
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/MorphologicWidthFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-package de.intevation.flys.artifacts.model.minfo;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.model.DataFacet;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-
-public class MorphologicWidthFacet
-extends DataFacet
-implements   FacetTypes
-{
-    private static Logger logger = Logger.getLogger(SedimentLoadFacet.class);
-
-
-    public MorphologicWidthFacet() {
-    }
-
-    public MorphologicWidthFacet(String facetName, String description) {
-        super(facetName, description);
-    }
-
-    public MorphologicWidthFacet(int idx, String name, String description,
-        ComputeType type, String stateId, String hash) {
-        super(idx, name, description, type, hash, stateId);
-    }
-
-    public Object getData(Artifact artifact, CallContext context) {
-
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-        String width_id = flys.getDataAsString("width_id");
-
-        MorphologicWidth width =
-            MorphologicWidthFactory.getWidth(Integer.valueOf(width_id));
-
-        return width;
-    }
-
-    /** Copy deeply. */
-    @Override
-    public Facet deepCopy() {
-        MorphologicWidthFacet copy = new MorphologicWidthFacet();
-        copy.set(this);
-        copy.type = type;
-        copy.hash = hash;
-        copy.stateId = stateId;
-        return copy;
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/MorphologicWidthFactory.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,87 +0,0 @@
-package de.intevation.flys.artifacts.model.minfo;
-
-import java.util.List;
-
-import net.sf.ehcache.Cache;
-import net.sf.ehcache.Element;
-
-import org.apache.log4j.Logger;
-import org.hibernate.SQLQuery;
-import org.hibernate.Session;
-import org.hibernate.type.StandardBasicTypes;
-
-import de.intevation.flys.artifacts.cache.CacheFactory;
-import de.intevation.flys.artifacts.model.StaticMorphoWidthCacheKey;
-import de.intevation.flys.backend.SessionHolder;
-
-
-public class MorphologicWidthFactory
-{
-    /** Private logger to use here. */
-    private static Logger log = Logger.getLogger(MorphologicWidthFactory.class);
-
-    public static final String SQL_SELECT =
-        "SELECT mwv.station AS station, mwv.width AS width " +
-        "   FROM morphologic_width mw" +
-        "       JOIN morphologic_width_values mwv on mwv.morphologic_width_id = mw.id" +
-        "   WHERE mw.id = :width_id";
-
-    private MorphologicWidthFactory() {
-    }
-
-
-    /**
-     * Get WKms for given column and wst_id, caring about the cache.
-     */
-    public static MorphologicWidth getWidth(int width_id) {
-        log.debug("MorphologicWidthFactory.getWidth");
-        Cache cache = CacheFactory.getCache(StaticMorphoWidthCacheKey.CACHE_NAME);
-
-        StaticMorphoWidthCacheKey cacheKey;
-
-        if (cache != null) {
-            cacheKey = new StaticMorphoWidthCacheKey(width_id);
-            Element element = cache.get(cacheKey);
-            if (element != null) {
-                log.debug("Got static bedheight values from cache");
-                return (MorphologicWidth)element.getValue();
-            }
-        }
-        else {
-            cacheKey = null;
-        }
-
-        MorphologicWidth values = getWidthUncached(width_id);
-
-        if (values != null && cacheKey != null) {
-            log.debug("Store static morphologic width values in cache.");
-            Element element = new Element(cacheKey, values);
-            cache.put(element);
-        }
-        return values;
-    }
-
-    private static MorphologicWidth getWidthUncached(int width_id) {
-        if (log.isDebugEnabled()) {
-            log.debug("MorphologicWidthFactory.getWidthUncached");
-        }
-
-        Session session = SessionHolder.HOLDER.get();
-        SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT)
-                .addScalar("station", StandardBasicTypes.DOUBLE)
-                .addScalar("width", StandardBasicTypes.DOUBLE);
-        sqlQuery.setInteger("width_id", width_id);
-        List<Object []> results = sqlQuery.list();
-
-        MorphologicWidth widths = new MorphologicWidth();
-        for (int i = 0; i < results.size(); i++) {
-            Object[] row = results.get(i);
-            log.debug("got station: " + (Double)row[0]);
-            widths.add(
-                (Double) row[0],
-                (Double) row[1]);
-        }
-        return widths;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/QualityMeasurement.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,79 +0,0 @@
-package de.intevation.flys.artifacts.model.minfo;
-
-import java.util.Date;
-import java.util.Map;
-
-
-public class QualityMeasurement {
-
-    private double              km;
-    private Date                date;
-    private double              depth1;
-    private double              depth2;
-    private Map<String, Double> charDiameter;
-
-    public QualityMeasurement() {
-
-    }
-
-    public QualityMeasurement(
-        double km,
-        Date date,
-        double depth1,
-        double depth2,
-        Map<String, Double> diameter) {
-        this.setKm(km);
-        this.setDate(date);
-        this.depth1 = depth1;
-        this.depth2 = depth2;
-        this.setDiameter(diameter);
-    }
-
-    public double getKm() {
-        return km;
-    }
-
-    public void setKm(double km) {
-        this.km = km;
-    }
-
-    public Date getDate() {
-        return date;
-    }
-
-    public void setDate(Date date) {
-        this.date = date;
-    }
-
-    public Map<String, Double> getAllDiameter() {
-        return charDiameter;
-    }
-
-    public void setDiameter(Map<String, Double> charDiameter) {
-        this.charDiameter = charDiameter;
-    }
-
-    public double getDiameter(String key) {
-        return charDiameter.get(key);
-    }
-
-    public void setDiameter(String key, double value) {
-        charDiameter.put(key, value);
-    }
-
-    public double getDepth1() {
-        return depth1;
-    }
-
-    public void setDepth1(double depth1) {
-        this.depth1 = depth1;
-    }
-
-    public double getDepth2() {
-        return depth2;
-    }
-
-    public void setDepth2(double depth2) {
-        this.depth2 = depth2;
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/QualityMeasurementFactory.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,204 +0,0 @@
-package de.intevation.flys.artifacts.model.minfo;
-
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.apache.log4j.Logger;
-import org.hibernate.SQLQuery;
-import org.hibernate.Session;
-import org.hibernate.transform.BasicTransformerAdapter;
-import org.hibernate.type.StandardBasicTypes;
-
-import de.intevation.flys.backend.SedDBSessionHolder;
-
-
-public class QualityMeasurementFactory {
-
-    private static Logger logger = Logger.getLogger(QualityMeasurementFactory.class);
-
-    private static final String SQL_BED_MEASUREMENT =
-        "SELECT st.km       as km," +
-        "       st.datum    as datum," +
-        "       sp.tiefevon as depth1," +
-        "       sp.tiefebis as depth2," +
-        "       sa.d10      as d10," +
-        "       sa.d16      as d16," +
-        "       sa.d20      as d20," +
-        "       sa.d25      as d25," +
-        "       sa.d30      as d30," +
-        "       sa.d40      as d40," +
-        "       sa.d50      as d50," +
-        "       sa.d60      as d60," +
-        "       sa.d70      as d70," +
-        "       sa.d75      as d75," +
-        "       sa.d80      as d80," +
-        "       sa.d84      as d84," +
-        "       sa.d90      as d90," +
-        "       sa.dmin     as dmin," +
-        "       sa.dmax     as dmax " +
-        "FROM sohltest st " +
-        "    JOIN station sn ON sn.stationid = st.stationid " +
-        "    JOIN gewaesser gw ON gw.gewaesserid = sn.gewaesserid " +
-        "    JOIN sohlprobe sp ON sp.sohltestid = st.sohltestid " +
-        "    JOIN siebanalyse sa ON sa.sohlprobeid = sp.sohlprobeid " +
-        "WHERE gw.name = :name AND " +
-        "      st.km IS NOT NULL AND " +
-        "      sp.tiefevon IS NOT NULL AND " +
-        "      sp.tiefebis IS NOT NULL AND " + // TODO: Test if char diameter ist null.
-        "      st.km BETWEEN :from - 0.001 AND :to + 0.001 AND " +
-        "      st.datum BETWEEN :start AND :end";
-
-    private static final String SQL_BEDLOAD_MEASUREMENT =
-        "SELECT m.km    as km," +
-        "       m.datum as datum," +
-        "       m.d10   as d10," +
-        "       m.d16   as d16," +
-        "       m.d20   as d20," +
-        "       m.d25   as d25," +
-        "       m.d30   as d30," +
-        "       m.d40   as d40," +
-        "       m.d50   as d50," +
-        "       m.d60   as d60," +
-        "       m.d70   as d70," +
-        "       m.d75   as d75," +
-        "       m.d80   as d80," +
-        "       m.d84   as d84," +
-        "       m.d90   as d90," +
-        "       m.dmin  as dmin," +
-        "       m.dmax  as dmax " +
-        "FROM messung m" +
-        "    JOIN station sn ON sn.stationid = m.stationid" +
-        "    JOIN gewaesser gw ON gw.gewaesserid = sn.gewaesserid " +
-        "WHERE gw.name = :name AND " +
-        "      m.km IS NOT NULL AND " +
-        "      m.d10 IS NOT NULL AND" + //TODO: Add all other char. diameter.
-        "      m.km BETWEEN :from - 0.001 AND :to + 0.001 AND" +
-        "      m.datum BETWEEN :start AND :end";
-
-    public static final class QualityMeasurementResultTransformer
-    extends BasicTransformerAdapter {
-
-        public static QualityMeasurementResultTransformer INSTANCE = new QualityMeasurementResultTransformer();
-
-        public QualityMeasurementResultTransformer() {
-        }
-
-        @Override
-        public Object transformTuple(Object[] tuple, String[] aliases) {
-            Map<String, Double> map = new HashMap<String, Double>();
-            double km = 0;
-            Date d = null;
-            double depth1 = Double.NaN;
-            double depth2 = Double.NaN;
-            for (int i = 0; i < tuple.length; ++i) {
-                if (tuple[i] != null) {
-                    if (aliases[i].equals("km")) {
-                        km = ((Number) tuple[i]).doubleValue();
-                    }
-                    else if (aliases[i].equals("datum")) {
-                        d = (Date) tuple[i];
-                    }
-                    else if (aliases[i].equals("depth1")) {
-                        depth1 = ((Number) tuple[i]).doubleValue();
-                    }
-                    else if (aliases[i].equals("depth2")) {
-                        depth2 = ((Number) tuple[i]).doubleValue();
-                    }
-                    else {
-                        map.put(aliases[i], ((Double) tuple[i])/1000);
-                    }
-                }
-            }
-            return new QualityMeasurement(km, d, depth1, depth2, map);
-        }
-    } // class BasicTransformerAdapter
-
-    private QualityMeasurementFactory() {
-    }
-
-    protected static QualityMeasurements load(
-        Session session,
-        String river,
-        double from,
-        double to,
-        Date start,
-        Date end,
-        String statement
-    ) {
-        SQLQuery query = session.createSQLQuery(statement)
-            .addScalar("km", StandardBasicTypes.DOUBLE)
-            .addScalar("datum", StandardBasicTypes.DATE)
-            .addScalar("d10", StandardBasicTypes.DOUBLE)
-            .addScalar("d16", StandardBasicTypes.DOUBLE)
-            .addScalar("d20", StandardBasicTypes.DOUBLE)
-            .addScalar("d25", StandardBasicTypes.DOUBLE)
-            .addScalar("d30", StandardBasicTypes.DOUBLE)
-            .addScalar("d40", StandardBasicTypes.DOUBLE)
-            .addScalar("d50", StandardBasicTypes.DOUBLE)
-            .addScalar("d60", StandardBasicTypes.DOUBLE)
-            .addScalar("d70", StandardBasicTypes.DOUBLE)
-            .addScalar("d75", StandardBasicTypes.DOUBLE)
-            .addScalar("d80", StandardBasicTypes.DOUBLE)
-            .addScalar("d84", StandardBasicTypes.DOUBLE)
-            .addScalar("d90", StandardBasicTypes.DOUBLE)
-            .addScalar("dmin", StandardBasicTypes.DOUBLE)
-            .addScalar("dmax", StandardBasicTypes.DOUBLE);
-
-        if (statement.equals(SQL_BED_MEASUREMENT)) {
-            query.addScalar("depth1", StandardBasicTypes.DOUBLE);
-            query.addScalar("depth2", StandardBasicTypes.DOUBLE);
-        }
-
-        query.setString("name", river);
-        query.setDouble("from", from);
-        query.setDouble("to", to);
-        query.setDate("start", start);
-        query.setDate("end", end);
-
-        query.setResultTransformer(
-            QualityMeasurementResultTransformer.INSTANCE);
-
-        return new QualityMeasurements(query.list());
-    }
-
-    public static QualityMeasurements getBedMeasurements(
-        String river,
-        double from,
-        double to,
-        Date start,
-        Date end) {
-        Session session = SedDBSessionHolder.HOLDER.get();
-        try {
-            return load(session, river, from, to, start, end,
-            SQL_BED_MEASUREMENT);
-        }
-        finally {
-            //session.close();
-        }
-    }
-
-    public static QualityMeasurements getBedloadMeasurements(
-        String river,
-        double from,
-        double to,
-        Date start,
-        Date end
-    ) {
-        Session session = SedDBSessionHolder.HOLDER.get();
-        try {
-            return load(
-                session,
-                river,
-                from,
-                to,
-                start,
-                end,
-                SQL_BEDLOAD_MEASUREMENT);
-        }
-        finally {
-            //session.close();
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/QualityMeasurements.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-package de.intevation.flys.artifacts.model.minfo;
-
-import java.util.LinkedList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-public class QualityMeasurements {
-    private static Logger logger = Logger.getLogger(QualityMeasurements.class);
-    private List<QualityMeasurement> measurements;
-
-    public QualityMeasurements() {
-    }
-
-    public QualityMeasurements(List<QualityMeasurement> list) {
-        measurements = list;
-    }
-
-    public List<QualityMeasurement> getMeasurements() {
-        return measurements;
-    }
-
-    public List<QualityMeasurement> getMeasurements(double km) {
-        List<QualityMeasurement> res = new LinkedList<QualityMeasurement>();
-        for (QualityMeasurement qm: measurements) {
-            if (qm.getKm() == km) {
-                res.add(qm);
-            }
-        }
-        return res;
-    }
-
-    public List<Double> getKms() {
-        List<Double> result = new LinkedList<Double>();
-        for (QualityMeasurement qm : measurements) {
-            if (result.indexOf(qm.getKm()) < 0) {
-                result.add(qm.getKm());
-            }
-        }
-        return result;
-    }
-
-    public void setMeasurements(List<QualityMeasurement> list) {
-        this.measurements = list;
-    }
-
-    public void addMeasurement(QualityMeasurement qm) {
-        this.measurements.add(qm);
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/SedimentDensity.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,158 +0,0 @@
-package de.intevation.flys.artifacts.model.minfo;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.apache.log4j.Logger;
-
-
-public class SedimentDensity
-{
-    private static final Logger logger = Logger
-        .getLogger(SedimentDensity.class);
-
-    private Map<Integer, List<SedimentDensityValue>> densities;
-    private List<Integer> years;
-
-    public SedimentDensity() {
-        this.densities = new HashMap<Integer, List<SedimentDensityValue>>();
-        this.years = new ArrayList<Integer>();
-    }
-
-    public Map<Integer, List<SedimentDensityValue>> getDensities() {
-        return densities;
-    }
-
-    public void setDensities(Map<Integer, List<SedimentDensityValue>> densities) {
-        this.densities = densities;
-    }
-
-    public void addDensity(double km, double density, int year) {
-        logger.debug("adding " + year);
-        if (this.densities.containsKey(year)) {
-            List<SedimentDensityValue> list = this.densities.get(year);
-            list.add(new SedimentDensityValue(km, density, year));
-        }
-        else {
-            List<SedimentDensityValue> list =
-                new ArrayList<SedimentDensityValue>();
-            list.add(new SedimentDensityValue(km, density, year));
-            densities.put(year, list);
-        }
-        if (!this.years.contains(new Integer(year))) {
-            logger.debug("new year");
-            years.add(new Integer(year));
-        }
-    }
-
-    public List<Integer> getYears() {
-        return years;
-    }
-
-    public void setYears(List<Integer> years) {
-        this.years = years;
-    }
-
-    public double getDensity(double km, int year) {
-        Collections.sort(this.years);
-        if (this.years.size() == 1) {
-            return getDensityAtKm(densities.get(year), km);
-        }
-        else {
-            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) {
-                    return getDensityAtKm(densities.get(y1), km);
-                }
-                else if (i == years.size() -1) {
-                    continue;
-                }
-                else {
-                    return getDensityAtKm(densities.get(y2), km);
-                }
-            }
-        }
-        return 1.8d;
-    }
-
-    private double getDensityAtKm(
-        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;
-            }
-            if (sdv.getKm() > km) {
-                next = sdv;
-                break;
-            }
-            prev = sdv;
-        }
-        if (found) {
-            return prev.getDensity();
-        }
-        else {
-            return spline(prev, next, km);
-        }
-    }
-
-    private double spline(
-        SedimentDensityValue prev,
-        SedimentDensityValue next,
-        double km
-    ) {
-        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;
-    }
-
-    public void cleanUp() {
-        Set<Integer> keys = densities.keySet();
-        for (Integer key : keys) {
-            List<SedimentDensityValue> list = densities.get(key);
-            if (list.size() == 0) {
-                return;
-            }
-            List<SedimentDensityValue> cleaned =
-                new ArrayList<SedimentDensityValue>();
-            double prevkm = list.get(0).getKm();
-            int counter = 0;
-            double sum = 0d;
-            for (SedimentDensityValue value : list) {
-                if (value.getKm() == prevkm) {
-                    sum += value.getDensity();
-                    counter++;
-                }
-                else {
-                    cleaned.add(new SedimentDensityValue(
-                        prevkm,
-                        sum / counter,
-                        value.getYear()));
-                    sum = value.getDensity();
-                    counter = 1;
-                }
-                prevkm = value.getKm();
-            }
-            this.densities.put(key, cleaned);
-        }
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/SedimentDensityFactory.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,92 +0,0 @@
-package de.intevation.flys.artifacts.model.minfo;
-
-import java.util.List;
-
-import net.sf.ehcache.Cache;
-import net.sf.ehcache.Element;
-
-import org.apache.log4j.Logger;
-import org.hibernate.SQLQuery;
-import org.hibernate.Session;
-import org.hibernate.type.StandardBasicTypes;
-
-import de.intevation.flys.artifacts.cache.CacheFactory;
-import de.intevation.flys.backend.SessionHolder;
-
-
-public class SedimentDensityFactory
-{
-    /** Private logger to use here. */
-    private static Logger log = Logger.getLogger(SedimentDensityFactory.class);
-
-    private static final String DENSITY_CACHE_NAME = "sedimentdensity";
-
-    /**Query to get sediment density values and kms. */
-    private static final String SQL_SELECT_DENSITY =
-        "SELECT sdv.station AS km, " +
-        "       sdv.density AS density," +
-        "       sdv.year AS year " +
-        "   FROM sediment_density sd" +
-        "       JOIN rivers r ON sd.river_id = r.id " +
-        "       JOIN sediment_density_values sdv ON sd.id = sdv.sediment_density_id" +
-        "   WHERE r.name = :name";
-
-    private SedimentDensityFactory() {}
-
-    public static SedimentDensity getSedimentDensity(
-        String river,
-        double startKm,
-        double endKm,
-        int year
-    ) {
-        log.debug("getSedimentDensity");
-        Cache cache = CacheFactory.getCache(DENSITY_CACHE_NAME);
-
-        if (cache == null) {
-            log.debug("Cache not configured.");
-            return getSedimentDensityUncached(river, startKm, endKm, year);
-        }
-
-        String key = river + startKm + endKm;
-        Element element = cache.get(key);
-        if (element != null) {
-            log.debug("SedimentDensity found in cache!");
-            return (SedimentDensity)element.getValue();
-        }
-        SedimentDensity value =
-            getSedimentDensityUncached(river, startKm, endKm, year);
-
-        if (value != null && key != null) {
-            log.debug("Store sediment density values in cache.");
-            element = new Element(key, value);
-            cache.put(element);
-        }
-        return value;
-    }
-
-    private static SedimentDensity getSedimentDensityUncached(
-        String river,
-        double startKm,
-        double endKm,
-        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();
-        SedimentDensity density = new SedimentDensity();
-        for (Object[] row : results) {
-            if (row[0] != null && row[1] != null && row[2] != null) {
-                density.addDensity((Double)row[0], (Double)row[1], (Integer)row[2]);
-            }
-        }
-
-        density.cleanUp();
-        return density;
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/SedimentDensityValue.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-package de.intevation.flys.artifacts.model.minfo;
-
-
-public class SedimentDensityValue
-{
-
-    private double km;
-    private double density;
-    private int year;
-
-    public SedimentDensityValue() {
-        this.km = 0d;
-        this.density = 0d;
-        this.year = 0;
-    }
-
-    public SedimentDensityValue(double km, double density, int year) {
-        this.km = km;
-        this.density = density;
-        this.year = year;
-    }
-
-    public double getKm() {
-        return km;
-    }
-
-    public void setKm(double km) {
-        this.km = km;
-    }
-
-    public double getDensity() {
-        return density;
-    }
-
-    public void setDensity(double density) {
-        this.density = density;
-    }
-
-    public int getYear() {
-        return year;
-    }
-
-    public void setYear(int year) {
-        this.year = year;
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/SedimentLoad.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,226 +0,0 @@
-package de.intevation.flys.artifacts.model.minfo;
-
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Set;
-
-import de.intevation.flys.artifacts.model.NamedObjectImpl;
-
-/** Gives access to Fractions (at kms). */
-public class SedimentLoad
-extends NamedObjectImpl
-{
-    protected String description;
-    protected Date start;
-    protected Date end;
-    protected boolean isEpoch;
-
-    protected HashMap<Double, SedimentLoadFraction> kms;
-
-    public SedimentLoad() {
-        kms = new HashMap<Double, SedimentLoadFraction>();
-    }
-
-    public SedimentLoad(
-        String description,
-        Date start,
-        Date end,
-        boolean isEpoch
-    ) {
-        this();
-        this.description = description;
-        this.start = start;
-        this.end = end;
-        this.isEpoch = isEpoch;
-    }
-
-    public String getDescription() {
-        return description;
-    }
-
-    public void setDescription(String description) {
-        this.description = description;
-    }
-
-    public Date getStart() {
-        return start;
-    }
-
-    public void setStart(Date start) {
-        this.start = start;
-    }
-
-    public Date getEnd() {
-        return end;
-    }
-
-    public void setEnd(Date end) {
-        this.end = end;
-    }
-
-    public boolean isEpoch() {
-        return isEpoch;
-    }
-
-    public void setEpoch(boolean isEpoch) {
-        this.isEpoch = isEpoch;
-    }
-
-    public Set<Double> getKms() {
-        return kms.keySet();
-    }
-
-    public void addKm(double km, SedimentLoadFraction fraction) {
-        kms.put(km, fraction);
-    }
-
-    public SedimentLoadFraction getFraction(double km) {
-        if (kms.get(km) == null) {
-            return new SedimentLoadFraction();
-        }
-        return kms.get(km);
-    }
-
-    public void setCoarse(double km, double coarse) {
-        if (kms.containsKey(km)) {
-            kms.get(km).setCoarse(coarse);
-        }
-        else {
-            SedimentLoadFraction f = new SedimentLoadFraction();
-            f.setCoarse(coarse);
-            kms.put(km, f);
-        }
-    }
-
-    public void setFineMiddle(double km, double fine_middle) {
-        if (kms.containsKey(km)) {
-            kms.get(km).setFine_middle(fine_middle);
-        }
-        else {
-            SedimentLoadFraction f = new SedimentLoadFraction();
-            f.setFine_middle(fine_middle);
-            kms.put(km, f);
-        }
-    }
-
-    public void setSand(double km, double sand) {
-        if (kms.containsKey(km)) {
-            kms.get(km).setSand(sand);
-        }
-        else {
-            SedimentLoadFraction f = new SedimentLoadFraction();
-            f.setSand(sand);
-            kms.put(km, f);
-        }
-    }
-
-    public void setSuspSand(double km, double susp_sand) {
-        if (kms.containsKey(km)) {
-            kms.get(km).setSusp_sand(susp_sand);
-        }
-        else {
-            SedimentLoadFraction f = new SedimentLoadFraction();
-            f.setSusp_sand(susp_sand);
-            kms.put(km, f);
-        }
-    }
-
-    public void setSuspSandBed(double km, double susp_sand_bed) {
-        if (kms.containsKey(km)) {
-            kms.get(km).setSusp_sand_bed(susp_sand_bed);
-        }
-        else {
-            SedimentLoadFraction f = new SedimentLoadFraction();
-            f.setSusp_sand_bed(susp_sand_bed);
-            kms.put(km, f);
-        }
-    }
-
-    public void setSuspSediment(double km, double susp_sediment) {
-        if (kms.containsKey(km)) {
-            kms.get(km).setSusp_sediment(susp_sediment);
-        }
-        else {
-            SedimentLoadFraction f = new SedimentLoadFraction();
-            f.setSusp_sediment(susp_sediment);
-            kms.put(km, f);
-        }
-    }
-
-    public void setLoadTotal(double km, double total) {
-        if (kms.containsKey(km)) {
-            kms.get(km).setLoadTotal(total);
-        }
-        else {
-            SedimentLoadFraction f = new SedimentLoadFraction();
-            f.setLoadTotal(total);
-            kms.put(km, f);
-        }
-    }
-
-    public void setTotal(double km, double total) {
-        if (kms.containsKey(km)) {
-            kms.get(km).setTotal(total);
-        }
-        else {
-            SedimentLoadFraction f = new SedimentLoadFraction();
-            f.setTotal(total);
-            kms.put(km, f);
-        }
-    }
-
-    public boolean hasCoarse() {
-        for (SedimentLoadFraction slf : kms.values()) {
-            if (slf.getCoarse() > 0d) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    public boolean hasFineMiddle() {
-        for (SedimentLoadFraction slf : kms.values()) {
-            if (slf.getFine_middle() > 0d) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    public boolean hasSand() {
-        for (SedimentLoadFraction slf : kms.values()) {
-            if (slf.getSand() > 0d) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    public boolean hasSuspSand() {
-        for (SedimentLoadFraction slf : kms.values()) {
-            if (slf.getSusp_sand() > 0d) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    public boolean hasSuspSediment() {
-        for (SedimentLoadFraction slf : kms.values()) {
-            if (slf.getSusp_sediment() > 0d) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    public boolean hasTotalLoad() {
-        for (SedimentLoadFraction slf : kms.values()) {
-            if (slf.getLoadTotal() > 0d) {
-                return true;
-            }
-        }
-        return false;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/SedimentLoadCalculation.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,343 +0,0 @@
-package de.intevation.flys.artifacts.model.minfo;
-
-import gnu.trove.TDoubleArrayList;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.flys.artifacts.access.SedimentLoadAccess;
-import de.intevation.flys.artifacts.model.Calculation;
-import de.intevation.flys.artifacts.model.CalculationResult;
-
-
-/** Calculate sediment load. */
-public class SedimentLoadCalculation
-extends Calculation
-{
-
-    /** Private logger. */
-    private static final Logger logger = Logger
-        .getLogger(SedimentLoadCalculation.class);
-
-    protected String river;
-    protected String yearEpoch;
-    protected double kmUp;
-    protected double kmLow;
-    protected int[] period;
-    protected int[][] epoch;
-    protected String unit;
-
-    public SedimentLoadCalculation() {
-    }
-
-    public CalculationResult calculate(SedimentLoadAccess access) {
-        logger.info("SedimentLoadCalculation.calculate");
-
-        String river = access.getRiver();
-        String yearEpoch = access.getYearEpoch();
-        String unit = access.getUnit();
-        int[] period = null;
-        int[][] epoch = null;
-        double kmUp = access.getUpperKM();
-        double kmLow = access.getLowerKM();
-        if (yearEpoch.equals("year")) {
-            period = access.getPeriod();
-            epoch = null;
-        }
-        else if (yearEpoch.equals("epoch") || yearEpoch.equals("off_epoch")) {
-            epoch = access.getEpochs();
-            period = null;
-        }
-        else {
-            addProblem("minfo.missing.year_epoch");
-        }
-
-        if (river == null) {
-            // TODO: i18n
-            addProblem("minfo.missing.river");
-        }
-
-        if (period == null && epoch == null) {
-            addProblem("minfo.missing.time");
-        }
-
-        if (!hasProblems()) {
-            this.river = river;
-            this.yearEpoch = yearEpoch;
-            this.unit = unit;
-            this.period = period;
-            this.epoch = epoch;
-            this.kmUp = kmUp;
-            this.kmLow = kmLow;
-            return internalCalculate();
-        }
-
-        return new CalculationResult();
-    }
-
-    private CalculationResult internalCalculate() {
-        logger.debug("internalCalulate; mode:" + yearEpoch);
-        if (yearEpoch.equals("year")) {
-            List<SedimentLoadResult> results =
-                new ArrayList<SedimentLoadResult>();
-            for (int i = period[0]; i <= period[1]; i++) {
-                SedimentLoadResult res = calculateYear(i);
-                results.add(res);
-            }
-            return new CalculationResult(
-                results.toArray(new SedimentLoadResult[results.size()]), this);
-        }
-        else if (yearEpoch.equals("epoch")) {
-            List<SedimentLoadResult> results =
-                new ArrayList<SedimentLoadResult>();
-            for (int i = 0; i < epoch.length; i++) {
-                SedimentLoadResult res = calculateEpoch(i);
-                results.add(res);
-            }
-            return new CalculationResult(
-                results.toArray(new SedimentLoadResult[results.size()]), this);
-        }
-        else if (yearEpoch.equals("off_epoch")) {
-            List<SedimentLoadResult> results =
-                new ArrayList<SedimentLoadResult>();
-            for (int i = 0; i < epoch.length; i++) {
-                SedimentLoadResult res = calculateOffEpoch(i);
-                results.add(res);
-            }
-            return new CalculationResult(
-                results.toArray(new SedimentLoadResult[results.size()]), this);
-        }
-        return null;
-    }
-
-    private SedimentLoadResult calculateEpoch(int i) {
-        List<SedimentLoad> epochLoads = new ArrayList<SedimentLoad>();
-        for (int j = epoch[i][0]; j < epoch[i][1]; j++) {
-            epochLoads.add(SedimentLoadFactory.getLoadWithData(
-                this.river,
-                this.yearEpoch,
-                this.kmLow,
-                this.kmUp,
-                j,
-                j));
-        }
-
-        SedimentLoad resLoad = new SedimentLoad();
-        TDoubleArrayList kms = new TDoubleArrayList();
-
-        for (SedimentLoad load : epochLoads) {
-            for (double km : load.getKms()) {
-                if (!kms.contains(km)) {
-                    kms.add(km);
-                }
-            }
-        }
-
-        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());
-                    cSum++;
-                }
-                if (f.getFine_middle() > 0d) {
-                    double fm = resLoad.getFraction(km).getFine_middle();
-                    resLoad.setFineMiddle(km, fm + f.getFine_middle());
-                    fmSum++;
-                }
-                if (f.getSand() > 0d) {
-                    double s = resLoad.getFraction(km).getSand();
-                    resLoad.setSand(km, s + f.getSand());
-                    sSum++;
-                }
-                if (f.getSusp_sand() > 0d) {
-                    double s = resLoad.getFraction(km).getSusp_sand();
-                    resLoad.setSuspSand(km, s + f.getSusp_sand());
-                    ssSum++;
-                }
-                if (f.getSusp_sand_bed() > 0d) {
-                    double s = resLoad.getFraction(km).getSusp_sand_bed();
-                    resLoad.setSuspSandBed(km, s + f.getSusp_sand_bed());
-                    ssbSum++;
-                }
-                if (f.getSusp_sediment() > 0d) {
-                    double s = resLoad.getFraction(km).getSusp_sediment();
-                    resLoad.setSuspSediment(km, s + f.getSusp_sediment());
-                    sseSum++;
-                }
-            }
-            SedimentLoadFraction fr = resLoad.getFraction(km);
-            resLoad.setCoarse(km, fr.getCoarse()/cSum);
-            resLoad.setFineMiddle(km, fr.getFine_middle()/fmSum);
-            resLoad.setSand(km, fr.getSand()/sSum);
-            resLoad.setSuspSand(km, fr.getSusp_sand()/ssSum);
-            resLoad.setSuspSandBed(km, fr.getSusp_sand_bed()/ssbSum);
-            resLoad.setSuspSediment(km, fr.getSusp_sediment()/sseSum);
-        }
-        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);
-        }
-
-        return result;
-    }
-
-    private SedimentLoadResult calculateOffEpoch(int i) {
-        SedimentLoad load = SedimentLoadFactory.getLoadWithData(
-            this.river,
-            this.yearEpoch,
-            kmLow,
-            kmUp,
-            this.epoch[i][0],
-            this.epoch[i][1]);
-        SedimentLoadResult result;
-        SedimentLoad sl = calculateTotalLoad(load, this.epoch[i][0]);
-        if (unit.equals("m3_per_a")) {
-            SedimentLoad slu = calculateUnit(sl, 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);
-        }
-
-        return result;
-    }
-
-    /** Fetch loads for a single year, calculate total and
-     * return the result containing both. */
-    private SedimentLoadResult calculateYear(int y) {
-        SedimentLoad load = SedimentLoadFactory.getLoadWithData(
-            this.river,
-            this.yearEpoch,
-            this.kmLow,
-            this.kmUp,
-            y,
-            y);
-
-        SedimentLoadResult result;
-        SedimentLoad sl = calculateTotalLoad(load, y);
-        if (unit.equals("m3_per_a")) {
-            SedimentLoad slu = calculateUnit(sl, y);
-            result = new SedimentLoadResult(y, 0, slu);
-        }
-        else {
-            result = new SedimentLoadResult(y, 0, sl);
-        }
-        return result;
-    }
-
-    private SedimentLoad calculateTotalLoad(SedimentLoad load, int year) {
-        logger.debug("calculateTotalLoad");
-        boolean problemThisYear = false;
-        if (!load.hasCoarse()) {
-            addProblem("missing.fraction.coarse", Integer.toString(year));
-            problemThisYear = true;
-        }
-        if (!load.hasFineMiddle()) {
-            addProblem("missing.fraction.fine_middle", Integer.toString(year));
-            problemThisYear = true;
-        }
-        if (!load.hasSand()) {
-            addProblem("missing.fraction.sand", Integer.toString(year));
-            problemThisYear = true;
-        }
-        if (!load.hasSuspSand()) {
-            addProblem("missing.fraction.susp_sand", Integer.toString(year));
-            problemThisYear = true;
-        }
-        if (!load.hasSuspSediment()) {
-            addProblem("missing.fraction.susp_sediment", Integer.toString(year));
-            problemThisYear = true;
-        }
-        if (problemThisYear) {
-            logger.warn("Some problem, not calculating total load.");
-            return load;
-        }
-        for(double km : load.getKms()) {
-            SedimentLoadFraction fraction = load.getFraction(km);
-            double total = 0d;
-            if ((fraction.getCoarse() <= 0d && load.hasCoarse())){
-                addProblem(km, "missing.data.coarse");
-                continue;
-            }
-            if (fraction.getFine_middle() <= 0d && load.hasFineMiddle()) {
-                addProblem(km, "missing.data.fine_middle");
-                continue;
-            }
-            if (fraction.getSand() <= 0d && load.hasSand()) {
-                addProblem(km, "missing data.sand");
-                continue;
-            }
-            if (fraction.getSusp_sand() <= 0d && load.hasSuspSand()) {
-                addProblem(km, "missing.data.susp_sand");
-                continue;
-            }
-            if (fraction.getSusp_sediment() <= 0d && load.hasSuspSediment()) {
-                addProblem(km, "missing.data.susp_sediment");
-                continue;
-            }
-            total += fraction.getCoarse() +
-                fraction.getFine_middle() +
-                fraction.getSand() +
-                fraction.getSusp_sand() +
-                fraction.getSusp_sediment();
-            load.setTotal(km, total);
-        }
-        return load;
-    }
-
-    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);
-            double coarse = fraction.getCoarse();
-            double fineMiddle = fraction.getFine_middle();
-            double sand = fraction.getSand();
-            double suspSand = fraction.getSusp_sand();
-            double bedSand = fraction.getSusp_sand_bed();
-            double sediment = fraction.getSusp_sediment();
-            double total = fraction.getTotal();
-            load.setCoarse(km, (coarse * dens));
-            load.setFineMiddle(km, (fineMiddle * dens));
-            load.setSand(km, (sand * dens));
-            load.setSuspSand(km, (suspSand * dens));
-            load.setSuspSandBed(km, (bedSand * dens));
-            load.setSuspSediment(km, (sediment * dens));
-            load.setTotal(km, (total * dens));
-        }
-        return load;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/SedimentLoadFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,178 +0,0 @@
-package de.intevation.flys.artifacts.model.minfo;
-
-import de.intevation.artifactdatabase.state.Facet;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.DataFacet;
-import de.intevation.flys.artifacts.model.FacetTypes;
-
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-import de.intevation.flys.model.MeasurementStation;
-
-import de.intevation.flys.utils.FLYSUtils;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.TreeMap;
-
-import org.apache.log4j.Logger;
-
-
-/** Facet to access various sediment loads. */
-public class SedimentLoadFacet
-extends DataFacet
-{
-    /** Very own logger. */
-    private static Logger logger = Logger.getLogger(SedimentLoadFacet.class);
-
-    /** Used as tolerance value when fetching measurement stations. */
-    private static double EPSILON = 1e-5;
-
-
-    public SedimentLoadFacet() {
-    }
-
-    public SedimentLoadFacet(int idx, String name, String description,
-        ComputeType type, String stateId, String hash) {
-        super(idx, name, description, type, hash, stateId);
-    }
-
-    public Object getData(Artifact artifact, CallContext context) {
-        logger.debug("Get data for sediment load at index: " + index);
-
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-
-        CalculationResult res = (CalculationResult) flys.compute(context, hash,
-            stateId, type, false);
-
-        Object[] data =
-            (SedimentLoadResult[]) res.getData(); // TODO CAST TO SPECIFIC CLASS
-
-        List<MeasurementStation> allStations = FLYSUtils.getRiver(flys).getMeasurementStations();
-        SedimentLoadResult result = data != null && data.length > index ? (SedimentLoadResult)data[index] : null;
-        if (result == null) {
-            return null;
-        }
-
-        List<Double> sortedStarts = new ArrayList<Double>();
-        // Filter stations according to type.
-        List<MeasurementStation> stations = new ArrayList<MeasurementStation>();
-        for (MeasurementStation station: allStations) {
-            if (station.getRange() == null || station.getMeasurementType() == null) {
-                continue;
-            }
-            if (FacetTypes.IS.SEDIMENT_LOAD_NO_FLOAT(this.getName())
-                && station.getMeasurementType().equals("Geschiebe")) {
-                stations.add(station);
-                sortedStarts.add(station.getStation());
-            }
-            else if (!FacetTypes.IS.SEDIMENT_LOAD_NO_FLOAT(this.getName())
-                && station.getMeasurementType().equals("Schwebstoff")) {
-                stations.add(station);
-                sortedStarts.add(station.getStation());
-            }
-        }
-        Collections.sort(sortedStarts);
-
-        // Access data according to type.
-        double[][] sd = getLoadData(result);
-
-        // Sort by km.
-        TreeMap<Double, Double> sortData = new TreeMap<Double,Double>();
-
-        double[] km   = sd[0];
-        double[] load = sd[1];
-
-        for (int i = 0 ; i < km.length; i++) {
-            sortData.put(km[i], load[i]);
-        }
-
-        double[][] values = new double[2][];
-        values[0] = new double[km.length*3];
-        values[1] = new double[km.length*3];
-
-        List<double[]> kmWithoutStation = new ArrayList<double[]>();
-
-        // Find station via its station (km).
-        // TODO use a binarySearch instead of linear absdiff approach
-        int i = 0;
-        for (Map.Entry<Double, Double> entry: sortData.entrySet()) {
-            boolean matchFound = false;
-            // For now, ignore overlaps like (B> next A)
-            for (MeasurementStation station: stations) {
-                if (Math.abs(station.getStation() - entry.getKey()) < EPSILON ||
-                    station.getRange().containsTolerant(entry.getKey())) {
-                    // TODO: In rare cases, two matches can be found.
-                    values[0][i*3] = station.getRange().getA().doubleValue() + EPSILON;
-                    values[1][i*3] = entry.getValue();
-                    values[0][i*3+1] = station.getRange().getB().doubleValue() - EPSILON;
-                    values[1][i*3+1] = entry.getValue();
-                    values[0][i*3+2] = station.getRange().getB().doubleValue();
-                    values[1][i*3+2] = entry.getValue();
-                    matchFound = true;
-                }
-            }
-            // Store points without match for later assessment.
-            if (!matchFound) {
-                logger.warn("measurement without station ("+entry.getKey()+")!");
-            }
-            i++;
-        }
-
-        for (int x = 0; x < values[0].length-1; x++) {
-            // Introduce gaps where no data in measurement station.
-            if (Math.abs(values[0][x+1] - values[0][x]) > 3*EPSILON
-                    && values[1][x+1] != values[1][x]) {
-                values[0][x] = Double.NaN;
-                values[1][x] = Double.NaN;
-            }
-        }
-
-        return values;
-    }
-
-
-    /** Get data according to type of facet. */
-    private double[][] getLoadData(SedimentLoadResult result) {
-        if (getName().equals(FacetTypes.SEDIMENT_LOAD_SAND))
-            return result.getSandData();
-        else if (getName().equals(FacetTypes.SEDIMENT_LOAD_COARSE))
-            return result.getCoarseData();
-        else if (getName().equals(FacetTypes.SEDIMENT_LOAD_FINEMIDDLE))
-            return result.getFineMiddleData();
-        else if (getName().equals(FacetTypes.SEDIMENT_LOAD_SUSP_SAND))
-            return result.getSuspSandData();
-        else if (getName().equals(FacetTypes.SEDIMENT_LOAD_SUSP_SAND_BED))
-            return result.getSuspSandBedData();
-        else if (getName().equals(FacetTypes.SEDIMENT_LOAD_SUSP_SEDIMENT))
-            return result.getSuspSedimentData();
-        else if (getName().equals(FacetTypes.SEDIMENT_LOAD_TOTAL_LOAD))
-            return result.getTotalLoadData();
-        else if (getName().equals(FacetTypes.SEDIMENT_LOAD_TOTAL))
-            return result.getTotalData();
-        else {
-            logger.error("SedimentLoadFacet " + getName() + " cannot determine data type.");
-            return null;
-        }
-    }
-
-    /** Copy deeply. */
-    @Override
-    public Facet deepCopy() {
-        SedimentLoadFacet copy = new SedimentLoadFacet();
-        copy.set(this);
-        copy.type = type;
-        copy.hash = hash;
-        copy.stateId = stateId;
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/SedimentLoadFactory.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,383 +0,0 @@
-package de.intevation.flys.artifacts.model.minfo;
-
-import gnu.trove.TDoubleArrayList;
-
-import java.util.Calendar;
-import java.util.Date;
-import java.util.List;
-
-import net.sf.ehcache.Cache;
-import net.sf.ehcache.Element;
-
-import org.apache.log4j.Logger;
-import org.hibernate.SQLQuery;
-import org.hibernate.Session;
-import org.hibernate.type.StandardBasicTypes;
-
-import de.intevation.flys.artifacts.cache.CacheFactory;
-import de.intevation.flys.artifacts.model.StaticSedimentLoadCacheKey;
-import de.intevation.flys.backend.SessionHolder;
-
-/** Pull Sediment Loads out of db. */
-public class SedimentLoadFactory
-{
-    /** Private logger to use here. */
-    private static Logger log = Logger.getLogger(SedimentLoadFactory.class);
-
-    public static final String LOADS_CACHE_NAME = "sedimentloads";
-    public static final String LOAD_DATA_CACHE_NAME = "sedimentload-data";
-
-    /** Query to get km and ws for wst_id and column_pos. */
-    public static final String SQL_SELECT_SINGLES =
-        "SELECT DISTINCT " +
-        "       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 = :name " +
-        "       AND ti.stop_time IS NULL " +
-        "       AND syv.station BETWEEN :startKm AND :endKm";
-
-    /** Query to get name for wst_id and column_pos. */
-    public static final String SQL_SELECT_EPOCHS =
-        "SELECT DISTINCT " +
-        "       sy.description AS description, " +
-        "       ti.start_time AS start, " +
-        "       ti.stop_time AS end " +
-        "   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 = :name " +
-        "       AND ti.stop_time IS NOT NULL " +
-        "       AND syv.station BETWEEN :startKm AND :endKm";
-
-    public static final String SQL_SELECT_SINGLES_DATA =
-        "SELECT" +
-        "       sy.description AS description, " +
-        "       ti.start_time AS year, " +
-        "       syv.value AS load, " +
-        "       syv.station AS km " +
-        "   FROM     sediment_yield sy " +
-        "       JOIN rivers r ON sy.river_id = r.id " +
-        "       JOIN time_intervals ti ON sy.time_interval_id = ti.id " +
-        "       JOIN sediment_yield_values syv ON sy.id = syv.sediment_yield_id " +
-        "       JOIN grain_fraction gf ON sy.grain_fraction_id = gf.id " +
-        "   WHERE   r.name = :name " +
-        "       AND ti.start_time BETWEEN :begin AND :end " +
-        "       AND ti.stop_time IS NULL " +
-        "       AND gf.name = :grain " +
-        "       AND syv.station BETWEEN :startKm AND :endKm";
-
-    public static final String SQL_SELECT_EPOCHS_DATA =
-        "SELECT" +
-        "       sy.description AS description, " +
-        "       ti.start_time AS startYear, " +
-        "       syv.value AS load, " +
-        "       syv.station AS km," +
-        "       ti.stop_time AS endYear " +
-        "   FROM     sediment_yield sy " +
-        "       JOIN rivers r ON sy.river_id = r.id " +
-        "       JOIN time_intervals ti ON sy.time_interval_id = ti.id " +
-        "       JOIN sediment_yield_values syv ON sy.id = syv.sediment_yield_id " +
-        "       JOIN grain_fraction gf ON sy.grain_fraction_id = gf.id " +
-        "   WHERE   r.name = :name " +
-        "       AND ti.start_time BETWEEN :sbegin AND :send " +
-        "       AND ti.stop_time BETWEEN :ebegin AND :eend " +
-        "       AND gf.name = :grain " +
-        "       AND syv.station BETWEEN :startKm AND :endKm";
-
-    private SedimentLoadFactory() {
-    }
-
-    /**
-     *
-     */
-    public static SedimentLoad[] getLoads(
-        String river,
-        String type,
-        double startKm,
-        double endKm
-    ) {
-        log.debug("SedimentLoadFactory.getLoads");
-        Cache cache = CacheFactory.getCache(LOADS_CACHE_NAME);
-
-        if (cache == null) {
-            log.debug("Cache not configured.");
-            return getSedimentLoadsUncached(river, type, startKm, endKm);
-        }
-
-        StaticSedimentLoadCacheKey key =
-            new StaticSedimentLoadCacheKey(river, startKm, endKm, 0, 0);
-
-        Element element = cache.get(key);
-
-        if (element != null) {
-            log.debug("SedimentLoad found in cache");
-            return (SedimentLoad[])element.getValue();
-        }
-
-        SedimentLoad[] values =
-            getSedimentLoadsUncached(river, type, startKm, endKm);
-
-        if (values != null && key != null) {
-            log.debug("Store static sediment load values in cache.");
-            element = new Element(key, values);
-            cache.put(element);
-        }
-        return values;
-    }
-
-    public static SedimentLoad getLoadWithData(
-        String river,
-        String type,
-        double startKm,
-        double endKm,
-        int syear,
-        int eyear
-    ) {
-        log.debug("SedimentLoadFactory.getLoadWithData");
-        Cache cache = CacheFactory.getCache(LOAD_DATA_CACHE_NAME);
-
-        if (cache == null) {
-            log.debug("Cache not configured.");
-            return getSedimentLoadWithDataUncached(
-                river,
-                type,
-                startKm,
-                endKm,
-                syear,
-                eyear);
-        }
-
-        StaticSedimentLoadCacheKey key =
-            new StaticSedimentLoadCacheKey(river, startKm, endKm, syear, eyear);
-
-        Element element = cache.get(key);
-
-        if (element != null) {
-            log.debug("SedimentLoad found in cache");
-            return (SedimentLoad)element.getValue();
-        }
-
-        SedimentLoad values = getSedimentLoadWithDataUncached(
-            river,
-            type,
-            startKm,
-            endKm,
-            syear,
-            eyear);
-
-        if (values != null && key != null) {
-            log.debug("Store static bed height values in cache.");
-            element = new Element(key, values);
-            cache.put(element);
-        }
-        return values;
-    }
-
-    /**
-     * Get sediment loads from db.
-     * @param river the river
-     * @param type the sediment load type (year or epoch)
-     * @return according sediment loads.
-     */
-    public static SedimentLoad[] getSedimentLoadsUncached(
-        String river,
-        String type,
-        double startKm,
-        double endKm
-    ) {
-        log.debug("SedimentLoadFactory.getSedimentLoadsUncached");
-
-        Session session = SessionHolder.HOLDER.get();
-        SQLQuery sqlQuery = null;
-
-        if (type.equals("single")) {
-            sqlQuery = session.createSQLQuery(SQL_SELECT_SINGLES)
-                .addScalar("description", StandardBasicTypes.STRING)
-                .addScalar("year", StandardBasicTypes.DATE);
-            sqlQuery.setString("name", river);
-            sqlQuery.setDouble("startKm", startKm);
-            sqlQuery.setDouble("endKm", endKm);
-            List<Object []> results = sqlQuery.list();
-            SedimentLoad[] loads = new SedimentLoad[results.size()];
-            for (int i = 0; i < results.size(); i++) {
-                Object[] row = results.get(i);
-                loads[i] = new SedimentLoad(
-                    (String) row[0],
-                    (Date) row[1],
-                    null,
-                    false);
-            }
-            return loads;
-        }
-        else if (type.equals("epoch")) {
-            sqlQuery = session.createSQLQuery(SQL_SELECT_EPOCHS)
-                .addScalar("description", StandardBasicTypes.STRING)
-                .addScalar("start", StandardBasicTypes.DATE)
-                .addScalar("end", StandardBasicTypes.DATE);
-            sqlQuery.setString("name", river);
-            sqlQuery.setDouble("startKm", startKm);
-            sqlQuery.setDouble("endKm", endKm);
-            List<Object []> results = sqlQuery.list();
-
-            SedimentLoad[] loads = new SedimentLoad[results.size()];
-            for (int i = 0; i < results.size(); i++) {
-                Object[] row = results.get(i);
-                loads[i] = new SedimentLoad(
-                    (String) row[0],
-                    (Date) row[1],
-                    (Date) row[2],
-                    true);
-            }
-            return loads;
-        }
-        return new SedimentLoad[0];
-    }
-
-    /**
-     * Get sediment loads from db.
-     * @param river the river
-     * @param type the sediment load type (year or epoch)
-     * @return according sediment loads.
-     */
-    public static SedimentLoad getSedimentLoadWithDataUncached(
-        String river,
-        String type,
-        double startKm,
-        double endKm,
-        int syear,
-        int eyear
-    ) {
-        log.debug("SedimentLoadFactory.getSedimentLoadWithDataUncached");
-        Session session = SessionHolder.HOLDER.get();
-        SQLQuery sqlQuery = null;
-
-        Calendar start = Calendar.getInstance();
-        start.set(syear - 1, 11, 31);
-        Calendar end = Calendar.getInstance();
-        end.set(syear, 11, 30);
-
-        if (type.equals("year") || type.equals("epoch")) {
-            sqlQuery = session.createSQLQuery(SQL_SELECT_SINGLES_DATA)
-                .addScalar("description", StandardBasicTypes.STRING)
-                .addScalar("year", StandardBasicTypes.DATE)
-                .addScalar("load", StandardBasicTypes.DOUBLE)
-                .addScalar("km", StandardBasicTypes.DOUBLE);
-            sqlQuery.setString("name", river);
-            sqlQuery.setDouble("startKm", startKm);
-            sqlQuery.setDouble("endKm", endKm);
-            sqlQuery.setDate("begin", start.getTime());
-            sqlQuery.setDate("end", end.getTime());
-            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);
-            getValues("coarse", sqlQuery, load);
-            getValues("fine_middle", sqlQuery, load);
-            getValues("sand", sqlQuery, load);
-            getValues("suspended_sediment", sqlQuery, load);
-            getValues("susp_sand_bed", sqlQuery, load);
-            getValues("susp_sand", sqlQuery, load);
-
-            return load;
-        }
-        else if (type.equals("off_epoch")) {
-            Calendar toStart = Calendar.getInstance();
-            toStart.set(eyear - 1, 11, 31);
-            Calendar toEnd = Calendar.getInstance();
-            toEnd.set(eyear, 11, 30);
-            sqlQuery = session.createSQLQuery(SQL_SELECT_EPOCHS_DATA)
-                .addScalar("description", StandardBasicTypes.STRING)
-                .addScalar("startYear", StandardBasicTypes.DATE)
-                .addScalar("load", StandardBasicTypes.DOUBLE)
-                .addScalar("km", StandardBasicTypes.DOUBLE)
-                .addScalar("endYear", StandardBasicTypes.DATE);
-            sqlQuery.setString("name", river);
-            sqlQuery.setDouble("startKm", startKm);
-            sqlQuery.setDouble("endKm", endKm);
-            sqlQuery.setDate("sbegin", start.getTime());
-            sqlQuery.setDate("send", end.getTime());
-            sqlQuery.setDate("ebegin",toStart.getTime());
-            sqlQuery.setDate("eend", toEnd.getTime());
-            sqlQuery.setString("grain", "total");
-
-            List<Object[]> results = null;
-            results = sqlQuery.list();
-
-            SedimentLoad load = new SedimentLoad();
-            Object[] row = results.get(0);
-            load = new SedimentLoad(
-                    (String) row[0],
-                    (Date) row[1],
-                    (Date) row[4],
-                    true);
-            TDoubleArrayList kms = new TDoubleArrayList();
-            for (int i = 0; i < results.size(); i++) {
-                row = results.get(i);
-                kms.add((Double)row[3]);
-                load.setLoadTotal((Double)row[3], (Double)row[2]);
-            }
-            getValues("coarse", sqlQuery, load);
-            getValues("fine_middle", sqlQuery, load);
-            getValues("sand", sqlQuery, load);
-            getValues("suspended_sediment", sqlQuery, load);
-            getValues("susp_sand_bed", sqlQuery, load);
-            getValues("susp_sand", sqlQuery, load);
-            return load;
-        }
-        return new SedimentLoad();
-    }
-
-
-    /**
-     * Run query with grain parameter set to fraction, feed result into
-     * load.
-     * @param fraction value to set 'grain' parameter in query to.
-     * @param query query in which to set 'grain' parameter and run.
-     * @param load[out] SedimentLoad which to populate with values.
-     */
-    protected static void getValues (
-        String fraction,
-        SQLQuery query,
-        SedimentLoad load
-    ) {
-        query.setString("grain", fraction);
-        List<Object[]> results = query.list();
-        for (int i = 0; i < results.size(); i++) {
-            Object[] row = results.get(i);
-            double km = (Double)row[3];
-            double v = -1;
-            if (row[2] != null) {
-                v = ((Double)row[2]).doubleValue();
-            }
-            if (fraction.equals("coarse")) {
-                load.setCoarse(km, v);
-            }
-            else if (fraction.equals("sand")) {
-                load.setSand(km, v);
-            }
-            else if (fraction.equals("fine_middle")) {
-                load.setFineMiddle(km, v);
-            }
-            else if (fraction.equals("suspended_sediment")) {
-                load.setSuspSediment(km, v);
-            }
-            else if (fraction.equals("susp_sand")) {
-                load.setSuspSand(km, v);
-            }
-            else if (fraction.equals("susp_sand_bed")) {
-                load.setSuspSandBed(km, v);
-            }
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/SedimentLoadFraction.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,91 +0,0 @@
-package de.intevation.flys.artifacts.model.minfo;
-
-import de.intevation.flys.artifacts.model.NamedObjectImpl;
-
-
-public class SedimentLoadFraction
-extends NamedObjectImpl
-{
-    double sand;
-    double fine_middle;
-    double coarse;
-    double susp_sand;
-    double susp_sand_bed;
-    double susp_sediment;
-    double loadTotal;
-    double total;
-
-    public SedimentLoadFraction() {
-        sand = 0d;
-        fine_middle = 0d;
-        coarse = 0d;
-        susp_sand = 0d;
-        susp_sand_bed = 0d;
-        susp_sediment = 0d;
-        loadTotal = 0d;
-    }
-
-    public double getSand() {
-        return sand;
-    }
-
-    public void setSand(double sand) {
-        this.sand = sand;
-    }
-
-    public double getFine_middle() {
-        return fine_middle;
-    }
-
-    public void setFine_middle(double fine_middle) {
-        this.fine_middle = fine_middle;
-    }
-
-    public double getCoarse() {
-        return coarse;
-    }
-
-    public void setCoarse(double coarse) {
-        this.coarse = coarse;
-    }
-
-    public double getSusp_sand() {
-        return susp_sand;
-    }
-
-    public void setSusp_sand(double susp_sand) {
-        this.susp_sand = susp_sand;
-    }
-
-    public double getSusp_sand_bed() {
-        return susp_sand_bed;
-    }
-
-    public void setSusp_sand_bed(double susp_sand_bed) {
-        this.susp_sand_bed = susp_sand_bed;
-    }
-
-    public double getSusp_sediment() {
-        return susp_sediment;
-    }
-
-    public void setSusp_sediment(double susp_sediment) {
-        this.susp_sediment = susp_sediment;
-    }
-
-    public double getTotal() {
-        return total;
-    }
-
-    public void setTotal(double total) {
-        this.total = total;
-    }
-
-    public double getLoadTotal() {
-        return loadTotal;
-    }
-
-    public void setLoadTotal(double total) {
-        this.loadTotal = total;
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/SedimentLoadResult.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,213 +0,0 @@
-package de.intevation.flys.artifacts.model.minfo;
-
-import gnu.trove.TDoubleArrayList;
-
-import java.io.Serializable;
-import java.util.Set;
-
-import org.apache.log4j.Logger;
-
-
-/** Result from a SedimentLoadCalculation. */
-public class SedimentLoadResult
-implements Serializable
-{
-    private static final Logger logger = Logger
-        .getLogger(SedimentLoadResult.class);
-    protected int startYear;
-    protected int endYear;
-    protected SedimentLoad load;
-
-    public SedimentLoadResult() {
-    }
-
-    public SedimentLoadResult(
-        int startYear,
-        int endYear,
-        SedimentLoad load
-    ) {
-        this.startYear = startYear;
-        this.endYear = endYear;
-        this.load = load;
-    }
-
-    public int getStartYear() {
-        return this.startYear;
-    }
-
-    public void setStartYear(int year) {
-        this.startYear = year;
-    }
-
-    public int getEndYear() {
-        return this.endYear;
-    }
-
-    public void setEndYear(int year) {
-        this.endYear = year;
-    }
-
-    public double[][] getTotalData () {
-        Set<Double> kms = this.load.getKms();
-        TDoubleArrayList k = new TDoubleArrayList();
-        TDoubleArrayList total = new TDoubleArrayList();
-        for (double km : kms) {
-            if (load.getFraction(km).getTotal() > 0d) {
-                k.add(km);
-                total.add(load.getFraction(km).getTotal());
-            }
-        }
-        return new double [][] {
-            k.toNativeArray(),
-            total.toNativeArray()
-        };
-    }
-
-    /** Search all SedimenLoads fractions for sand and returns
-     * an array [[km1, km2][sand1, sand2]]. */
-    public double[][] getSandData() {
-        Set<Double> kms = this.load.getKms();
-        TDoubleArrayList k = new TDoubleArrayList();
-        TDoubleArrayList sand = new TDoubleArrayList();
-        for(double km : kms) {
-            if (load.getFraction(km).getSand() > 0d) {
-                k.add(km);
-                sand.add(load.getFraction(km).getSand());
-            }
-        }
-        return new double [][] {
-            k.toNativeArray(),
-            sand.toNativeArray()
-        };
-    }
-
-    public double[][] getFineMiddleData() {
-        Set<Double> kms = this.load.getKms();
-        TDoubleArrayList k = new TDoubleArrayList();
-        TDoubleArrayList fm = new TDoubleArrayList();
-        for (double km : kms) {
-            if (load.getFraction(km).getFine_middle() > 0d) {
-                k.add(km);
-                fm.add(load.getFraction(km).getFine_middle());
-            }
-        }
-        return new double [][] {
-            k.toNativeArray(),
-            fm.toNativeArray()
-        };
-    }
-
-    public double[][] getCoarseData() {
-        Set<Double> kms = this.load.getKms();
-        TDoubleArrayList k = new TDoubleArrayList();
-        TDoubleArrayList coarse = new TDoubleArrayList();
-        for (double km : kms) {
-            if (load.getFraction(km).getCoarse() > 0d) {
-                k.add(km);
-                coarse.add(load.getFraction(km).getCoarse());
-            }
-        }
-        return new double [][] {
-            k.toNativeArray(),
-            coarse.toNativeArray()
-        };
-    }
-
-    public double[][] getSuspSandData() {
-        Set<Double> kms = this.load.getKms();
-        TDoubleArrayList k = new TDoubleArrayList();
-        TDoubleArrayList ss = new TDoubleArrayList();
-        for (double km : kms) {
-            if (load.getFraction(km).getSusp_sand() > 0d) {
-                k.add(km);
-                ss.add(load.getFraction(km).getSusp_sand());
-            }
-        }
-        return new double [][] {
-            k.toNativeArray(),
-            ss.toNativeArray()
-        };
-    }
-
-    public double[][] getSuspSandBedData() {
-        Set<Double> kms = this.load.getKms();
-        TDoubleArrayList k = new TDoubleArrayList();
-        TDoubleArrayList ss = new TDoubleArrayList();
-        for (double km : kms) {
-            if (load.getFraction(km).getSusp_sand_bed() > 0d) {
-                k.add(km);
-                ss.add(load.getFraction(km).getSusp_sand_bed());
-            }
-        }
-        return new double [][] {
-            k.toNativeArray(),
-            ss.toNativeArray()
-        };
-    }
-
-    public double[][] getSuspSedimentData() {
-        Set<Double> kms = this.load.getKms();
-        TDoubleArrayList k = new TDoubleArrayList();
-        TDoubleArrayList ss = new TDoubleArrayList();
-        for (double km : kms) {
-            if (load.getFraction(km).getSusp_sediment() > 0d) {
-                k.add(km);
-                ss.add(load.getFraction(km).getSusp_sediment());
-            }
-        }
-        return new double [][] {
-            k.toNativeArray(),
-            ss.toNativeArray()
-        };
-    }
-
-    public double[][] getTotalLoadData() {
-        Set<Double> kms = this.load.getKms();
-        TDoubleArrayList k = new TDoubleArrayList();
-        TDoubleArrayList ss = new TDoubleArrayList();
-        for (double km : kms) {
-            if (load.getFraction(km).getLoadTotal() > 0d) {
-                k.add(km);
-                ss.add(load.getFraction(km).getLoadTotal());
-            }
-        }
-        return new double [][] {
-            k.toNativeArray(),
-            ss.toNativeArray()
-        };
-    }
-
-    public boolean hasCoarseData() {
-        return getCoarseData()[0].length > 0;
-    }
-
-    public boolean hasFineMiddleData() {
-        return getFineMiddleData()[0].length > 0;
-    }
-
-    public boolean hasSandData() {
-        return getSandData()[0].length > 0;
-    }
-
-    public boolean hasSuspSandData() {
-        return getSuspSandData()[0].length > 0;
-    }
-
-    public boolean hasSuspSandBedData() {
-        return getSuspSandBedData()[0].length > 0;
-    }
-
-    public boolean hasSuspSedimentData() {
-        return getSuspSedimentData()[0].length > 0;
-    }
-
-    public boolean hasTotalLoadData() {
-        return getTotalLoadData()[0].length > 0;
-    }
-
-    public boolean hasTotalData() {
-        return getTotalData()[0].length > 0;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
-
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/sq/Fitting.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,149 +0,0 @@
-package de.intevation.flys.artifacts.model.sq;
-
-import de.intevation.flys.artifacts.math.fitting.Function;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.commons.math.MathException;
-
-import org.apache.commons.math.optimization.fitting.CurveFitter;
-
-import org.apache.commons.math.optimization.general.LevenbergMarquardtOptimizer;
-
-import org.apache.log4j.Logger;
-
-public class Fitting
-implements   Outlier.Callback
-{
-    private static Logger log = Logger.getLogger(Fitting.class);
-
-    public interface Callback {
-
-        void afterIteration(
-            double [] parameters,
-            SQ []     measurements,
-            SQ []     outliers,
-            double    standardDeviation,
-            double    chiSqr);
-    } // interfacte
-
-    protected Function function;
-
-    protected double [] coeffs;
-
-    protected de.intevation.flys.artifacts.math.Function instance;
-
-    protected double stdDevFactor;
-    protected double chiSqr;
-
-    protected Callback callback;
-
-    public Fitting() {
-    }
-
-    public Fitting(Function function, double stdDevFactor) {
-        this();
-        this.function     = function;
-        this.stdDevFactor = stdDevFactor;
-    }
-
-    public Function getFunction() {
-        return function;
-    }
-
-    public void setFunction(Function function) {
-        this.function = function;
-    }
-
-    public double getStdDevFactor() {
-        return stdDevFactor;
-    }
-
-    public void setStdDevFactor(double stdDevFactor) {
-        this.stdDevFactor = stdDevFactor;
-    }
-
-    @Override
-    public void initialize(List<SQ> sqs) throws MathException {
-
-        LevenbergMarquardtOptimizer lmo =
-            new LevenbergMarquardtOptimizer();
-
-        CurveFitter cf = new CurveFitter(lmo);
-        for (SQ sq: sqs) {
-            cf.addObservedPoint(sq.getQ(), sq.getS());
-        }
-
-        coeffs = cf.fit(
-            function, function.getInitialGuess());
-
-        instance = function.instantiate(coeffs);
-
-        chiSqr = lmo.getChiSquare();
-    }
-
-    @Override
-    public double eval(SQ sq) {
-        double s = instance.value(sq.q);
-        return sq.s - s;
-    }
-
-    @Override
-    public void iterationFinished(
-        double   standardDeviation,
-        SQ       outlier,
-        List<SQ> remainings
-    ) {
-        if (log.isDebugEnabled()) {
-            log.debug("iterationFinished ----");
-            log.debug(" num remainings: " + remainings.size());
-            log.debug(" has outlier: " + outlier != null);
-            log.debug(" standardDeviation: " + standardDeviation);
-            log.debug(" Chi^2: " + chiSqr);
-            log.debug("---- iterationFinished");
-        }
-        callback.afterIteration(
-            coeffs,
-            remainings.toArray(new SQ[remainings.size()]),
-            outlier != null ? new SQ [] { outlier} : new SQ [] {},
-            standardDeviation,
-            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);
-
-        if (sqs.size() < 2) {
-            log.warn("Too less points for fitting.");
-            return false;
-        }
-
-        this.callback = callback;
-
-        try {
-            Outlier.detectOutliers(this, sqs, stdDevFactor, method);
-        }
-        catch (MathException me) {
-            log.warn(me);
-            return false;
-        }
-
-        return true;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/sq/Measurement.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,202 +0,0 @@
-package de.intevation.flys.artifacts.model.sq;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-public class Measurement
-{
-    private static final Log log =
-        LogFactory.getLog(Measurement.class);
-
-    public static final double LOG_10_8 = Math.log(10) - Math.log(8);
-    public static final double SCALE_8 = Math.log(10) - Math.log(6.3);
-
-    public static final double LOG_8_6 = Math.log(8) - Math.log(6.3);
-    public static final double SCALE_4 = Math.log(10) - Math.log(6.3);
-
-    protected Map<String, Object> data;
-
-    protected List<Sieve> sieves;
-
-    protected SieveArray sieveArray;
-
-    public Measurement() {
-    }
-
-    public Measurement(Map<String, Object> data, List<Sieve> sieves) {
-        this.data = data;
-        this.sieves = sieves;
-    }
-
-    protected double get(String name) {
-        Number value = (Number)data.get(name);
-        return value != null ? value.doubleValue() : Double.NaN;
-    }
-
-    protected void set(String name, double value) {
-        data.put(name, Double.valueOf(value));
-    }
-
-    public Object getData(String name) {
-        return data.get(name);
-    }
-
-    public Map<String, Object> getData() {
-        return data;
-    }
-
-    protected void putData(String name, Object value) {
-        data.put(name, value);
-    }
-
-    public double S_SS() {
-        return get("TSAND");
-    }
-
-    public double S_SF() {
-        return get("TSCHWEB") - get("TSAND");
-    }
-
-    public double Q() {
-        return get("Q_BPEGEL");
-    }
-
-    public double TOTAL_BL() {
-        return get("TGESCHIEBE");
-    }
-
-    public double BL_G() {
-        return get("BL_G");
-    }
-
-    public double BL_C() {
-        return get("BL_C");
-    }
-
-    public double BL_S() {
-        return get("BL_S");
-    }
-
-    public double S_BL_S() {
-        return TOTAL_BL() * BL_S();
-    }
-
-    public double S_BL_FG() {
-        return TOTAL_BL() * BL_G();
-    }
-
-    public double S_BL_CG() {
-        return TOTAL_BL() * BL_C();
-    }
-
-    public double S_BL_1() {
-        return S_BL_S() + S_BL_FG() + S_BL_CG();
-    }
-
-    public double S_BL_2() {
-        return S_SS() + S_BL_S() + S_BL_FG() + S_BL_CG();
-    }
-
-    @Override
-    public String toString() {
-        return "Measurement: " + data;
-    }
-
-    /**
-     * Gets the sieves for this instance.
-     *
-     * @return The sieves.
-     */
-    public List<Sieve> getSieves() {
-        return this.sieves;
-    }
-
-    /**
-     * Gets the sieveArray for this instance.
-     *
-     * @return The sieveArray.
-     */
-    public SieveArray getSieveArray() {
-        if (sieveArray == null) {
-            sieveArray = calculateSieveArray();
-        }
-        adjustSieves();
-        return sieveArray;
-    }
-
-    protected Sieve findSieve(double diameter) {
-        for (Sieve s: sieves) {
-            if (s.matchesDiameter(diameter)) {
-                return s;
-            }
-        }
-        return null;
-    }
-
-    protected void deleteSieve(double diameter) {
-        for (int i = sieves.size()-1; i >= 0; --i) {
-            if (sieves.get(i).matchesDiameter(diameter)) {
-                sieves.remove(i);
-                break;
-            }
-        }
-    }
-
-    public void adjustSieves() {
-
-        // If we already have an 8mm diameter sieve
-        // we dont need to 'invent' it.
-        if (findSieve(8d) != null) {
-            return;
-        }
-
-        // create a new 8mm sieve.
-        // delete 6.3mm sieve.
-        // modify 4mm sieve.
-
-        Sieve six  = findSieve(6.3d);
-        Sieve ten  = findSieve(10d);
-        Sieve four = findSieve(4d);
-
-        if (six == null || ten == null || four == null) {
-            log.warn("missing diameter");
-            return;
-        }
-
-        double sixValue  = six.getLoad();
-        double tenValue  = ten.getLoad();
-        double fourValue = four.getLoad();
-
-        deleteSieve(6.3);
-
-        double eightValue = ((LOG_10_8 / SCALE_8*sixValue) + tenValue);
-        double newFourValue = ((LOG_8_6 / SCALE_4*sixValue) + fourValue);
-
-        deleteSieve(4.0);
-        sieves.add(new Sieve(8d, eightValue));
-        sieves.add(new Sieve(4d, newFourValue));
-        sieveArray.adjust(
-            eightValue/sieveArray.totalLoad(),
-            newFourValue/sieveArray.totalLoad());
-        log.debug("Adjusted loads: " + Arrays.toString(sieveArray.getLoads()));
-        log.debug("Adjusted norm loads:" + Arrays.toString(sieveArray.getNormLoads()));
-    }
-
-    protected SieveArray calculateSieveArray() {
-
-        SieveArray sa = new SieveArray();
-
-        for (Sieve s: sieves) {
-            sa.doSieving(s);
-        }
-
-        sa.calculateNormLoads();
-
-        return sa;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/sq/MeasurementFactory.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,511 +0,0 @@
-package de.intevation.flys.artifacts.model.sq;
-
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.TreeMap;
-
-import org.apache.log4j.Logger;
-
-import org.hibernate.SQLQuery;
-import org.hibernate.Session;
-
-import org.hibernate.transform.BasicTransformerAdapter;
-
-import org.hibernate.type.StandardBasicTypes;
-
-import de.intevation.flys.artifacts.model.DateRange;
-
-import de.intevation.flys.backend.SedDBSessionHolder;
-
-public class MeasurementFactory
-{
-    private static final Logger log =
-        Logger.getLogger(MeasurementFactory.class);
-
-    public static final String SQL_TOTALS =
-        "SELECT " +
-            "m.Q_BPEGEL AS Q_BPEGEL,"+
-            "m.TSCHWEB  AS TSCHWEB," +
-            "m.TSAND    AS TSAND " +
-        "FROM MESSUNG m " +
-            "JOIN STATION   s ON m.STATIONID   = s.STATIONID " +
-            "JOIN GEWAESSER r ON s.GEWAESSERID = r.GEWAESSERID " +
-        "WHERE " +
-            "r.NAME = :river_name " +
-            "AND m.Q_BPEGEL IS NOT NULL " +
-            "AND s.KM BETWEEN :location - 0.001 AND :location + 0.001 " +
-            "AND m.DATUM BETWEEN :from AND :to " +
-            "AND m.DATUM IS NOT NULL";
-
-    public static final String SQL_FACTIONS =
-        "SELECT " +
-            "m.datum        AS DATUM," +
-            "m.Q_BPEGEL     AS Q_BPEGEL,"+
-            "g.GLOTRECHTEID AS GLOTRECHTEID," +
-            "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 " +
-        "FROM MESSUNG m " +
-            "JOIN STATION    s ON m.STATIONID    = s.STATIONID " +
-            "JOIN GEWAESSER  r ON s.GEWAESSERID  = r.GEWAESSERID " +
-            "JOIN GLOTRECHTE g ON m.MESSUNGID    = g.MESSUNGID " +
-            "JOIN GPROBE    gp ON g.GLOTRECHTEID = gp.GLOTRECHTEID " +
-            "JOIN GSIEBUNG  gs ON g.GLOTRECHTEID = gs.GLOTRECHTEID " +
-            "JOIN GSIEBSATZ si ON m.GSIEBSATZID  = si.GSIEBSATZID " +
-        "WHERE " +
-            "r.NAME = :river_name " +
-            "AND m.Q_BPEGEL IS NOT NULL " +
-            "AND s.KM BETWEEN :location - 0.001 AND :location + 0.001 " +
-            "AND m.DATUM BETWEEN :from AND :to " +
-            "AND m.TGESCHIEBE IS NOT NULL " +
-            "AND m.DATUM IS NOT NULL " +
-            "AND (" +
-                "COALESCE(gs.RSIEB01, 0) + COALESCE(gs.RSIEB02, 0) +" +
-                "COALESCE(gs.RSIEB03, 0) + COALESCE(gs.RSIEB04, 0) +" +
-                "COALESCE(gs.RSIEB05, 0) + COALESCE(gs.RSIEB06, 0) +" +
-                "COALESCE(gs.RSIEB07, 0) + COALESCE(gs.RSIEB08, 0) +" +
-                "COALESCE(gs.RSIEB09, 0) + COALESCE(gs.RSIEB10, 0) +" +
-                "COALESCE(gs.RSIEB11, 0) + COALESCE(gs.RSIEB12, 0) +" +
-                "COALESCE(gs.RSIEB13, 0) + COALESCE(gs.RSIEB14, 0) +" +
-                "COALESCE(gs.RSIEB15, 0) + COALESCE(gs.RSIEB16, 0) +" +
-                "COALESCE(gs.RSIEB17, 0) + COALESCE(gs.RSIEB18, 0) +" +
-                "COALESCE(gs.RSIEB19, 0) + COALESCE(gs.RSIEB20, 0) +" +
-                "COALESCE(gs.RSIEB21, 0) + COALESCE(gs.REST, 0)) >= 0 " +
-        "ORDER BY " +
-            "m.DATUM, g.UFERABST, g.GLOTRECHTEID, gp.LFDNR";
-
-    public static final BasicTransformerAdapter TOTALS_TRANSFORMER =
-        new BasicTransformerAdapter() {
-			private static final long serialVersionUID = 1L;
-
-			@Override
-            public Object transformTuple(Object [] tuple, String [] aliases) {
-                Map<String, Object> map = new HashMap<String, Object>();
-                for (int i = 0; i < tuple.length; ++i) {
-                    Object value = tuple[i];
-                    if (value != null) {
-                        map.put(aliases[i], value);
-                    }
-                }
-                return new Measurement(map, Collections.<Sieve>emptyList());
-            }
-        };
-
-    private static final int index(String s) {
-        return Integer.parseInt(s.substring(s.length()-2))-1;
-    }
-
-    public static final BasicTransformerAdapter FRACTIONS_TRANSFORMER =
-        new BasicTransformerAdapter() {
-			private static final long serialVersionUID = 1L;
-
-            @Override
-            public Object transformTuple(Object [] tuple, String [] aliases) {
-                Map<String, Object> map = new HashMap<String, Object>();
-
-                Sieve [] sieves = new Sieve[21];
-
-                List<Sieve> validSieves = new ArrayList<Sieve>(21);
-
-                for (int i = 0; i < tuple.length; ++i) {
-                    Object value = tuple[i];
-                    if (value == null) {
-                        continue;
-                    }
-                    String alias = aliases[i];
-                    if (alias.startsWith("SIEB")
-                    ||  alias.startsWith("RSIEB")) {
-                        int idx = index(alias);
-                        Sieve s = sieves[idx];
-                        double v = (Double)value;
-                        if (s == null) {
-                            s = new Sieve();
-                            sieves[idx] = s;
-                        }
-                        if (alias.startsWith("SIEB")) {
-                            s.setDiameter(v);
-                        }
-                        else {
-                            s.setLoad(v);
-                        }
-                    }
-                    else if (alias.equals("REST")) {
-                        Sieve s = new Sieve(0d, (Double)value);
-                        validSieves.add(s);
-                    }
-                    else {
-                        map.put(alias, value);
-                    }
-
-                }
-                for (Sieve s: sieves) {
-                    if (s != null) {
-                        validSieves.add(s);
-                    }
-                }
-                return new Measurement(map, validSieves);
-            }
-        };
-
-    private MeasurementFactory() {
-    }
-
-    public static Measurements getMeasurements(
-        String    river,
-        double    location,
-        DateRange dateRange
-    ) {
-        Session session = SedDBSessionHolder.HOLDER.get();
-        try {
-            List<Measurement> totals = loadTotals(
-                session, river, location, dateRange);
-
-            List<Measurement> accumulated = loadFractions(
-                session, river, location, dateRange);
-
-            return new Measurements(totals, accumulated);
-        }
-        finally {
-            session.close();
-        }
-    }
-
-    protected static List<Measurement> loadTotals(
-        Session   session,
-        String    river,
-        double    location,
-        DateRange dateRange
-    ) {
-        SQLQuery query = session.createSQLQuery(SQL_TOTALS)
-            .addScalar("Q_BPEGEL", StandardBasicTypes.DOUBLE)
-            .addScalar("TSCHWEB",  StandardBasicTypes.DOUBLE)
-            .addScalar("TSAND",    StandardBasicTypes.DOUBLE);
-
-        query.setString("river_name", river);
-        query.setDouble("location", location);
-        query.setDate("from", dateRange.getFrom());
-        query.setDate("to", dateRange.getTo());
-
-        query.setResultTransformer(TOTALS_TRANSFORMER);
-
-        @SuppressWarnings("unchecked")
-        List<Measurement> result = (List<Measurement>)query.list();
-        return result;
-    }
-
-    protected static List<Measurement> loadFractions(
-        Session   session,
-        String    river,
-        double    location,
-        DateRange dateRange
-    ) {
-        boolean debug = log.isDebugEnabled();
-
-        SQLQuery query = session.createSQLQuery(SQL_FACTIONS)
-            .addScalar("Q_BPEGEL",     StandardBasicTypes.DOUBLE)
-            .addScalar("DATUM",        StandardBasicTypes.DATE)
-            .addScalar("GLOTRECHTEID", StandardBasicTypes.INTEGER)
-            .addScalar("LFDNR",        StandardBasicTypes.INTEGER)
-            .addScalar("UFERABST",     StandardBasicTypes.DOUBLE)
-            .addScalar("UFERABLINKS",  StandardBasicTypes.DOUBLE)
-            .addScalar("TSCHWEB",      StandardBasicTypes.DOUBLE)
-            .addScalar("TSAND",        StandardBasicTypes.DOUBLE)
-            .addScalar("GTRIEB",       StandardBasicTypes.DOUBLE)
-            .addScalar("TGESCHIEBE",   StandardBasicTypes.DOUBLE)
-            .addScalar("RSIEB01",      StandardBasicTypes.DOUBLE)
-            .addScalar("RSIEB02",      StandardBasicTypes.DOUBLE)
-            .addScalar("RSIEB03",      StandardBasicTypes.DOUBLE)
-            .addScalar("RSIEB04",      StandardBasicTypes.DOUBLE)
-            .addScalar("RSIEB05",      StandardBasicTypes.DOUBLE)
-            .addScalar("RSIEB06",      StandardBasicTypes.DOUBLE)
-            .addScalar("RSIEB07",      StandardBasicTypes.DOUBLE)
-            .addScalar("RSIEB08",      StandardBasicTypes.DOUBLE)
-            .addScalar("RSIEB09",      StandardBasicTypes.DOUBLE)
-            .addScalar("RSIEB10",      StandardBasicTypes.DOUBLE)
-            .addScalar("RSIEB11",      StandardBasicTypes.DOUBLE)
-            .addScalar("RSIEB12",      StandardBasicTypes.DOUBLE)
-            .addScalar("RSIEB13",      StandardBasicTypes.DOUBLE)
-            .addScalar("RSIEB14",      StandardBasicTypes.DOUBLE)
-            .addScalar("RSIEB15",      StandardBasicTypes.DOUBLE)
-            .addScalar("RSIEB16",      StandardBasicTypes.DOUBLE)
-            .addScalar("RSIEB17",      StandardBasicTypes.DOUBLE)
-            .addScalar("RSIEB18",      StandardBasicTypes.DOUBLE)
-            .addScalar("RSIEB19",      StandardBasicTypes.DOUBLE)
-            .addScalar("RSIEB20",      StandardBasicTypes.DOUBLE)
-            .addScalar("RSIEB21",      StandardBasicTypes.DOUBLE)
-            .addScalar("REST",         StandardBasicTypes.DOUBLE)
-            .addScalar("SIEB01",       StandardBasicTypes.DOUBLE)
-            .addScalar("SIEB02",       StandardBasicTypes.DOUBLE)
-            .addScalar("SIEB03",       StandardBasicTypes.DOUBLE)
-            .addScalar("SIEB04",       StandardBasicTypes.DOUBLE)
-            .addScalar("SIEB05",       StandardBasicTypes.DOUBLE)
-            .addScalar("SIEB06",       StandardBasicTypes.DOUBLE)
-            .addScalar("SIEB07",       StandardBasicTypes.DOUBLE)
-            .addScalar("SIEB08",       StandardBasicTypes.DOUBLE)
-            .addScalar("SIEB09",       StandardBasicTypes.DOUBLE)
-            .addScalar("SIEB10",       StandardBasicTypes.DOUBLE)
-            .addScalar("SIEB11",       StandardBasicTypes.DOUBLE)
-            .addScalar("SIEB12",       StandardBasicTypes.DOUBLE)
-            .addScalar("SIEB13",       StandardBasicTypes.DOUBLE)
-            .addScalar("SIEB14",       StandardBasicTypes.DOUBLE)
-            .addScalar("SIEB15",       StandardBasicTypes.DOUBLE)
-            .addScalar("SIEB16",       StandardBasicTypes.DOUBLE)
-            .addScalar("SIEB17",       StandardBasicTypes.DOUBLE)
-            .addScalar("SIEB18",       StandardBasicTypes.DOUBLE)
-            .addScalar("SIEB19",       StandardBasicTypes.DOUBLE)
-            .addScalar("SIEB20",       StandardBasicTypes.DOUBLE)
-            .addScalar("SIEB21",       StandardBasicTypes.DOUBLE);
-
-        query.setString("river_name", river);
-        query.setDouble("location", location);
-        query.setDate("from", dateRange.getFrom());
-        query.setDate("to", dateRange.getTo());
-
-        query.setResultTransformer(FRACTIONS_TRANSFORMER);
-
-        @SuppressWarnings("unchecked")
-        List<Measurement> measuments = (List<Measurement>)query.list();
-
-        if (debug) {
-            log.debug("num fraction results: " + measuments.size());
-        }
-
-        List<Measurement> same = new ArrayList<Measurement>();
-
-        Integer lastLR = null;
-
-        List<Measurement> accumulated = new ArrayList<Measurement>();
-
-        for (Measurement m: measuments) {
-            Integer currentLR = (Integer)m.getData("GLOTRECHTEID");
-
-            boolean newDS = lastLR == null
-                || (currentLR != null && !lastLR.equals(currentLR));
-
-            if (newDS && !same.isEmpty()) {
-                accumulated.add(accumulate(same));
-                same.clear();
-            }
-
-            same.add(m);
-
-            lastLR = currentLR;
-        }
-
-        if (!same.isEmpty()) {
-            accumulated.add(accumulate(same));
-        }
-
-        if (debug) {
-            log.debug("Before date separation: " + accumulated.size());
-        }
-
-        accumulated = separateByDate(accumulated);
-
-        if (debug) {
-            log.debug("After date separation: " + accumulated.size());
-        }
-
-        return accumulated;
-    }
-
-    protected static List<Measurement> separateByDate(List<Measurement> measurements) {
-
-        List<Measurement> result = new ArrayList<Measurement>();
-
-        List<Measurement> same = new ArrayList<Measurement>();
-
-        Date lastDate = null;
-
-        for (Measurement m: measurements) {
-            Date currentDate = (Date)m.getData("DATUM");
-            if ((lastDate == null
-            || !equalDate(currentDate, lastDate))
-            && !same.isEmpty()
-            ) {
-                result.add(processSameDate(same));
-                same.clear();
-            }
-            same.add(m);
-            lastDate = currentDate;
-        }
-
-        if (!same.isEmpty()) {
-            result.add(processSameDate(same));
-        }
-
-        return result;
-    }
-
-
-    protected static Measurement processSameDate(List<Measurement> measurements) {
-        int N = measurements.size();
-
-        boolean debug = log.isDebugEnabled();
-        if (debug && N > 0) {
-            log.debug("process same date for Q: " + measurements.get(0).Q());
-        }
-        if (N == 1) {
-            Measurement current = measurements.get(0);
-            double left = current.get("UFERABLINKS");
-            double right = current.get("UFERABST");
-            current.set("EFFWIDTH", left + right);
-        }
-        else {
-            for (int i = 0; i < N; ++i) {
-                Measurement current = measurements.get(i);
-
-                if (i == 0) {
-                    Measurement next = measurements.get(i+1);
-                    double distCurrent = current.get("UFERABST");
-                    double distNext = next.get("UFERABST");
-                    current.set("EFFWIDTH", distNext - distCurrent);
-                }
-                else if (i == N-1) {
-                    Measurement prev = measurements.get(i-1);
-                    double distCurrent = current.get("UFERABST");
-                    double distPrev = prev.get("UFERABST");
-                    current.set("EFFWIDTH", distCurrent - distPrev);
-                }
-                else {
-                    Measurement prev = measurements.get(i-1);
-                    Measurement next = measurements.get(i+1);
-                    double distPrev = prev.get("UFERABST");
-                    double distNext = next.get("UFERABST");
-                    current.set("EFFWIDTH", 0.5*(distNext - distPrev));
-                }
-                if (debug) {
-                    log.debug("effective width: " + current.get("EFFWIDTH"));
-                }
-            }
-        }
-
-        double sumSandF   = 0d;
-        double sumCoarseF = 0d;
-        double sumGravelF = 0d;
-        double sumNorm    = 0d;
-
-        for (Measurement m: measurements) {
-            SieveArray sa   = m.getSieveArray();
-            if (sa.totalLoad() < SieveArray.EPSILON) {
-                continue;
-            }
-            double sandF    = sa.sandNormFraction();
-            double coarseF  = sa.coarseNormFraction();
-            double gravelF  = sa.gravelNormFraction();
-            double effWidth = m.get("EFFWIDTH");
-            double gt       = m.get("GTRIEB");
-            double scale    = effWidth*gt;
-            sumSandF   += scale*sandF;
-            sumCoarseF += scale*coarseF;
-            sumGravelF += scale*gravelF;
-            sumNorm    += scale;
-            if (debug) {
-                log.debug("fractions - s: " +
-                    sandF + " c: " +
-                    coarseF + " g: " +
-                    gravelF);
-                log.debug("scale: " + scale + " = " + effWidth + " * " + gt);
-            }
-        }
-
-        Map<String, Object> data =
-            new HashMap<String, Object>(measurements.get(0).getData());
-
-        Measurement m = new Measurement(data, Collections.<Sieve>emptyList());
-
-        sumNorm = 1d/sumNorm;
-
-        m.set("BL_S", sumNorm*sumSandF);
-        m.set("BL_G", sumNorm*sumGravelF);
-        m.set("BL_C", sumNorm*sumCoarseF);
-        if (debug) {
-            log.debug(
-                "BL_S: " + m.get("BL_S") +
-                " BL_G: " + m.get("BL_G") +
-                " BL_C: " + m.get("BL_C"));
-        }
-        return m;
-    }
-
-
-    private static final boolean equalDate(Date a, Date b) {
-        Calendar ca = Calendar.getInstance();
-        Calendar cb = Calendar.getInstance();
-        ca.setTime(a);
-        cb.setTime(b);
-        return ca.get(Calendar.YEAR)         == cb.get(Calendar.YEAR)
-            && ca.get(Calendar.MONTH)        == cb.get(Calendar.MONTH)
-            && ca.get(Calendar.DAY_OF_MONTH) == cb.get(Calendar.DAY_OF_MONTH);
-    }
-
-
-    protected static Measurement accumulate(List<Measurement> measuments) {
-
-        int N = measuments.size();
-        if (N == 1) {
-            return measuments.get(0);
-        }
-        TreeMap<Double, double []> diameters =
-            new TreeMap<Double, double []>(Sieve.DIAMETER_CMP);
-
-        double sumGTrieb = 0d;
-        for (Measurement m: measuments) {
-            for (Sieve s: m.getSieves()) {
-                Double key = s.getDiameter();
-                double [] sum = diameters.get(key);
-                if (sum == null) {
-                    sum = new double[1];
-                    diameters.put(key, sum);
-                }
-                sum[0] += s.getLoad();
-            }
-            // calculate 'Geschiebetrieb'
-            sumGTrieb += m.get("GTRIEB");
-        }
-        List<Sieve> accumulatedSieves = new ArrayList<Sieve>(diameters.size());
-        for (Map.Entry<Double, double []> entry: diameters.entrySet()) {
-            accumulatedSieves.add(
-                new Sieve(entry.getKey(),
-                    entry.getValue()[0]/N));
-        }
-        Map<String, Object> data =
-            new HashMap<String, Object>(measuments.get(0).getData());
-
-        data.put("GTRIEB", sumGTrieb/N);
-
-        return new Measurement(data, accumulatedSieves);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/sq/Measurements.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,164 +0,0 @@
-package de.intevation.flys.artifacts.model.sq;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-public class Measurements
-{
-    private static final Logger log = Logger.getLogger(Measurements.class);
-
-    public interface SExtractor {
-        double getS(Measurement measument);
-    } // interface SExtractor
-
-    public static final SExtractor S_SF_EXTRACTOR = new SExtractor() {
-        @Override
-        public double getS(Measurement measument) {
-            return measument.S_SF();
-        }
-    };
-
-    public static final SExtractor S_SS_EXTRACTOR = new SExtractor() {
-        @Override
-        public double getS(Measurement measument) {
-            return measument.S_SS();
-        }
-    };
-
-    public static final SExtractor S_BL_S_EXTRACTOR = new SExtractor() {
-        @Override
-        public double getS(Measurement measument) {
-            return measument.S_BL_S();
-        }
-    };
-
-    public static final SExtractor S_BL_FG_EXTRACTOR = new SExtractor() {
-        @Override
-        public double getS(Measurement measument) {
-            return measument.S_BL_FG();
-        }
-    };
-
-    public static final SExtractor S_BL_CG_EXTRACTOR = new SExtractor() {
-        @Override
-        public double getS(Measurement measument) {
-            return measument.S_BL_CG();
-        }
-    };
-
-    public static final SExtractor S_BL_EXTRACTOR = new SExtractor() {
-        @Override
-        public double getS(Measurement measument) {
-            return measument.BL_S();
-        }
-    };
-
-    protected List<Measurement> measuments;
-    protected List<Measurement> accumulated;
-
-    public Measurements() {
-    }
-
-    public Measurements(
-        List<Measurement> measuments,
-        List<Measurement> accumulated
-    ) {
-        if (log.isDebugEnabled()) {
-            log.debug("number of measuments: " + measuments.size());
-            log.debug("number of accumulated: " + accumulated.size());
-        }
-        this.measuments = measuments;
-        this.accumulated = accumulated;
-    }
-
-    public static 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());
-            if (sq.isValid()) {
-                result.add(sq);
-            }
-            else {
-                ++invalid;
-            }
-        }
-        if (log.isDebugEnabled()) {
-            log.debug("Removed num invalid: " + invalid
-                + " of " + measuments.size());
-        }
-        return result;
-    }
-
-    public List<SQ> S_SF() {
-        return extractSQ(measuments, S_SF_EXTRACTOR);
-    }
-
-    public List<SQ> S_SS() {
-        return extractSQ(measuments, S_SS_EXTRACTOR);
-    }
-
-    public List<SQ> S_BL_S() {
-        return extractSQ(accumulated, S_BL_S_EXTRACTOR);
-    }
-
-    public List<SQ> S_BL_FG() {
-        return extractSQ(accumulated, S_BL_FG_EXTRACTOR);
-    }
-
-    public List<SQ> S_BL_CG() {
-        return extractSQ(accumulated, S_BL_CG_EXTRACTOR);
-    }
-
-    public List<SQ> S_BL() {
-        return extractSQ(accumulated, S_BL_EXTRACTOR);
-    }
-
-    public List<SQ> getSQs(int index) {
-        switch (index) {
-            case 0: return S_SF();
-            case 1: return S_SS();
-            case 2: return S_BL_S();
-            case 3: return S_BL_FG();
-            case 4: return S_BL_CG();
-            case 5: return S_BL();
-        }
-        log.error("THIS SHOULD NOT HAPPEN: Tried to access SQ[" + index + "]");
-        return new ArrayList<SQ>(0);
-    }
-
-    /**
-     * Gets the accumulated for this instance.
-     *
-     * @return The accumulated.
-     */
-    public List<Measurement> getAccumulated() {
-        return this.accumulated;
-    }
-
-    /**
-     * Sets the accumulated for this instance.
-     *
-     * @param accumulated The accumulated.
-     */
-    public void setAccumulated(List<Measurement> accumulated) {
-        this.accumulated = accumulated;
-    }
-
-
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder("Measurements [");
-        for (int i = 0, M = measuments.size(); i < M; ++i) {
-            if (i > 0) sb.append(", ");
-            sb.append(measuments.get(i));
-        }
-        return sb.append(']').toString();
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/sq/Outlier.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,93 +0,0 @@
-package de.intevation.flys.artifacts.model.sq;
-
-import de.intevation.flys.artifacts.math.GrubbsOutlier;
-import de.intevation.flys.artifacts.math.StdDevOutlier;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.commons.math.MathException;
-
-import org.apache.log4j.Logger;
-
-public class Outlier
-{
-    private static Logger log = Logger.getLogger(Outlier.class);
-
-    private static final String GRUBBS = "outlier.method.grubbs";
-
-    //private static final String STD_DEV = "std-dev";
-
-    public interface Callback {
-
-        void initialize(List<SQ> sqs) throws MathException;
-
-        double eval(SQ sq);
-
-        void iterationFinished(
-            double   stdDev,
-            SQ       outlier,
-            List<SQ> remaining);
-
-    } // interface Callback
-
-    public static void detectOutliers(
-        Callback callback,
-        List<SQ> sqs,
-        double   stdDevFactor,
-        String   method
-    )
-    throws MathException
-    {
-        boolean debug = log.isDebugEnabled();
-
-        if (method == null) {
-            method = "std-dev";
-        }
-
-        if (debug) {
-            log.debug("stdDevFactor: " + stdDevFactor);
-            log.debug("method: " + method);
-        }
-
-        List<SQ> data = new ArrayList<SQ>(sqs);
-
-        double [] stdDev = new double[1];
-
-        boolean useGrubbs = method.equals(GRUBBS);
-
-        if (useGrubbs) {
-            stdDevFactor = Math.max(0d, Math.min(stdDevFactor/100d, 1d));
-        }
-
-        List<Double> values = new ArrayList<Double>(data.size());
-
-        while (data.size() > 2) {
-
-            callback.initialize(data);
-
-            for (SQ sq: data) {
-                values.add(callback.eval(sq));
-            }
-
-            Integer ndx = useGrubbs
-                ? GrubbsOutlier.findOutlier(values, stdDevFactor, stdDev)
-                : StdDevOutlier.findOutlier(values, stdDevFactor, stdDev);
-
-            if (ndx == null) {
-                callback.iterationFinished(stdDev[0], null, data);
-                break;
-            }
-
-            SQ outlier = data.remove(ndx.intValue());
-            if (debug) {
-                log.debug("stdDev: " + stdDev[0]);
-                log.debug("removed " + ndx +
-                    "; S: " + outlier.getS() + " Q: " + outlier.getQ());
-            }
-            callback.iterationFinished(stdDev[0], outlier, data);
-            values.clear();
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/sq/SQ.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +0,0 @@
-package de.intevation.flys.artifacts.model.sq;
-
-import java.io.Serializable;
-
-
-public class SQ implements Serializable {
-
-    protected double s;
-    protected double q;
-
-    public SQ() {
-    }
-
-    public SQ(double s, double q) {
-        this.s = s;
-        this.q = q;
-    }
-
-
-    public double getS() {
-        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);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/sq/SQCurveFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,103 +0,0 @@
-package de.intevation.flys.artifacts.model.sq;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.math.fitting.Function;
-import de.intevation.flys.artifacts.math.fitting.FunctionFactory;
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.DataFacet;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.Parameters;
-
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-
-/**
- * Facet to show the curve in a sq relation.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class SQCurveFacet extends DataFacet implements FacetTypes {
-
-    private static final Logger log = Logger.getLogger(SQCurveFacet.class);
-
-
-    public static final String FUNCTION = "sq-pow";
-
-
-    private int fractionIdx;
-
-
-    public SQCurveFacet() {
-    }
-
-
-    public SQCurveFacet(
-        int    idx,
-        int    fractionIdx,
-        String name,
-        String description,
-        String hash,
-        String stateId
-    ) {
-        super(idx, name, description, ComputeType.ADVANCE, hash, stateId);
-        this.fractionIdx = fractionIdx;
-    }
-
-
-    @Override
-    public Object getData(Artifact artifact, CallContext context) {
-        log.debug("SQCurveFacet.getData");
-
-        if (!(artifact instanceof FLYSArtifact)) {
-            return null;
-        }
-
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-
-        CalculationResult res = (CalculationResult) flys.compute(
-            context, ComputeType.ADVANCE, false);
-
-        SQResult[]       results = (SQResult[]) res.getData();
-        SQFractionResult result  = results[index].getFraction(fractionIdx);
-
-        Parameters params = result.getParameters();
-
-        if (params == null) {
-            log.debug("no parameters found");
-            return null;
-        }
-
-        Function func = FunctionFactory.getInstance().getFunction(FUNCTION);
-        String[] paramNames = func.getParameterNames();
-
-        double [] coeffs = params.get(0, paramNames);
-
-        if (log.isDebugEnabled()) {
-            for (int i = 0, N = paramNames.length; i < N; i++) {
-                log.debug("retrieved parameter " + paramNames[i] +
-                          " = " + coeffs[i]);
-            }
-        }
-
-        de.intevation.flys.artifacts.math.Function mf =
-            func.instantiate(coeffs);
-
-        double [] extent = result.getQExtent();
-        return new SQFunction(mf, extent[0], extent[1]);
-    }
-
-
-    @Override
-    public SQCurveFacet deepCopy() {
-        SQCurveFacet copy = new SQCurveFacet();
-        copy.set(this);
-
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/sq/SQFractionResult.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,166 +0,0 @@
-package de.intevation.flys.artifacts.model.sq;
-
-import de.intevation.flys.artifacts.model.Parameters;
-
-import java.io.Serializable;
-
-import java.util.List;
-
-public class SQFractionResult
-implements   Serializable
-{
-    public static class Iteration
-    implements          Serializable
-    {
-        protected Parameters parameters;
-        protected SQ []      measurements;
-        protected SQ []      outliers;
-
-        public Iteration() {
-        }
-
-        public Iteration(
-            Parameters parameters,
-            SQ []      measurements,
-            SQ []      outliers
-        ) {
-            this.parameters   = parameters;
-            this.measurements = measurements;
-            this.outliers     = outliers;
-        }
-
-        public Parameters getParameters() {
-            return parameters;
-        }
-
-        public void setParameters(Parameters parameters) {
-            this.parameters = parameters;
-        }
-
-        public SQ [] getMeasurements() {
-            return measurements;
-        }
-
-        public void setMeasurements(SQ [] measurements) {
-            this.measurements = measurements;
-        }
-
-        public SQ [] getOutliers() {
-            return outliers;
-        }
-
-        public void setOutliers(SQ [] outliers) {
-            this.outliers = outliers;
-        }
-
-        public boolean isValid() {
-            return parameters   != null
-                && measurements != null
-                && outliers     != null;
-        }
-
-        public int numOutliers() {
-            return outliers != null
-                ? outliers.length
-                : 0;
-        }
-
-        public int numMeasurements() {
-            return measurements != null
-                ? measurements.length
-                : 0;
-        }
-    } // class Iteration
-
-    protected SQ []           measurements;
-    protected List<Iteration> iterations;
-
-    public SQFractionResult() {
-    }
-
-    public SQFractionResult(
-        SQ []           measurements,
-        List<Iteration> iterations
-    ) {
-        this.measurements = measurements;
-        this.iterations   = iterations;
-    }
-
-    public SQ [] getMeasurements() {
-        return measurements;
-    }
-
-    public void setMeasurements(SQ [] measurements) {
-        this.measurements = measurements;
-    }
-
-    public List<Iteration> getIterations() {
-        return iterations;
-    }
-
-    public void setIterations(List<Iteration> iterations) {
-        this.iterations = iterations;
-    }
-
-    public double [] getQExtent() {
-        return getQExtent(new double[2]);
-    }
-
-    public double [] getQExtent(double extent[]) {
-        extent[0] =  Double.MAX_VALUE;
-        extent[1] = -Double.MIN_VALUE;
-
-        for (SQ sq: measurements) {
-            double q = sq.getQ();
-            if (q < extent[0]) extent[0] = q;
-            if (q > extent[1]) extent[1] = q;
-        }
-
-        return extent;
-    }
-
-    public int numIterations() {
-        return iterations != null ? iterations.size() : 0;
-    }
-
-    public Parameters getParameters() {
-        return iterations != null && !iterations.isEmpty()
-            ? iterations.get(iterations.size()-1).getParameters()
-            : null;
-    }
-
-    public SQ [] getOutliers(int index) {
-        return index >= 0 && index < iterations.size()
-            ? iterations.get(index).getOutliers()
-            : null;
-    }
-
-    public Parameters getParameters(int index) {
-        return index >= 0 && index < iterations.size()
-            ? iterations.get(index).getParameters()
-            : null;
-    }
-
-    public SQ [] getMeasurements(int index) {
-        return index >= 0 && index < iterations.size()
-            ? iterations.get(index).getMeasurements()
-            : null;
-    }
-
-    public int totalNumOutliers() {
-        int sum = 0;
-        if (iterations != null) {
-            for (Iteration iteration: iterations) {
-                sum += iteration.numOutliers();
-            }
-        }
-        return sum;
-    }
-
-    public int numMeasurements() {
-        return measurements != null
-            ? measurements.length
-            : 0;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/sq/SQFunction.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-package de.intevation.flys.artifacts.model.sq;
-
-import de.intevation.flys.artifacts.math.Function;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class SQFunction {
-
-    private Function function;
-
-    private double minQ;
-    private double maxQ;
-
-
-    public SQFunction(Function function, double minQ, double maxQ) {
-        this.function = function;
-        this.minQ     = minQ;
-        this.maxQ     = maxQ;
-    }
-
-
-    public Function getFunction() {
-        return function;
-    }
-
-    public double getMinQ() {
-        return minQ;
-    }
-
-    public double getMaxQ() {
-        return maxQ;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/sq/SQMeasurementFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,73 +0,0 @@
-package de.intevation.flys.artifacts.model.sq;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.DataFacet;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-
-/**
- * Facet to show the outliers in a sq relation.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class SQMeasurementFacet extends DataFacet implements FacetTypes {
-
-    private static final Logger log = Logger.getLogger(SQMeasurementFacet.class);
-
-
-    private int fractionIdx;
-
-
-    public SQMeasurementFacet() {
-    }
-
-
-    public SQMeasurementFacet(
-        int    idx,
-        int    fractionIdx,
-        String name,
-        String description,
-        String hash,
-        String stateId
-    ) {
-        super(idx, name, description, ComputeType.ADVANCE, hash, stateId);
-        this.fractionIdx = fractionIdx;
-    }
-
-
-    @Override
-    public Object getData(Artifact artifact, CallContext context) {
-        log.debug("SQMeasurementFacet.getData");
-
-        if (!(artifact instanceof FLYSArtifact)) {
-            return null;
-        }
-
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-
-        CalculationResult res = (CalculationResult) flys.compute(
-            context, ComputeType.ADVANCE, false);
-
-        SQResult[]       result  = (SQResult[]) res.getData();
-        SQFractionResult fResult = result[index].getFraction(fractionIdx);
-
-        return fResult.getMeasurements();
-    }
-
-
-    @Override
-    public SQMeasurementFacet deepCopy() {
-        SQMeasurementFacet copy = new SQMeasurementFacet();
-        copy.set(this);
-
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/sq/SQOutlierCurveFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,102 +0,0 @@
-package de.intevation.flys.artifacts.model.sq;
-
-import de.intevation.artifactdatabase.state.Facet;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.flys.artifacts.math.fitting.Function;
-import de.intevation.flys.artifacts.math.fitting.FunctionFactory;
-
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.DataFacet;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.Parameters;
-
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-import org.apache.log4j.Logger;
-
-public class SQOutlierCurveFacet
-extends      DataFacet
-implements   FacetTypes
-{
-    private static final Logger log =
-        Logger.getLogger(SQOutlierCurveFacet.class);
-
-    public static final String FUNCTION = "sq-pow";
-
-    public static final int BITMASK_ITERATION = (1 << 16) - 1;
-
-    private int fractionIdx;
-
-    public SQOutlierCurveFacet() {
-    }
-
-    public SQOutlierCurveFacet(
-        int    idx,
-        int    fractionIdx,
-        String name,
-        String description,
-        String hash,
-        String stateId
-    ) {
-        super(idx, name, description, ComputeType.ADVANCE, hash, stateId);
-        this.fractionIdx = fractionIdx;
-    }
-
-
-    @Override
-    public Object getData(Artifact artifact, CallContext context) {
-        log.debug("SQOutlierCurveFacet.getData");
-
-        if (!(artifact instanceof FLYSArtifact)) {
-            return null;
-        }
-
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-
-        CalculationResult res = (CalculationResult) flys.compute(
-            context, ComputeType.ADVANCE, false);
-
-        int idx  = this.index >> 16;
-        int iter = this.index & BITMASK_ITERATION;
-
-        SQResult[]       results = (SQResult[]) res.getData();
-        SQFractionResult result  = results[idx].getFraction(fractionIdx);
-
-        Parameters params = result.getParameters(iter);
-
-        if (params == null) {
-            return null;
-        }
-
-        Function func = FunctionFactory.getInstance().getFunction(FUNCTION);
-        String [] paramNames = func.getParameterNames();
-
-        double [] coeffs = params.get(0, paramNames);
-
-        if (log.isDebugEnabled()) {
-            for (int i = 0; i < paramNames.length; i++) {
-                log.debug("retrieved parameter " + paramNames[i] +
-                          " = " + coeffs[i]);
-            }
-        }
-
-        de.intevation.flys.artifacts.math.Function mf =
-            func.instantiate(coeffs);
-
-        double [] extent = result.getQExtent();
-        return new SQFunction(mf, extent[0], extent[1]);
-    }
-
-    @Override
-    public Facet deepCopy() {
-        SQOutlierCurveFacet copy = new SQOutlierCurveFacet();
-        copy.set(this);
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/sq/SQOutlierFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,97 +0,0 @@
-package de.intevation.flys.artifacts.model.sq;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.DataFacet;
-import de.intevation.flys.artifacts.model.FacetTypes;
-
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-import org.apache.log4j.Logger;
-
-/**
- * Facet to show the curve in a sq relation.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class SQOutlierFacet extends DataFacet implements FacetTypes {
-
-    private static final Logger log = Logger.getLogger(SQOutlierFacet.class);
-
-    public static final int BITMASK_ITERATION = (1 << 16) - 1;
-
-    private int fractionIdx;
-
-    public SQOutlierFacet() {
-    }
-
-    public SQOutlierFacet(
-        int    idx,
-        int    fractionIdx,
-        String name,
-        String description,
-        String hash,
-        String stateId
-    ) {
-        super(idx, name, description, ComputeType.ADVANCE, hash, stateId);
-        this.fractionIdx = fractionIdx;
-    }
-
-
-    @Override
-    public Object getData(Artifact artifact, CallContext context) {
-        log.debug("SQOutlierFacet.getData");
-
-        if (!(artifact instanceof FLYSArtifact)) {
-            return null;
-        }
-
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-
-        CalculationResult res = (CalculationResult) flys.compute(
-            context, ComputeType.ADVANCE, false);
-
-        int idx  = this.index >> 16;
-        int iter = this.index & BITMASK_ITERATION;
-
-        boolean debug = log.isDebugEnabled();
-
-        if (debug) {
-            log.debug("Fetch data for index : " + this.index);
-            log.debug("  > index:       " + idx);
-            log.debug("  > fraction:    " + fractionIdx);
-            log.debug("  > iteration:   " + iter);
-        }
-
-        SQResult[]       result  = (SQResult[]) res.getData();
-        SQFractionResult fResult = result[idx].getFraction(fractionIdx);
-
-        if (fResult == null) {
-            log.warn("No SQFractionResult at " + idx + "|" + fractionIdx);
-            return null;
-        }
-
-        SQ [] outliers = fResult.getOutliers(iter);
-
-        if (debug) {
-            int num = outliers != null ? outliers.length : 0;
-            log.debug("Found " + num + " outliers for iteration " + iter);
-        }
-
-        return outliers;
-    }
-
-
-    @Override
-    public SQOutlierFacet deepCopy() {
-        SQOutlierFacet copy = new SQOutlierFacet();
-        copy.set(this);
-
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/sq/SQOutlierMeasurementFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,73 +0,0 @@
-package de.intevation.flys.artifacts.model.sq;
-
-import de.intevation.artifactdatabase.state.Facet;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.DataFacet;
-import de.intevation.flys.artifacts.model.FacetTypes;
-
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-import org.apache.log4j.Logger;
-
-public class SQOutlierMeasurementFacet
-extends      DataFacet
-implements   FacetTypes
-{
-    private static final Logger log =
-        Logger.getLogger(SQOutlierMeasurementFacet.class);
-
-    private int fractionIdx;
-
-    public static final int BITMASK_ITERATION = (1 << 16) - 1;
-
-    public SQOutlierMeasurementFacet() {
-    }
-
-    public SQOutlierMeasurementFacet(
-        int    idx,
-        int    fractionIdx,
-        String name,
-        String description,
-        String hash,
-        String stateId
-    ) {
-        super(idx, name, description, ComputeType.ADVANCE, hash, stateId);
-        this.fractionIdx = fractionIdx;
-    }
-
-    @Override
-    public Object getData(Artifact artifact, CallContext context) {
-        log.debug("SQOutlierMeasurementFacet.getData");
-
-        if (!(artifact instanceof FLYSArtifact)) {
-            return null;
-        }
-
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-
-        CalculationResult res = (CalculationResult) flys.compute(
-            context, ComputeType.ADVANCE, false);
-
-        int idx  = this.index >> 16;
-        int iter = this.index & BITMASK_ITERATION;
-
-        SQResult[]       result  = (SQResult[]) res.getData();
-        SQFractionResult fResult = result[idx].getFraction(fractionIdx);
-
-        return fResult.getMeasurements(iter);
-    }
-
-    @Override
-    public Facet deepCopy() {
-        SQOutlierMeasurementFacet copy = new SQOutlierMeasurementFacet();
-        copy.set(this);
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/sq/SQOverviewFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +0,0 @@
-package de.intevation.flys.artifacts.model.sq;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.model.DataFacet;
-import de.intevation.flys.artifacts.model.FacetTypes;
-
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-
-public class SQOverviewFacet
-extends      DataFacet
-implements   FacetTypes
-{
-    private static final Logger logger = Logger.getLogger(SQOverviewFacet.class);
-
-    protected int fractionIdx;
-
-    public SQOverviewFacet() {
-    }
-
-    public SQOverviewFacet(
-        int    idx,
-        int    fractionIdx,
-        String name,
-        String description,
-        String hash,
-        String stateId
-    ) {
-        super(idx, name, description, ComputeType.ADVANCE, hash, stateId);
-        this.fractionIdx = fractionIdx;
-    }
-
-
-    @Override
-    public Object getData(Artifact artifact, CallContext context) {
-        logger.debug("SQOverviewFacet.getData");
-
-        switch(fractionIdx) {
-            case 0: return "sq_relation_a";
-            case 1: return "sq_relation_b";
-            case 2: return "sq_relation_c";
-            case 3: return "sq_relation_d";
-            case 4: return "sq_relation_e";
-            case 5: return "sq_relation_f";
-            default: return null;
-        }
-    }
-
-
-    @Override
-    public SQOverviewFacet deepCopy() {
-        SQOverviewFacet copy = new SQOverviewFacet();
-        copy.set(this);
-
-        return copy;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/sq/SQRelationCalculation.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,194 +0,0 @@
-package de.intevation.flys.artifacts.model.sq;
-
-import de.intevation.flys.artifacts.access.SQRelationAccess;
-
-import de.intevation.flys.artifacts.math.fitting.Function;
-import de.intevation.flys.artifacts.math.fitting.FunctionFactory;
-
-import de.intevation.flys.artifacts.model.Calculation;
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.DateRange;
-import de.intevation.flys.artifacts.model.Parameters;
-
-import de.intevation.flys.backend.SedDBSessionHolder;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-public class SQRelationCalculation extends Calculation {
-
-    private static final Logger log =
-        Logger.getLogger(SQRelationCalculation.class);
-
-    public static final String SQ_FUNCTION_NAME = "sq-pow";
-
-    protected String    river;
-    protected double    location;
-    protected DateRange period;
-    protected double    outliers;
-    private   String    method;
-
-    public SQRelationCalculation() {
-    }
-
-    public SQRelationCalculation(SQRelationAccess access) {
-
-        String    river    = access.getRiver();
-        Double    location = access.getLocation();
-        DateRange period   = access.getPeriod();
-        Double    outliers = access.getOutliers();
-        String    method   = access.getOutlierMethod();
-
-        //river = "Rhein";
-
-        if (river == null) {
-            // TODO: i18n
-            addProblem("sq.missing.river");
-        }
-
-        if (location == null) {
-            // TODO: i18n
-            addProblem("sq.missing.location");
-        }
-
-        if (period == null) {
-            // TODO: i18n
-            addProblem("sq.missing.periods");
-        }
-
-        if (outliers == null) {
-            // TODO: i18n
-            addProblem("sq.missing.outliers");
-        }
-
-        if (method == null) {
-            //TODO: i18n
-            addProblem("sq.missing.method");
-        }
-
-        if (!hasProblems()) {
-            this.river    = river;
-            this.location = location;
-            this.period   = period;
-            this.outliers = outliers;
-            this.method   = method;
-        }
-    }
-
-
-    public CalculationResult calculate() {
-        log.debug("SQRelationCalculation.calculate");
-
-        if (hasProblems()) {
-            return new CalculationResult(this);
-        }
-
-        SedDBSessionHolder.acquire();
-        try {
-            return internalCalculate();
-        }
-        finally {
-            SedDBSessionHolder.release();
-        }
-    }
-
-    protected CalculationResult internalCalculate() {
-
-        Function function = FunctionFactory
-            .getInstance()
-            .getFunction(SQ_FUNCTION_NAME);
-
-        if (function == null) {
-            log.error("No '" + SQ_FUNCTION_NAME + "' function found.");
-            // TODO: i18n
-            addProblem("sq.missing.sq.function");
-        }
-
-        Measurements measurements =
-            MeasurementFactory.getMeasurements(river, location, period);
-
-        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);
-
-            if (iterations == null) {
-                // TODO: i18n
-                addProblem("sq.fitting.failed." + i);
-                fractionResult = new SQFractionResult();
-            }
-            else {
-                fractionResult = new SQFractionResult(
-                    sqs.toArray(new SQ[sqs.size()]),
-                    iterations);
-            }
-
-            fractionResults[i] = fractionResult;
-        }
-
-        return new CalculationResult(
-            new SQResult[] { new SQResult(location, fractionResults) },
-            this);
-    }
-
-    protected List<SQFractionResult.Iteration> doFitting(
-        final Function function,
-        List<SQ> sqs
-    ) {
-        final List<SQFractionResult.Iteration> iterations =
-            new ArrayList<SQFractionResult.Iteration>();
-
-        boolean success = new Fitting(function, outliers).fit(
-            sqs,
-            method,
-            new Fitting.Callback() {
-                @Override
-                public void afterIteration(
-                    double [] coeffs,
-                    SQ []     measurements,
-                    SQ []     outliers,
-                    double    standardDeviation,
-                    double    chiSqr
-                ) {
-                    Parameters parameters = createParameters(
-                        function.getParameterNames(),
-                        coeffs,
-                        standardDeviation,
-                        chiSqr);
-                    iterations.add(new SQFractionResult.Iteration(
-                        parameters,
-                        measurements,
-                        outliers));
-                }
-            });
-
-        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;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/sq/SQResult.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,60 +0,0 @@
-package de.intevation.flys.artifacts.model.sq;
-
-import java.io.Serializable;
-
-
-
-public class SQResult implements Serializable {
-
-    public static final int NUMBER_FRACTIONS = 6;
-
-    public static final int FRACTION_A = 0;
-    public static final int FRACTION_B = 1;
-    public static final int FRACTION_C = 2;
-    public static final int FRACTION_D = 3;
-    public static final int FRACTION_E = 4;
-    public static final int FRACTION_F = 5;
-
-    protected double km;
-    protected SQFractionResult[] fractions;
-
-    public SQResult() {
-        this(0d, new SQFractionResult[NUMBER_FRACTIONS]);
-    }
-
-    public SQResult(double km, SQFractionResult [] fractions) {
-        this.km        = km;
-        this.fractions = fractions;
-    }
-
-    public SQFractionResult getFraction(int idx) {
-        return idx >= 0 && idx < fractions.length
-            ? fractions[idx]
-            : null;
-    }
-
-    public void setFraction(int idx, SQFractionResult fraction) {
-        if (idx >= 0 && idx < fractions.length) {
-            this.fractions[idx] = fraction;
-        }
-    }
-
-    public static final String [] FRACTION_NAMES = {
-        "A", "B", "C", "D", "E", "F"
-    };
-
-    public String getFractionName(int idx) {
-        return idx >= 0 && idx < FRACTION_NAMES.length
-            ? FRACTION_NAMES[idx]
-            : "";
-    }
-
-    public double getKm() {
-        return km;
-    }
-
-    public void setKm(double km) {
-        this.km = km;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/sq/Sieve.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,79 +0,0 @@
-package de.intevation.flys.artifacts.model.sq;
-
-import java.util.Comparator;
-
-public class Sieve
-{
-    public static final double EPSILON = 1e-6;
-
-    public static final Comparator<Double> DIAMETER_CMP =
-        new Comparator<Double>() {
-            @Override
-            public int compare(Double a, Double b) {
-                double diff = a - b;
-                if (diff < -EPSILON) return -1;
-                if (diff >  EPSILON) return +1;
-                return 0;
-            }
-        };
-
-    protected double diameter;
-    protected double load;
-
-    /**
-     * Constructs a new instance.
-     */
-    public Sieve() {
-        this(Double.NaN, Double.NaN);
-    }
-
-    public Sieve(double diameter, double load) {
-        this.diameter = diameter;
-        this.load = load;
-    }
-
-    /**
-     * Gets the diameter for this instance.
-     *
-     * @return The diameter.
-     */
-    public double getDiameter() {
-        return this.diameter;
-    }
-
-    /**
-     * Sets the diameter for this instance.
-     *
-     * @param diameter The diameter.
-     */
-    public void setDiameter(double diameter) {
-        this.diameter = diameter;
-    }
-
-    /**
-     * Gets the load for this instance.
-     *
-     * @return The load.
-     */
-    public double getLoad() {
-        return this.load;
-    }
-
-    /**
-     * Sets the load for this instance.
-     *
-     * @param load The load.
-     */
-    public void setLoad(double load) {
-        this.load = load;
-    }
-
-    public boolean matchesDiameter(double diameter) {
-        return Math.abs(diameter - this.diameter) < EPSILON;
-    }
-
-    public boolean hasDiameter() {
-        return !Double.isNaN(diameter);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/sq/SieveArray.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,129 +0,0 @@
-package de.intevation.flys.artifacts.model.sq;
-
-import java.util.Arrays;
-
-import org.apache.log4j.Logger;
-
-public class SieveArray
-{
-    private static final Logger log =
-        Logger.getLogger(SieveArray.class);
-
-    public static final double EPSILON = 1e-8;
-
-    public static final double [] SIEVE_DIAMETERS = {
-        100d,   63d,  31.5d,    16d,
-          8d,    4d,     2d,     1d,
-        0.5d, 0.25d, 0.125d, 0.063d
-    };
-
-    protected double [] loads;
-    protected double [] normLoads;
-
-    public SieveArray() {
-        loads = new double[SIEVE_DIAMETERS.length+1];
-        normLoads = new double[SIEVE_DIAMETERS.length+1];
-    }
-
-    public void doSieving(Sieve s) {
-
-        double diameter = s.getDiameter();
-
-        for (int i = 0; i < SIEVE_DIAMETERS.length; ++i) {
-            if (diameter >= SIEVE_DIAMETERS[i]) {
-                loads[i] += s.getLoad();
-                return;
-            }
-        }
-        loads[loads.length-1] += s.getLoad();
-    }
-
-    public double totalLoad() {
-        double sum = 0d;
-        for (double load: loads) {
-            sum += load;
-        }
-        return sum;
-    }
-
-    public void calculateNormLoads() {
-        double total = totalLoad();
-        if (Math.abs(total) < EPSILON) {
-            System.arraycopy(loads, 0, normLoads, 0, loads.length);
-            return;
-        }
-        total = 1d/total;
-        for (int i = 0; i < normLoads.length; ++i) {
-            normLoads[i] = total*loads[i];
-        }
-        log.debug("calculated norm loads: " + Arrays.toString(normLoads));
-    }
-
-    public void adjust(double eight, double four) {
-        this.normLoads[4] = eight;
-        this.normLoads[5] = four;
-    }
-
-    /**
-     * Gets the loads for this instance.
-     *
-     * @return The loads.
-     */
-    public double[] getLoads() {
-        return this.loads;
-    }
-
-    /**
-     * Gets the loads for this instance.
-     *
-     * @param index The index to get.
-     * @return The loads.
-     */
-    public double getLoads(int index) {
-        return this.loads[index];
-    }
-
-    /**
-     * Gets the normLoads for this instance.
-     *
-     * @return The normLoads.
-     */
-    public double[] getNormLoads() {
-        return this.normLoads;
-    }
-
-    /**
-     * Gets the normLoads for this instance.
-     *
-     * @param index The index to get.
-     * @return The normLoads.
-     */
-    public double getNormLoads(int index) {
-        return this.normLoads[index];
-    }
-
-    public double sandNormFraction() {
-        double sum = 0d;
-        for (int i = 7; i < normLoads.length; ++i) {
-            sum += normLoads[i];
-        }
-        return sum;
-    }
-
-    public double coarseNormFraction() {
-        double sum = 0d;
-        for (int i = 0; i < 4; ++i) {
-            sum += normLoads[i];
-        }
-        return sum;
-    }
-
-    public double gravelNormFraction() {
-        double sum = 0d;
-        for (int i = 4; i < 7; ++i) {
-            sum += normLoads[i];
-        }
-        return sum;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/sq/StaticSQCacheKey.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,30 +0,0 @@
-package de.intevation.flys.artifacts.model.sq;
-
-import java.io.Serializable;
-
-
-public class StaticSQCacheKey
-implements Serializable
-{
-    public static final String CACHE_NAME = "static-sq-relation";
-
-    private String river;
-    private int measurementId;
-
-    public StaticSQCacheKey(String river, int measurementId) {
-        this.river = river;
-        this.measurementId = measurementId;
-    }
-
-    public int hashCode() {
-        return this.river.hashCode() | measurementId;
-    }
-
-    public boolean equals(Object other) {
-        if (!(other instanceof StaticSQCacheKey)) {
-            return false;
-        }
-        StaticSQCacheKey o = (StaticSQCacheKey) other;
-        return this.river == o.river && this.measurementId == o.measurementId;
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/sq/StaticSQContainer.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,85 +0,0 @@
-package de.intevation.flys.artifacts.model.sq;
-
-import java.util.ArrayList;
-import java.util.List;
-
-
-public class StaticSQContainer
-{
-    private String description;
-    private String stationName;
-    private double km;
-
-    private List<StaticSQRelation> relations;
-
-
-    public StaticSQContainer() {
-        relations = new ArrayList<StaticSQRelation>();
-    }
-
-    public StaticSQContainer(
-        String stationName,
-        String description,
-        double km
-    ) {
-        this.stationName = stationName;
-        this.description = description;
-        this.km = km;
-    }
-
-    public String getDescription() {
-        return description;
-    }
-
-    public void setDescription(String description) {
-        this.description = description;
-    }
-
-    public String getStationName() {
-        return stationName;
-    }
-
-    public void setStationName(String stationName) {
-        this.stationName = stationName;
-    }
-
-    public double getKm() {
-        return km;
-    }
-
-    public void setKm(double km) {
-        this.km = km;
-    }
-
-    public List<StaticSQRelation> getSQRelations() {
-        return relations;
-    }
-
-    public void setSQRelations(List<StaticSQRelation> relations) {
-        this.relations = relations;
-    }
-
-    public void addSQRelation(StaticSQRelation relation) {
-        this.relations.add(relation);
-    }
-
-    public StaticSQRelation getSQRelation(int ndx) {
-        return this.relations.get(ndx);
-    }
-
-    public int size() {
-        return this.relations.size();
-    }
-
-    public List<StaticSQRelation> getRelationsByParameter(
-        StaticSQRelation.Parameter parameter
-    ) {
-        List<StaticSQRelation> result = new ArrayList<StaticSQRelation>();
-        for (StaticSQRelation relation : relations) {
-            if (relation.getParameter() == parameter) {
-                result.add(relation);
-            }
-        }
-        return result;
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/sq/StaticSQFactory.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,123 +0,0 @@
-package de.intevation.flys.artifacts.model.sq;
-
-import java.math.BigDecimal;
-import java.util.Date;
-import java.util.List;
-
-import net.sf.ehcache.Cache;
-import net.sf.ehcache.Element;
-
-import org.apache.log4j.Logger;
-import org.hibernate.Query;
-import org.hibernate.Session;
-
-import de.intevation.flys.artifacts.cache.CacheFactory;
-import de.intevation.flys.backend.SessionHolder;
-
-
-public class StaticSQFactory
-{
-    private static final Logger log =
-        Logger.getLogger(StaticSQFactory.class);
-
-    public static final String SQL_SQ =
-        "SELECT " +
-            "sq.description AS description,"+
-            "ti.start_time  AS start_time," +
-            "ti.stop_time    AS stop_time, " +
-            "ms.name AS station_name, " +
-            "ms.station AS station_km, " +
-            "ms.measurement_type AS measurement_type, " +
-            "sqv.parameter AS parameter, " +
-            "sqv.a AS a, " +
-            "sqv.b AS b, " +
-            "sqv.qmax AS qmax " +
-        "FROM sq_relation sq " +
-            "JOIN time_intervals ti ON ti.id   = sq.time_interval_id " +
-            "JOIN rivers r ON r.id = sq.river_id " +
-            "JOIN sq_relation_value sqv ON sqv.sq_relation_id = sq.id " +
-            "JOIN measurement_station ms ON sqv.measurement_station_id = ms.id " +
-        "WHERE " +
-            "r.name = :river " +
-            "AND ms.id = :ms_id ";
-
-
-    private StaticSQFactory() {
-    }
-
-    public static StaticSQContainer getSQRelations(
-        String river,
-        int measurementStation
-    ) {
-        Cache cache = CacheFactory.getCache(StaticSQCacheKey.CACHE_NAME);
-
-        StaticSQCacheKey cacheKey;
-
-        if (cache != null) {
-            cacheKey = new StaticSQCacheKey(river, measurementStation);
-            Element element = cache.get(cacheKey);
-            if (element != null) {
-                log.debug("Got static bedheight values from cache");
-                return (StaticSQContainer)element.getValue();
-            }
-        }
-        else {
-            cacheKey = null;
-        }
-
-        StaticSQContainer values = getUncached(river, measurementStation);
-
-        if (values != null && cacheKey != null) {
-            log.debug("Store static sq relations in cache.");
-            Element element = new Element(cacheKey, values);
-            cache.put(element);
-        }
-        return values;
-    }
-
-    private static StaticSQContainer getUncached(
-        String river,
-        int measurementStation
-    ) {
-        Session session = SessionHolder.HOLDER.get();
-
-        Query query = session.createSQLQuery(SQL_SQ)
-            .addScalar("description")
-            .addScalar("start_time")
-            .addScalar("stop_time")
-            .addScalar("station_name")
-            .addScalar("station_km")
-            .addScalar("measurement_type")
-            .addScalar("parameter")
-            .addScalar("a")
-            .addScalar("b")
-            .addScalar("qmax");
-
-        query.setParameter("river", river);
-        query.setParameter("ms_id", measurementStation);
-
-        List<Object []> list = query.list();
-
-        if (list.isEmpty()) {
-            return new StaticSQContainer();
-        }
-
-        StaticSQContainer sq = new StaticSQContainer();
-        sq.setDescription((String)list.get(0)[0]);
-        sq.setStationName((String)list.get(0)[3]);
-        sq.setKm(((BigDecimal)list.get(0)[4]).doubleValue());
-
-        for (Object[] row : list) {
-            StaticSQRelation relation = new StaticSQRelation();
-            relation.setStartTime((Date)row[1]);
-            relation.setStopTime((Date)row[2]);
-            relation.setType((String)row[5]);
-            relation.setParameter((String)row[6]);
-            relation.setA(((BigDecimal)row[7]).doubleValue());
-            relation.setB(((BigDecimal)row[8]).doubleValue());
-            relation.setQmax(((BigDecimal)row[9]).doubleValue());
-            sq.addSQRelation(relation);
-        }
-        return sq;
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/sq/StaticSQRelation.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,103 +0,0 @@
-package de.intevation.flys.artifacts.model.sq;
-
-import java.io.Serializable;
-import java.util.Date;
-
-
-public class StaticSQRelation implements Serializable{
-
-    private Date startTime;
-    private Date stopTime;
-    private String type;
-    private Parameter parameter;
-    private double a;
-    private double b;
-    private double qmax;
-
-    public static enum Parameter {
-        A, B, C, D, E, F
-    }
-
-
-    public StaticSQRelation() {
-    }
-
-    public StaticSQRelation(
-        Date startTime,
-        Date stopTime,
-        String type,
-        Parameter parameter,
-        double a,
-        double b
-    ) {
-        this.startTime = startTime;
-        this.stopTime = stopTime;
-        this.type = type;
-        this.parameter = parameter;
-        this.a = a;
-        this.b = b;
-    }
-
-    public Date getStartTime() {
-        return startTime;
-    }
-
-    public void setStartTime(Date startTime) {
-        this.startTime = startTime;
-    }
-
-    public Date getStopTime() {
-        return stopTime;
-    }
-
-    public void setStopTime(Date stopTime) {
-        this.stopTime = stopTime;
-    }
-
-    public String getType() {
-        return type;
-    }
-
-    public void setType(String type) {
-        this.type = type;
-    }
-
-    public Parameter getParameter() {
-        return parameter;
-    }
-
-    public void setParameter(Parameter parameter) {
-        this.parameter = parameter;
-    }
-
-    public void setParameter(String parameter) {
-        if (parameter == null) {
-            return;
-        }
-        this.parameter = Parameter.valueOf(parameter);
-    }
-
-    public double getA() {
-        return a;
-    }
-
-    public void setA(double a) {
-        this.a = a;
-    }
-
-    public double getB() {
-        return b;
-    }
-
-    public void setB(double b) {
-        this.b = b;
-    }
-
-    public double getQmax() {
-        return qmax;
-    }
-
-    public void setQmax(double qmax) {
-        this.qmax = qmax;
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/resources/Resources.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,179 +0,0 @@
-package de.intevation.flys.artifacts.resources;
-
-import java.text.MessageFormat;
-import java.util.Locale;
-import java.util.MissingResourceException;
-import java.util.ResourceBundle;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.CallMeta;
-
-/**
- * This class provides methods for i18n.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class Resources {
-
-    /** The logger that is used in this class.*/
-    private static Logger logger = Logger.getLogger(Resources.class);
-
-    /** The singleton instance.*/
-    private static Resources INSTANCE;
-
-    /** The locales supported by this server.*/
-    protected Locale[] locales;
-
-    /**
-     * No instance of this class is necessary.
-     */
-    private Resources() {
-    }
-
-
-    /**
-     * Returns the locales supported by this server.
-     *
-     * @return the supported locales.
-     */
-    public synchronized Locale [] getLocales() {
-        if (locales == null) {
-            readLocales();
-        }
-
-        return locales;
-    }
-
-
-    /**
-     * Read the locales configured for this server.
-     */
-    protected void readLocales() {
-        // TODO IMPLEMENT ME
-
-        locales = new Locale[2];
-        locales[0] = Locale.GERMANY;
-        locales[1] = Locale.ENGLISH;
-    }
-
-
-    private static synchronized void ensureInstance() {
-        if (INSTANCE == null) {
-            INSTANCE = new Resources();
-        }
-    }
-
-
-    public static Locale getLocale(CallMeta meta) {
-        ensureInstance();
-
-        Locale[] locales = INSTANCE.getLocales();
-        return meta.getPreferredLocale(locales);
-    }
-
-
-    /**
-     * This method returns the translated value for <i>key</i> or <i>def</i> if
-     * <i>key</i> is not existing in the resource bundle.
-     *
-     * @param meta The CallMeta object of the request that contains the
-     * preferred locale.
-     * @param key The key that should be translated.
-     * @param def A default value that is returned, if <i>key</i> was not found.
-     *
-     * @return the translated message.
-     */
-    public static String getMsg(CallMeta meta, String key, String def) {
-        ensureInstance();
-
-        Locale[] locales = INSTANCE.getLocales();
-        Locale   locale  = meta.getPreferredLocale(locales);
-
-        return getMsg(locale, key, def);
-    }
-
-    public static String getMsg(
-            CallMeta meta,
-            String   key,
-            Object[] args
-            ) {
-        return getMsg(meta, key, key, args);
-    }
-
-    /**
-     * Returns a translated message based on a template specified by <i>key</i>
-     * that has necessary values to be filled in.
-     *
-     * @param meta The CallMeta object.
-     * @param key The key of the template in the resource bundle.
-     * @param def the default value if no template was found with <i>key</i>.
-     * @param args The arguments that are necessary for the template.
-     *
-     * @return a translated string.
-     */
-    public static String getMsg(
-            CallMeta meta,
-            String   key,
-            String   def,
-            Object[] args)
-    {
-        String template = getMsg(meta, key, (String)null);
-
-        if (template == null) {
-            return def;
-        }
-
-        return format(meta, template, args);
-    }
-
-    public static String format(
-            CallMeta   meta,
-            String     key,
-            String     def,
-            Object ... args
-            ) {
-        String template = getMsg(meta, key, (String)null);
-
-        if (template == null) {
-            template = def;
-        }
-
-        return format(meta, template, args);
-    }
-
-    /**
-     * Formats the given template using the arguments with respect of the
-     * appropriate locale given by the CallMeta instance.
-     */
-    public static String format(CallMeta meta, String templ, Object ... args) {
-        Locale locale = getLocale(meta);
-        MessageFormat mf = new MessageFormat(templ, locale);
-
-        return mf.format(args, new StringBuffer(), null).toString();
-    }
-
-    /**
-     * This method returns the translated value for <i>key</i> or <i>def</i> if
-     * <i>key</i> is not existing in the resource bundle.
-     *
-     * @param locale The locale.
-     * @param key The key that should be translated.
-     * @param def A default value that is returned, if <i>key</i> was not found.
-     *
-     * @return the translated message.
-     */
-    public static String getMsg(Locale locale, String key, String def) {
-        ResourceBundle bundle = ResourceBundle.getBundle("messages", locale);
-
-        try {
-            return bundle.getString(key);
-        }
-        catch (MissingResourceException mre) {
-            logger.warn("No message found for key: " + key);
-
-            return def;
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/AbstractChartService.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,162 +0,0 @@
-package de.intevation.flys.artifacts.services;
-
-import java.awt.Dimension;
-import java.awt.Transparency;
-import java.awt.image.BufferedImage;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-
-import javax.imageio.ImageIO;
-
-import org.apache.log4j.Logger;
-import org.jfree.chart.JFreeChart;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.NodeList;
-
-import de.intevation.artifactdatabase.DefaultService;
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.GlobalContext;
-import de.intevation.artifacts.Service;
-
-/** Serve chart. */
-public abstract class AbstractChartService extends DefaultService {
-
-    public static final int DEFAULT_WIDTH = 240;
-    public static final int DEFAULT_HEIGHT = 180;
-
-    public static final String DEFAULT_FORMAT = "png";
-
-    private static final Logger log = Logger
-        .getLogger(AbstractChartService.class);
-
-    // TODO: Load fancy image from resources.
-    public static final byte[] EMPTY = { (byte) 0x89, (byte) 0x50, (byte) 0x4e,
-        (byte) 0x47, (byte) 0x0d, (byte) 0x0a, (byte) 0x1a, (byte) 0x0a,
-        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x0d, (byte) 0x49,
-        (byte) 0x48, (byte) 0x44, (byte) 0x52, (byte) 0x00, (byte) 0x00,
-        (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
-        (byte) 0x01, (byte) 0x08, (byte) 0x00, (byte) 0x00, (byte) 0x00,
-        (byte) 0x00, (byte) 0x3a, (byte) 0x7e, (byte) 0x9b, (byte) 0x55,
-        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x73,
-        (byte) 0x52, (byte) 0x47, (byte) 0x42, (byte) 0x00, (byte) 0xae,
-        (byte) 0xce, (byte) 0x1c, (byte) 0xe9, (byte) 0x00, (byte) 0x00,
-        (byte) 0x00, (byte) 0x09, (byte) 0x70, (byte) 0x48, (byte) 0x59,
-        (byte) 0x73, (byte) 0x00, (byte) 0x00, (byte) 0x0b, (byte) 0x13,
-        (byte) 0x00, (byte) 0x00, (byte) 0x0b, (byte) 0x13, (byte) 0x01,
-        (byte) 0x00, (byte) 0x9a, (byte) 0x9c, (byte) 0x18, (byte) 0x00,
-        (byte) 0x00, (byte) 0x00, (byte) 0x07, (byte) 0x74, (byte) 0x49,
-        (byte) 0x4d, (byte) 0x45, (byte) 0x07, (byte) 0xdc, (byte) 0x04,
-        (byte) 0x04, (byte) 0x10, (byte) 0x30, (byte) 0x15, (byte) 0x7d,
-        (byte) 0x77, (byte) 0x36, (byte) 0x0b, (byte) 0x00, (byte) 0x00,
-        (byte) 0x00, (byte) 0x08, (byte) 0x74, (byte) 0x45, (byte) 0x58,
-        (byte) 0x74, (byte) 0x43, (byte) 0x6f, (byte) 0x6d, (byte) 0x6d,
-        (byte) 0x65, (byte) 0x6e, (byte) 0x74, (byte) 0x00, (byte) 0xf6,
-        (byte) 0xcc, (byte) 0x96, (byte) 0xbf, (byte) 0x00, (byte) 0x00,
-        (byte) 0x00, (byte) 0x0a, (byte) 0x49, (byte) 0x44, (byte) 0x41,
-        (byte) 0x54, (byte) 0x08, (byte) 0xd7, (byte) 0x63, (byte) 0xf8,
-        (byte) 0x0f, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x01,
-        (byte) 0x00, (byte) 0x1b, (byte) 0xb6, (byte) 0xee, (byte) 0x56,
-        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x49,
-        (byte) 0x45, (byte) 0x4e, (byte) 0x44, (byte) 0xae, (byte) 0x42,
-        (byte) 0x60, (byte) 0x82 };
-
-    private static final Output empty() {
-        return new Output(EMPTY, "image/png");
-    }
-
-    protected abstract JFreeChart createChart(Document data,
-        GlobalContext globalContext, CallMeta callMeta);
-
-    protected void init() {
-    };
-
-    protected void finish() {
-    };
-
-    @Override
-    public Service.Output process(Document data, GlobalContext globalContext,
-        CallMeta callMeta) {
-        log.debug("process");
-
-        init();
-        try {
-            JFreeChart chart = createChart(data, globalContext, callMeta);
-
-            if (chart == null) {
-                return empty();
-            }
-
-            Dimension extent = getExtent(data);
-            String format = getFormat(data);
-
-            return encode(chart, extent, format);
-        }
-        finally {
-            finish();
-        }
-    }
-
-    protected static Output encode(JFreeChart chart, Dimension extent,
-        String format) {
-        BufferedImage image = chart.createBufferedImage(extent.width,
-            extent.height, Transparency.BITMASK, null);
-
-        ByteArrayOutputStream out = new ByteArrayOutputStream();
-
-        try {
-            ImageIO.write(image, format, out);
-        }
-        catch (IOException ioe) {
-            log.warn("writing image failed", ioe);
-            return empty();
-        }
-
-        return new Output(out.toByteArray(), "image/" + format);
-    }
-
-    protected static Dimension getExtent(Document input) {
-
-        int width = DEFAULT_WIDTH;
-        int height = DEFAULT_HEIGHT;
-
-        NodeList extents = input.getElementsByTagName("extent");
-
-        if (extents.getLength() > 0) {
-            Element element = (Element) extents.item(0);
-            String w = element.getAttribute("width");
-            String h = element.getAttribute("height");
-
-            try {
-                width = Math.max(1, Integer.parseInt(w));
-            }
-            catch (NumberFormatException nfe) {
-                log.warn("width '" + w + "' is not a valid.");
-            }
-
-            try {
-                height = Math.max(1, Integer.parseInt(h));
-            }
-            catch (NumberFormatException nfe) {
-                log.warn("height '" + h + "' is not a valid");
-            }
-        }
-
-        return new Dimension(width, height);
-    }
-
-    protected static String getFormat(Document input) {
-        String format = DEFAULT_FORMAT;
-
-        NodeList formats = input.getElementsByTagName("format");
-
-        if (formats.getLength() > 0) {
-            String type = ((Element) formats.item(0)).getAttribute("type");
-            if (type.length() > 0) {
-                format = type;
-            }
-        }
-
-        return format;
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/BedKMChartService.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,302 +0,0 @@
-package de.intevation.flys.artifacts.services;
-
-import java.awt.Color;
-import java.awt.Dimension;
-import java.awt.Transparency;
-import java.awt.image.BufferedImage;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.util.Date;
-import java.util.List;
-
-import javax.imageio.ImageIO;
-
-import org.apache.log4j.Logger;
-import org.jfree.chart.ChartFactory;
-import org.jfree.chart.JFreeChart;
-import org.jfree.chart.axis.DateAxis;
-import org.jfree.chart.plot.PlotOrientation;
-import org.jfree.chart.plot.XYPlot;
-import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
-import org.jfree.data.xy.XYSeries;
-import org.jfree.data.xy.XYSeriesCollection;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.NodeList;
-
-import de.intevation.artifactdatabase.DefaultService;
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.GlobalContext;
-import de.intevation.artifacts.Service;
-import de.intevation.flys.artifacts.model.minfo.BedOverview;
-import de.intevation.flys.artifacts.model.minfo.BedOverviewFactory;
-import de.intevation.flys.artifacts.resources.Resources;
-import de.intevation.flys.backend.SedDBSessionHolder;
-import de.intevation.flys.utils.KMIndex;
-
-public class BedKMChartService extends DefaultService {
-
-    private static final long serialVersionUID = -4946194087923870485L;
-
-    private static final Logger log =
-        Logger.getLogger(BedKMChartService.class);
-
-    public static final int DEFAULT_WIDTH  = 240;
-    public static final int DEFAULT_HEIGHT = 180;
-
-    public static final String I18N_CHART_LABEL =
-        "bed.km.chart.label";
-
-    public static final String DEFAULT_CHART_LABEL =
-        "Measuring Points";
-
-    public static final String I18N_CHART_TITLE =
-        "bed.km.chart.title";
-
-    public static final String DEFAULT_CHART_TITLE =
-        "Measuring points";
-
-    public static final String I18N_KM_AXIS =
-        "bed.km.chart.km.axis";
-
-    public static final String DEFAULT_KM_AXIS =
-        "km";
-
-    public static final String I18N_DATE_AXIS =
-        "bed.km.chart.date.axis";
-
-    public static final String DEFAULT_DATE_AXIS =
-        "Date";
-
-    public static final String DEFAULT_FORMAT = "png";
-
-    // TODO: Load fancy image from resources.
-    public static final byte [] EMPTY = {
-        (byte)0x89, (byte)0x50, (byte)0x4e, (byte)0x47,
-        (byte)0x0d, (byte)0x0a, (byte)0x1a, (byte)0x0a,
-        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x0d,
-        (byte)0x49, (byte)0x48, (byte)0x44, (byte)0x52,
-        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01,
-        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01,
-        (byte)0x08, (byte)0x00, (byte)0x00, (byte)0x00,
-        (byte)0x00, (byte)0x3a, (byte)0x7e, (byte)0x9b,
-        (byte)0x55, (byte)0x00, (byte)0x00, (byte)0x00,
-        (byte)0x01, (byte)0x73, (byte)0x52, (byte)0x47,
-        (byte)0x42, (byte)0x00, (byte)0xae, (byte)0xce,
-        (byte)0x1c, (byte)0xe9, (byte)0x00, (byte)0x00,
-        (byte)0x00, (byte)0x09, (byte)0x70, (byte)0x48,
-        (byte)0x59, (byte)0x73, (byte)0x00, (byte)0x00,
-        (byte)0x0b, (byte)0x13, (byte)0x00, (byte)0x00,
-        (byte)0x0b, (byte)0x13, (byte)0x01, (byte)0x00,
-        (byte)0x9a, (byte)0x9c, (byte)0x18, (byte)0x00,
-        (byte)0x00, (byte)0x00, (byte)0x07, (byte)0x74,
-        (byte)0x49, (byte)0x4d, (byte)0x45, (byte)0x07,
-        (byte)0xdc, (byte)0x04, (byte)0x04, (byte)0x10,
-        (byte)0x30, (byte)0x15, (byte)0x7d, (byte)0x77,
-        (byte)0x36, (byte)0x0b, (byte)0x00, (byte)0x00,
-        (byte)0x00, (byte)0x08, (byte)0x74, (byte)0x45,
-        (byte)0x58, (byte)0x74, (byte)0x43, (byte)0x6f,
-        (byte)0x6d, (byte)0x6d, (byte)0x65, (byte)0x6e,
-        (byte)0x74, (byte)0x00, (byte)0xf6, (byte)0xcc,
-        (byte)0x96, (byte)0xbf, (byte)0x00, (byte)0x00,
-        (byte)0x00, (byte)0x0a, (byte)0x49, (byte)0x44,
-        (byte)0x41, (byte)0x54, (byte)0x08, (byte)0xd7,
-        (byte)0x63, (byte)0xf8, (byte)0x0f, (byte)0x00,
-        (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x00,
-        (byte)0x1b, (byte)0xb6, (byte)0xee, (byte)0x56,
-        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
-        (byte)0x49, (byte)0x45, (byte)0x4e, (byte)0x44,
-        (byte)0xae, (byte)0x42, (byte)0x60, (byte)0x82
-    };
-
-    private static final Output empty() {
-        return new Output(EMPTY, "image/png");
-    }
-
-    @Override
-    public Service.Output process(
-        Document      data,
-        GlobalContext globalContext,
-        CallMeta      callMeta
-    ) {
-        log.debug("SQKMChartService.process");
-
-        SedDBSessionHolder.acquire();
-        try {
-            return doProcess(data, globalContext, callMeta);
-        }
-        finally {
-            SedDBSessionHolder.HOLDER.get().close();
-            SedDBSessionHolder.release();
-        }
-    }
-
-    protected Service.Output doProcess(
-        Document      input,
-        GlobalContext globalContext,
-        CallMeta      callMeta
-    ) {
-        String    river  = getRiverName(input);
-        Dimension extent = getExtent(input);
-        String    format = getFormat(input);
-
-        if (river == null) {
-            log.warn("River invalid.");
-            return empty();
-        }
-
-        BedOverview overview = BedOverviewFactory.getOverview(river);
-
-        if (overview == null) {
-            log.warn("No overview found for river '" + river + "'");
-            return empty();
-        }
-
-        KMIndex<List<Date>> entries = overview.filter(BedOverview.ACCEPT);
-
-        JFreeChart chart = createChart(entries, river, callMeta);
-
-        return encode(chart, extent, format);
-    }
-
-    protected static Output encode(
-        JFreeChart chart,
-        Dimension  extent,
-        String     format
-    ) {
-        BufferedImage image = chart.createBufferedImage(
-            extent.width, extent.height,
-            Transparency.BITMASK,
-            null);
-
-        ByteArrayOutputStream out = new ByteArrayOutputStream();
-
-        try {
-            ImageIO.write(image, format, out);
-        }
-        catch (IOException ioe) {
-            log.warn("writing image failed", ioe);
-            return empty();
-        }
-
-        return new Output(out.toByteArray(), "image/" + format);
-    }
-
-    protected static JFreeChart createChart(
-        KMIndex<List<Date>> entries,
-        String      river,
-        CallMeta    callMeta
-    ) {
-
-        XYSeriesCollection dataset = new XYSeriesCollection();
-        String key = Resources.format(
-            callMeta, I18N_CHART_LABEL, DEFAULT_CHART_LABEL, river);
-
-        XYSeries series = new XYSeries(key);
-        for (KMIndex.Entry<List<Date>> e: entries) {
-            double km = e.getKm();
-            List<Date> ds = e.getValue();
-            for (Date d: ds) {
-                series.add(km, d.getTime());
-            }
-        }
-
-        dataset.addSeries(series);
-        String title = Resources.format(
-            callMeta, I18N_CHART_TITLE, DEFAULT_CHART_TITLE, river);
-
-        String kmAxis = Resources.getMsg(
-            callMeta, I18N_KM_AXIS, DEFAULT_KM_AXIS);
-
-        String dateAxis = Resources.getMsg(
-            callMeta, I18N_DATE_AXIS, DEFAULT_DATE_AXIS);
-
-        JFreeChart chart = ChartFactory.createXYLineChart(
-            title,
-            kmAxis,
-            dateAxis,
-            null,
-            PlotOrientation.VERTICAL,
-            true,
-            true,
-            false);
-
-        XYPlot plot = (XYPlot)chart.getPlot();
-
-        DateAxis dA = new DateAxis();
-        plot.setRangeAxis(dA);
-        plot.setDataset(0, dataset);
-
-        chart.setBackgroundPaint(Color.white);
-        plot.setBackgroundPaint(Color.white);
-        plot.setDomainGridlinePaint(Color.gray);
-        plot.setRangeGridlinePaint(Color.gray);
-        plot.setDomainGridlinesVisible(true);
-        plot.setRangeGridlinesVisible(true);
-        XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer) plot.getRenderer();
-
-        renderer.setSeriesPaint(0, Color.gray);
-        renderer.setSeriesLinesVisible(0, false);
-        renderer.setSeriesShapesVisible(0, true);
-        renderer.setDrawOutlines(true);
-        return chart;
-    }
-
-
-    protected static String getRiverName(Document input) {
-        NodeList rivers = input.getElementsByTagName("river");
-
-        if (rivers.getLength() == 0) {
-            return null;
-        }
-
-        String river = ((Element)rivers.item(0)).getAttribute("name");
-
-        return river.length() > 0 ? river : null;
-    }
-
-    protected static Dimension getExtent(Document input) {
-
-        int width  = DEFAULT_WIDTH;
-        int height = DEFAULT_HEIGHT;
-
-        NodeList extents = input.getElementsByTagName("extent");
-
-        if (extents.getLength() > 0) {
-            Element element = (Element)extents.item(0);
-            String w = element.getAttribute("width");
-            String h = element.getAttribute("height");
-
-            try {
-                width = Math.max(1, Integer.parseInt(w));
-            }
-            catch (NumberFormatException nfe) {
-                log.warn("width '" + w + "' is not a valid.");
-            }
-
-            try {
-                height = Math.max(1, Integer.parseInt(h));
-            }
-            catch (NumberFormatException nfe) {
-                log.warn("height '" + h + "' is not a valid");
-            }
-        }
-
-        return new Dimension(width, height);
-    }
-
-    protected static String getFormat(Document input) {
-        String format = DEFAULT_FORMAT;
-
-        NodeList formats = input.getElementsByTagName("format");
-
-        if (formats.getLength() > 0) {
-            String type = ((Element)formats.item(0)).getAttribute("type");
-            if (type.length() > 0) {
-                format = type;
-            }
-        }
-
-        return format;
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/BedloadKMChartService.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,302 +0,0 @@
-package de.intevation.flys.artifacts.services;
-
-import java.awt.Color;
-import java.awt.Dimension;
-import java.awt.Transparency;
-import java.awt.image.BufferedImage;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.util.Date;
-import java.util.List;
-
-import javax.imageio.ImageIO;
-
-import org.apache.log4j.Logger;
-import org.jfree.chart.ChartFactory;
-import org.jfree.chart.JFreeChart;
-import org.jfree.chart.axis.DateAxis;
-import org.jfree.chart.plot.PlotOrientation;
-import org.jfree.chart.plot.XYPlot;
-import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
-import org.jfree.data.xy.XYSeries;
-import org.jfree.data.xy.XYSeriesCollection;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.NodeList;
-
-import de.intevation.artifactdatabase.DefaultService;
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.GlobalContext;
-import de.intevation.artifacts.Service;
-import de.intevation.flys.artifacts.model.minfo.BedloadOverview;
-import de.intevation.flys.artifacts.model.minfo.BedloadOverviewFactory;
-import de.intevation.flys.artifacts.resources.Resources;
-import de.intevation.flys.backend.SedDBSessionHolder;
-import de.intevation.flys.utils.KMIndex;
-
-public class BedloadKMChartService extends DefaultService {
-
-    private static final long serialVersionUID = 4156704841305086495L;
-
-    private static final Logger log =
-        Logger.getLogger(BedloadKMChartService.class);
-
-    public static final int DEFAULT_WIDTH  = 240;
-    public static final int DEFAULT_HEIGHT = 180;
-
-    public static final String I18N_CHART_LABEL =
-        "bedload.km.chart.label";
-
-    public static final String DEFAULT_CHART_LABEL =
-        "Measuring Points";
-
-    public static final String I18N_CHART_TITLE =
-        "bedload.km.chart.title";
-
-    public static final String DEFAULT_CHART_TITLE =
-        "Measuring points";
-
-    public static final String I18N_KM_AXIS =
-        "bedload.km.chart.km.axis";
-
-    public static final String DEFAULT_KM_AXIS =
-        "km";
-
-    public static final String I18N_DATE_AXIS =
-        "bedload.km.chart.date.axis";
-
-    public static final String DEFAULT_DATE_AXIS =
-        "Date";
-
-    public static final String DEFAULT_FORMAT = "png";
-
-    // TODO: Load fancy image from resources.
-    public static final byte [] EMPTY = {
-        (byte)0x89, (byte)0x50, (byte)0x4e, (byte)0x47,
-        (byte)0x0d, (byte)0x0a, (byte)0x1a, (byte)0x0a,
-        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x0d,
-        (byte)0x49, (byte)0x48, (byte)0x44, (byte)0x52,
-        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01,
-        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01,
-        (byte)0x08, (byte)0x00, (byte)0x00, (byte)0x00,
-        (byte)0x00, (byte)0x3a, (byte)0x7e, (byte)0x9b,
-        (byte)0x55, (byte)0x00, (byte)0x00, (byte)0x00,
-        (byte)0x01, (byte)0x73, (byte)0x52, (byte)0x47,
-        (byte)0x42, (byte)0x00, (byte)0xae, (byte)0xce,
-        (byte)0x1c, (byte)0xe9, (byte)0x00, (byte)0x00,
-        (byte)0x00, (byte)0x09, (byte)0x70, (byte)0x48,
-        (byte)0x59, (byte)0x73, (byte)0x00, (byte)0x00,
-        (byte)0x0b, (byte)0x13, (byte)0x00, (byte)0x00,
-        (byte)0x0b, (byte)0x13, (byte)0x01, (byte)0x00,
-        (byte)0x9a, (byte)0x9c, (byte)0x18, (byte)0x00,
-        (byte)0x00, (byte)0x00, (byte)0x07, (byte)0x74,
-        (byte)0x49, (byte)0x4d, (byte)0x45, (byte)0x07,
-        (byte)0xdc, (byte)0x04, (byte)0x04, (byte)0x10,
-        (byte)0x30, (byte)0x15, (byte)0x7d, (byte)0x77,
-        (byte)0x36, (byte)0x0b, (byte)0x00, (byte)0x00,
-        (byte)0x00, (byte)0x08, (byte)0x74, (byte)0x45,
-        (byte)0x58, (byte)0x74, (byte)0x43, (byte)0x6f,
-        (byte)0x6d, (byte)0x6d, (byte)0x65, (byte)0x6e,
-        (byte)0x74, (byte)0x00, (byte)0xf6, (byte)0xcc,
-        (byte)0x96, (byte)0xbf, (byte)0x00, (byte)0x00,
-        (byte)0x00, (byte)0x0a, (byte)0x49, (byte)0x44,
-        (byte)0x41, (byte)0x54, (byte)0x08, (byte)0xd7,
-        (byte)0x63, (byte)0xf8, (byte)0x0f, (byte)0x00,
-        (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x00,
-        (byte)0x1b, (byte)0xb6, (byte)0xee, (byte)0x56,
-        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
-        (byte)0x49, (byte)0x45, (byte)0x4e, (byte)0x44,
-        (byte)0xae, (byte)0x42, (byte)0x60, (byte)0x82
-    };
-
-    private static final Output empty() {
-        return new Output(EMPTY, "image/png");
-    }
-
-    @Override
-    public Service.Output process(
-        Document      data,
-        GlobalContext globalContext,
-        CallMeta      callMeta
-    ) {
-        log.debug("SQKMChartService.process");
-
-        SedDBSessionHolder.acquire();
-        try {
-            return doProcess(data, globalContext, callMeta);
-        }
-        finally {
-            SedDBSessionHolder.HOLDER.get().close();
-            SedDBSessionHolder.release();
-        }
-    }
-
-    protected Service.Output doProcess(
-        Document      input,
-        GlobalContext globalContext,
-        CallMeta      callMeta
-    ) {
-        String    river  = getRiverName(input);
-        Dimension extent = getExtent(input);
-        String    format = getFormat(input);
-
-        if (river == null) {
-            log.warn("River invalid.");
-            return empty();
-        }
-
-        BedloadOverview overview = BedloadOverviewFactory.getOverview(river);
-
-        if (overview == null) {
-            log.warn("No overview found for river '" + river + "'");
-            return empty();
-        }
-
-        KMIndex<List<Date>> entries = overview.filter(BedloadOverview.ACCEPT);
-
-        JFreeChart chart = createChart(entries, river, callMeta);
-
-        return encode(chart, extent, format);
-    }
-
-    protected static Output encode(
-        JFreeChart chart,
-        Dimension  extent,
-        String     format
-    ) {
-        BufferedImage image = chart.createBufferedImage(
-            extent.width, extent.height,
-            Transparency.BITMASK,
-            null);
-
-        ByteArrayOutputStream out = new ByteArrayOutputStream();
-
-        try {
-            ImageIO.write(image, format, out);
-        }
-        catch (IOException ioe) {
-            log.warn("writing image failed", ioe);
-            return empty();
-        }
-
-        return new Output(out.toByteArray(), "image/" + format);
-    }
-
-    protected static JFreeChart createChart(
-        KMIndex<List<Date>> entries,
-        String      river,
-        CallMeta    callMeta
-    ) {
-
-        XYSeriesCollection dataset = new XYSeriesCollection();
-        String key = Resources.format(
-            callMeta, I18N_CHART_LABEL, DEFAULT_CHART_LABEL, river);
-
-        XYSeries series = new XYSeries(key);
-        for (KMIndex.Entry<List<Date>> e: entries) {
-            double km = e.getKm();
-            List<Date> ds = e.getValue();
-            for (Date d: ds) {
-                series.add(km, d.getTime());
-            }
-        }
-
-        dataset.addSeries(series);
-        String title = Resources.format(
-            callMeta, I18N_CHART_TITLE, DEFAULT_CHART_TITLE, river);
-
-        String kmAxis = Resources.getMsg(
-            callMeta, I18N_KM_AXIS, DEFAULT_KM_AXIS);
-
-        String dateAxis = Resources.getMsg(
-            callMeta, I18N_DATE_AXIS, DEFAULT_DATE_AXIS);
-
-        JFreeChart chart = ChartFactory.createXYLineChart(
-            title,
-            kmAxis,
-            dateAxis,
-            null,
-            PlotOrientation.VERTICAL,
-            true,
-            true,
-            false);
-
-        XYPlot plot = (XYPlot)chart.getPlot();
-
-        DateAxis dA = new DateAxis();
-        plot.setRangeAxis(dA);
-        plot.setDataset(0, dataset);
-
-        chart.setBackgroundPaint(Color.white);
-        plot.setBackgroundPaint(Color.white);
-        plot.setDomainGridlinePaint(Color.gray);
-        plot.setRangeGridlinePaint(Color.gray);
-        plot.setDomainGridlinesVisible(true);
-        plot.setRangeGridlinesVisible(true);
-        XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer) plot.getRenderer();
-
-        renderer.setSeriesPaint(0, Color.gray);
-        renderer.setSeriesLinesVisible(0, false);
-        renderer.setSeriesShapesVisible(0, true);
-        renderer.setDrawOutlines(true);
-        return chart;
-    }
-
-
-    protected static String getRiverName(Document input) {
-        NodeList rivers = input.getElementsByTagName("river");
-
-        if (rivers.getLength() == 0) {
-            return null;
-        }
-
-        String river = ((Element)rivers.item(0)).getAttribute("name");
-
-        return river.length() > 0 ? river : null;
-    }
-
-    protected static Dimension getExtent(Document input) {
-
-        int width  = DEFAULT_WIDTH;
-        int height = DEFAULT_HEIGHT;
-
-        NodeList extents = input.getElementsByTagName("extent");
-
-        if (extents.getLength() > 0) {
-            Element element = (Element)extents.item(0);
-            String w = element.getAttribute("width");
-            String h = element.getAttribute("height");
-
-            try {
-                width = Math.max(1, Integer.parseInt(w));
-            }
-            catch (NumberFormatException nfe) {
-                log.warn("width '" + w + "' is not a valid.");
-            }
-
-            try {
-                height = Math.max(1, Integer.parseInt(h));
-            }
-            catch (NumberFormatException nfe) {
-                log.warn("height '" + h + "' is not a valid");
-            }
-        }
-
-        return new Dimension(width, height);
-    }
-
-    protected static String getFormat(Document input) {
-        String format = DEFAULT_FORMAT;
-
-        NodeList formats = input.getElementsByTagName("format");
-
-        if (formats.getLength() > 0) {
-            String type = ((Element)formats.item(0)).getAttribute("type");
-            if (type.length() > 0) {
-                format = type;
-            }
-        }
-
-        return format;
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/CacheInvalidationService.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-package de.intevation.flys.artifacts.services;
-
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.GlobalContext;
-
-import de.intevation.artifacts.common.utils.XMLUtils;
-
-import de.intevation.artifactdatabase.XMLService;
-
-import de.intevation.flys.artifacts.cache.CacheFactory;
-
-import net.sf.ehcache.Cache;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.NodeList;
-
-public class CacheInvalidationService
-extends      XMLService
-{
-    @Override
-    public Document processXML(
-        Document      data,
-        GlobalContext globalContext,
-        CallMeta      callMeta
-    ) {
-        Document result = XMLUtils.newDocument();
-
-        Element all = result.createElement("caches");
-
-        NodeList caches = data.getElementsByTagName("cache");
-
-        for (int i = 0, C = caches.getLength(); i < C; ++i) {
-            Element c = (Element)caches.item(i);
-            String name = c.getAttribute("name");
-            Element e = result.createElement("cache");
-            e.setAttribute("name", name);
-            Cache cache = CacheFactory.getCache(name);
-            if (cache != null) {
-                cache.removeAll();
-                e.setTextContent("All elements removed.");
-            }
-            else {
-                e.setTextContent("Error: Cache not found.");
-            }
-            all.appendChild(e);
-        }
-
-        result.appendChild(all);
-
-        return result;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/CrossSectionKMService.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,242 +0,0 @@
-package de.intevation.flys.artifacts.services;
-
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.GlobalContext;
-
-import de.intevation.artifacts.common.utils.XMLUtils;
-
-import de.intevation.flys.artifacts.cache.CacheFactory;
-
-import de.intevation.flys.backend.SessionHolder;
-
-import de.intevation.flys.model.CrossSection;
-import de.intevation.flys.model.CrossSectionLine;
-
-import java.util.AbstractMap;
-import java.util.ArrayDeque;
-import java.util.Deque;
-import java.util.List;
-import java.util.Map;
-import java.util.NavigableMap;
-
-import java.util.concurrent.ConcurrentSkipListMap;
-
-import net.sf.ehcache.Cache;
-
-import org.apache.log4j.Logger;
-
-import org.hibernate.Query;
-import org.hibernate.Session;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.NodeList;
-
-
-/**
- * Service to find the next/previous km (measurement) of cross sections.
- * Looking at the query for a single cross-section id at a single km, the
- * service does the following:
- *
- * It returns the km itself if a measurement at that km was found and
- * the N nearest other measurement points in both directions.
- *
- * That means, you can pass N=0 to find out whether a measurement at given km
- * exists.
- *
- * If less than N neighbours exist in one direction, less are delivered
- * (e.g. given measurements at [0,2,3,4,5,7,8,9] a query for km=8, N=3 will
- * result in [4,5,7,8,9]).
- */
-public class CrossSectionKMService
-extends      FLYSService
-{
-    private static Logger logger =
-        Logger.getLogger(CrossSectionKMService.class);
-
-    public static final String CACHE_NAME = "cross-section-kms";
-
-
-    /** Trivial constructor. */
-    public CrossSectionKMService() {
-    }
-
-
-    /**
-     * @param data
-     */
-    @Override
-    public Document doProcess(
-        Document      data,
-        GlobalContext globalContext,
-        CallMeta      callMeta
-    ) {
-        logger.debug("CrossSectionKMService.doProcess");
-
-        NodeList crossSectionNodes =
-            data.getElementsByTagName("art:cross-section");
-
-        Document document = XMLUtils.newDocument();
-
-        Element all = document.createElement("cross-sections");
-
-        for (int i = 0, CS = crossSectionNodes.getLength(); i < CS; ++i) {
-            Element crossSectionElement = (Element)crossSectionNodes.item(i);
-
-            String idString = crossSectionElement.getAttribute("id");
-            String kmString = crossSectionElement.getAttribute("km");
-            String neighborsString = crossSectionElement.getAttribute("n");
-
-            if (idString.length() == 0 || kmString.length() == 0) {
-                logger.debug("missing attributes in cross-section element");
-                continue;
-            }
-
-            double  km;
-            Integer crossSectionId;
-            int     N = 2;
-
-            try {
-                km             = Double.parseDouble(kmString);
-                crossSectionId = Integer.valueOf(idString);
-
-                if (neighborsString.length() > 0) {
-                    N = Integer.parseInt(neighborsString);
-                }
-            }
-            catch (NumberFormatException nfe) {
-                logger.debug("converting number failed", nfe);
-                continue;
-            }
-
-            NavigableMap<Double, Integer> map = getKms(crossSectionId);
-
-            if (map == null) {
-                logger.debug("cannot find cross section " + crossSectionId);
-                continue;
-            }
-
-            Deque<Map.Entry<Double, Integer>> result =
-                nearestNeighbors(map, km, N);
-
-            if (!result.isEmpty()) {
-                Element csE = document.createElement("cross-section");
-                csE.setAttribute("id", idString);
-                for (Map.Entry<Double, Integer> entry: result) {
-                    Element lineE = document.createElement("line");
-                    lineE.setAttribute(
-                        "line-id", String.valueOf(entry.getValue()));
-                    lineE.setAttribute(
-                        "km", String.valueOf(entry.getKey()));
-                    csE.appendChild(lineE);
-                }
-                all.appendChild(csE);
-            }
-        }
-
-        document.appendChild(all);
-
-        return document;
-    }
-
-    public static NavigableMap<Double, Integer> getKms(int crossSectionId) {
-
-        Cache cache = CacheFactory.getCache(CACHE_NAME);
-
-        if (cache == null) {
-            return getUncached(crossSectionId);
-        }
-
-        NavigableMap<Double, Integer> map;
-
-        net.sf.ehcache.Element element = cache.get(crossSectionId);
-        if (element == null) {
-            map = getUncached(crossSectionId);
-            if (map != null) {
-                element = new net.sf.ehcache.Element(
-                    crossSectionId, map);
-                cache.put(element);
-            }
-        }
-        else {
-            map = (NavigableMap<Double, Integer>)element.getValue();
-        }
-
-        return map;
-    }
-
-
-    /**
-     * @param km  the kilometer from which to start searching for other
-     *            measurements
-     * @param N   number of neighboring measurements to find.
-     */
-    public static Deque<Map.Entry<Double, Integer>> nearestNeighbors(
-        NavigableMap<Double, Integer> map,
-        double                        km,
-        int                           N
-    ) {
-        Deque<Map.Entry<Double, Integer>> result =
-            new ArrayDeque<Map.Entry<Double, Integer>>(2*N);
-
-        Integer v = map.get(km);
-
-        if (v != null) {
-            result.add(new AbstractMap.SimpleEntry<Double, Integer>(km, v));
-        }
-
-        int i = 0;
-        for (Map.Entry<Double, Integer> entry:
-             map.headMap(km, false).descendingMap().entrySet()) {
-            if (i++ >= N) {
-                break;
-            }
-            result.addFirst(entry);
-        }
-
-        i = 0;
-        for (Map.Entry<Double, Integer> entry:
-             map.tailMap(km, false).entrySet()) {
-            if (i++ >= N) {
-                break;
-            }
-            result.addLast(entry);
-        }
-
-        return result;
-    }
-
-
-    /**
-     * @param crossSectionId id of queried cross-section (in db).
-     * @return Mapping from kilometer to db-id.
-     */
-    public static NavigableMap<Double, Integer> getUncached(
-        Integer crossSectionId
-    ) {
-        NavigableMap<Double, Integer> result =
-            new ConcurrentSkipListMap<Double, Integer>();
-
-        Session session = SessionHolder.HOLDER.get();
-        Query query = session.createQuery(
-            "from CrossSection where id=:id");
-        query.setParameter("id", crossSectionId);
-
-        List<CrossSection> crossSections = query.list();
-        if (crossSections.isEmpty()) {
-            return null;
-        }
-
-        CrossSection crossSection = crossSections.get(0);
-        List<CrossSectionLine> lines = crossSection.getLines();
-
-        for (CrossSectionLine line: lines) {
-            Double  km = line.getKm().doubleValue();
-            Integer id = line.getId();
-            result.put(km, id);
-        }
-
-        return result;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/DischargeInfoService.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,122 +0,0 @@
-package de.intevation.flys.artifacts.services;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.Date;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.GlobalContext;
-
-import de.intevation.artifacts.common.ArtifactNamespaceContext;
-import de.intevation.artifacts.common.utils.XMLUtils;
-
-import de.intevation.flys.artifacts.model.DischargeTables;
-import de.intevation.flys.model.Gauge;
-import de.intevation.flys.model.DischargeTable;
-import de.intevation.flys.model.TimeInterval;
-
-/**
- * This service provides information about discharges at a defined gauge.
- *
- * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
- */
-public class DischargeInfoService extends FLYSService {
-
-    /** The logger used in this service. */
-    private static Logger logger = Logger.getLogger(DischargeInfoService.class);
-
-    public static final String GAUGE_XPATH = "/art:gauge/text()";
-
-    public DischargeInfoService() {
-    }
-
-
-    @Override
-    public Document doProcess(
-        Document      data,
-        GlobalContext globalContext,
-        CallMeta      callMeta
-    ) {
-        logger.debug("DischargeInfoService.process");
-        logger.debug(XMLUtils.toString(data));
-
-        String gaugeNumber = XMLUtils.xpathString(
-            data, GAUGE_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);
-
-        long gn;
-        try {
-            gn = Long.parseLong(gaugeNumber);
-        }
-        catch (NumberFormatException nfe) {
-            logger.warn("Invalid gauge number. Cannot return discharg info.");
-            return XMLUtils.newDocument();
-        }
-
-        Gauge gauge = Gauge.getGaugeByOfficialNumber(gn);
-
-        logger.debug("Found gauge: " + gauge.getName());
-
-        return buildDocument(gauge);
-    }
-
-
-    protected Document buildDocument(Gauge gauge) {
-        Document result = XMLUtils.newDocument();
-
-        List<DischargeTable> tables =gauge.getDischargeTables();
-        Collections.sort(tables);
-
-        Element all = result.createElement("discharges");
-        for (DischargeTable dt: tables) {
-            Element discharge = result.createElement("discharge");
-            discharge.setAttribute("description", dt.getDescription());
-
-            // Get time interval.
-            TimeInterval ti = dt.getTimeInterval();
-
-            if (ti == null) {
-                logger.warn("DischargeTable has no TimeInterval set!");
-                continue;
-            }
-
-            Date startTime = ti.getStartTime();
-            Date stopTime = ti.getStopTime();
-
-            if (startTime != null) {
-                discharge.setAttribute("start", String.valueOf(startTime.getTime()));
-            }
-            else {
-                continue;
-            }
-
-            if (stopTime != null && dt.getKind() != DischargeTables.MASTER) {
-                discharge.setAttribute("end", String.valueOf(stopTime.getTime()));
-            }
-            else if (dt.getKind() == DischargeTables.MASTER) {
-                long now = System.currentTimeMillis();
-                discharge.setAttribute("end", String.valueOf(now));
-            }
-            else {
-                continue;
-            }
-
-            all.appendChild(discharge);
-        }
-        result.appendChild(all);
-        return result;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/DischargeTablesOverview.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,300 +0,0 @@
-package de.intevation.flys.artifacts.services;
-
-import java.awt.Color;
-import java.text.DateFormat;
-import java.text.ParseException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Date;
-import java.util.List;
-import java.util.Locale;
-
-import org.apache.log4j.Logger;
-import org.hibernate.Session;
-import org.jfree.chart.ChartFactory;
-import org.jfree.chart.JFreeChart;
-import org.jfree.chart.plot.Marker;
-import org.jfree.chart.plot.PlotOrientation;
-import org.jfree.chart.plot.XYPlot;
-import org.jfree.data.xy.XYSeries;
-import org.jfree.data.xy.XYSeriesCollection;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.NodeList;
-
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.GlobalContext;
-import de.intevation.flys.artifacts.model.DischargeTables;
-import de.intevation.flys.artifacts.model.GaugesFactory;
-import de.intevation.flys.artifacts.resources.Resources;
-import de.intevation.flys.backend.SessionHolder;
-import de.intevation.flys.model.DischargeTable;
-import de.intevation.flys.model.Gauge;
-import de.intevation.flys.model.MainValue;
-import de.intevation.flys.model.TimeInterval;
-
-
-/** Generate Discharge Table chart. */
-public class DischargeTablesOverview extends AbstractChartService {
-
-    private static final Logger log = Logger
-        .getLogger(DischargeTablesOverview.class);
-
-    private static final long serialVersionUID = 1L;
-
-    public static final String I18N_CHART_TITLE = "gauge.discharge.service.chart.title";
-    public static final String DEFAULT_CHART_TITLE = "Pegel: XXX";
-
-    public static final String I18N_CHART_X_AXIS_TITLE = "gauge.discharge.service.chart.x.title";
-    public static final String DEFAULT_X_AXIS_TITLE = "Q [m^3/s]";
-
-    public static final String I18N_CHART_Y_AXIS_TITLE = "gauge.discharge.service.chart.y.title";
-    public static final String DEFAULT_Y_AXIS_TITLE = "W [cm]";
-
-    public static final String I18N_CHART_SERIES_TITLE = "gauge.discharge.service.chart.series.title";
-    public static final String DEFAULT_CHART_SERIES_TITLE = "Abflusskurve";
-
-    public static final String I18N_CHART_SERIES_TITLE_MASTER = "gauge.discharge.service.chart.series.title.master";
-    public static final String DEFAULT_CHART_SERIES_TITLE_MASTER = "Aktuelle Abflusskurve";
-
-    public static final DateFormat DATE_FORMAT = DateFormat.getDateInstance(
-        DateFormat.SHORT, Locale.GERMANY);
-
-    private Session session;
-
-    @Override
-    protected void init() {
-        session = SessionHolder.acquire();
-    }
-
-    @Override
-    protected void finish() {
-        if (session != null) {
-            session.close();
-            SessionHolder.release();
-        }
-    }
-
-    protected JFreeChart createChart(Document data,
-        GlobalContext globalContext, CallMeta callMeta) {
-
-        Gauge gauge = extractGauge(data);
-
-        if (gauge == null) {
-            log.warn("Could not determine Gauge from request!");
-            return null;
-        }
-
-        log.info("create discharge chart for gauge '" + gauge.getName() + "'");
-        TimeInterval timerange = extractTimeInterval(data);
-
-        List<DischargeTable> dts = getDischargeTables(gauge, timerange);
-        XYSeriesCollection dataset = new XYSeriesCollection();
-
-        for (DischargeTable dt : dts) {
-            try {
-                XYSeries series = createSeries(callMeta, dt);
-                if (series != null) {
-                    dataset.addSeries(series);
-                }
-            }
-            catch (IllegalArgumentException iae) {
-                log.warn("unable to create discharge curve: "
-                    + iae.getMessage());
-            }
-        }
-
-        String title = Resources.format(callMeta, I18N_CHART_TITLE,
-            DEFAULT_CHART_TITLE, gauge.getName());
-
-        String xAxis = Resources.getMsg(callMeta, I18N_CHART_X_AXIS_TITLE,
-            DEFAULT_X_AXIS_TITLE);
-
-        String yAxis = Resources.format(callMeta, I18N_CHART_Y_AXIS_TITLE,
-            DEFAULT_Y_AXIS_TITLE);
-
-        JFreeChart chart = ChartFactory.createXYLineChart(title, xAxis, yAxis,
-            null, PlotOrientation.VERTICAL, true, true, false);
-
-        chart.setBackgroundPaint(Color.white);
-
-        XYPlot plot = (XYPlot) chart.getPlot();
-        plot.setDataset(0, dataset);
-        plot.setBackgroundPaint(Color.white);
-        plot.setDomainGridlinePaint(Color.gray);
-        plot.setRangeGridlinePaint(Color.gray);
-        plot.setDomainGridlinesVisible(true);
-        plot.setRangeGridlinesVisible(true);
-
-        applyMainValueMarkers(
-            plot,
-            gauge,
-            callMeta);
-
-        return chart;
-    }
-
-    protected XYSeries createSeries(CallMeta callMeta, DischargeTable dt)
-        throws IllegalArgumentException {
-
-        double[][] xy = null;
-
-        if (dt.getKind() == DischargeTables.MASTER) {
-            xy = DischargeTables.loadDischargeTableValues(dt,
-                DischargeTables.MASTER_SCALE);
-        }
-        else {
-            xy = DischargeTables.loadDischargeTableValues(dt,
-                DischargeTables.HISTORICAL_SCALE);
-        }
-
-        XYSeries series = new XYSeries(createSeriesTitle(callMeta, dt), false);
-        for (int i = 0, n = xy[0].length; i < n; i++) {
-            series.add(xy[0][i], xy[1][i]);
-        }
-
-        return series;
-    }
-
-
-    /** Add domain markers to plot that indicate mainvalues. */
-    protected static void applyMainValueMarkers(
-        XYPlot   plot,
-        Gauge    gauge,
-        CallMeta meta
-    ) {
-        String river = gauge.getRiver().getName();
-        double km    = gauge.getStation().doubleValue();
-
-        // Get Gauge s mainvalues.
-        List<MainValue> mainValues = gauge.getMainValues();
-        for (MainValue mainValue : mainValues) {
-            if (mainValue.getMainValue().getType().getName().equals("Q")) {
-                // Its a Q main value.
-                Marker m = FixingsKMChartService.createQSectorMarker(
-                    mainValue.getValue().doubleValue(),
-                    mainValue.getMainValue().getName());
-                plot.addDomainMarker(m);
-            }
-            else if (mainValue.getMainValue().getType().getName().equals("W")) {
-                // Its a W main value.
-                Marker m = FixingsKMChartService.createQSectorMarker(
-                    mainValue.getValue().doubleValue(),
-                    mainValue.getMainValue().getName());
-                plot.addRangeMarker(m);
-            }
-        }
-    }
-
-    protected String createSeriesTitle(CallMeta callMeta, DischargeTable dt)
-        throws IllegalArgumentException {
-        TimeInterval timeInterval = dt.getTimeInterval();
-
-        if (timeInterval == null) {
-            return Resources.format(callMeta, DEFAULT_CHART_SERIES_TITLE);
-        }
-
-        Date start = timeInterval.getStartTime();
-        Date end = timeInterval.getStopTime();
-
-        if (start != null && end != null) {
-            return Resources.format(callMeta, I18N_CHART_SERIES_TITLE,
-                DEFAULT_CHART_SERIES_TITLE, start, end);
-        }
-        else if (start != null) {
-            return Resources.format(callMeta, I18N_CHART_SERIES_TITLE_MASTER,
-                DEFAULT_CHART_SERIES_TITLE, start);
-        }
-        else {
-            throw new IllegalArgumentException(
-                "Missing start date of DischargeTable " + dt.getId());
-        }
-    }
-
-    protected Gauge extractGauge(Document data) {
-        NodeList gauges = data.getElementsByTagName("gauge");
-
-        if (gauges.getLength() > 0) {
-            String name = ((Element) gauges.item(0)).getAttribute("name");
-
-            try {
-                long officialNumber = Long.valueOf(name);
-                return Gauge.getGaugeByOfficialNumber(officialNumber);
-            }
-            catch (NumberFormatException nfe) {
-                // it seems, that the client uses the name of the gauge instead
-                // of its official number
-            }
-
-            if (name != null && name.length() > 0) {
-                return GaugesFactory.getGauge(name);
-            }
-        }
-
-        return null;
-    }
-
-    protected TimeInterval extractTimeInterval(Document data) {
-        NodeList timeranges = data.getElementsByTagName("timerange");
-
-        if (timeranges != null && timeranges.getLength() > 0) {
-            Element timerange = (Element) timeranges.item(0);
-
-            String lower = timerange.getAttribute("lower");
-            String upper = timerange.getAttribute("upper");
-
-            if (lower != null && upper != null) {
-                try {
-                    Date d1 = DATE_FORMAT.parse(lower);
-                    Date d2 = DATE_FORMAT.parse(upper);
-
-                    return new TimeInterval(d1, d2);
-                }
-                catch (ParseException pe) {
-                    log.warn("Wrong time format: " + pe.getMessage());
-                }
-            }
-        }
-
-        return null;
-    }
-
-    protected List<DischargeTable> getDischargeTables(Gauge gauge,
-        TimeInterval timerange) {
-        List<DischargeTable> all = gauge.getDischargeTables();
-        Collections.sort(all);
-
-        if (timerange == null) {
-            return all;
-        }
-
-        List<DischargeTable> dts = new ArrayList<DischargeTable>(all.size());
-        long startDate = timerange.getStartTime().getTime();
-        long stopDate = timerange.getStopTime().getTime();
-
-        for (DischargeTable dt : all) {
-            TimeInterval tmp = dt.getTimeInterval();
-            if (tmp == null) {
-                // this should never happen because all discharge tables should
-                // have a time interval set!
-                continue;
-            }
-
-            Date start = tmp.getStartTime();
-            Date stop = tmp.getStartTime();
-
-            if (start.getTime() > startDate && start.getTime() < stopDate) {
-                dts.add(dt);
-                continue;
-            }
-            else if (stop != null && stop.getTime() < stopDate
-                && stop.getTime() > startDate) {
-                dts.add(dt);
-                continue;
-            }
-        }
-
-        return dts;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/DistanceInfoService.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,141 +0,0 @@
-package de.intevation.flys.artifacts.services;
-
-import java.util.Iterator;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.GlobalContext;
-
-import de.intevation.artifacts.common.ArtifactNamespaceContext;
-import de.intevation.artifacts.common.utils.XMLUtils;
-
-import de.intevation.flys.model.FastAnnotations;
-
-import de.intevation.flys.artifacts.model.LocationProvider;
-
-
-/**
- * This service provides information about distances of a specified river.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class DistanceInfoService extends FLYSService {
-
-    /** The logger used in this service. */
-    private static Logger logger = Logger.getLogger(DistanceInfoService.class);
-
-    public static final String RIVER_XPATH = "/art:river/text()";
-
-    public static final String FILTER_XPATH = "/art:river/art:filter/text()";
-
-
-    /**
-     * The default constructor.
-     */
-    public DistanceInfoService() {
-    }
-
-
-    @Override
-    public Document doProcess(
-        Document      data,
-        GlobalContext globalContext,
-        CallMeta      callMeta
-    ) {
-        logger.debug("DistanceInfoService.process");
-
-        String river = XMLUtils.xpathString(
-            data, RIVER_XPATH, ArtifactNamespaceContext.INSTANCE);
-
-        String filterName  = XMLUtils.xpathString(
-            data, FILTER_XPATH, ArtifactNamespaceContext.INSTANCE);
-
-        if (river == null || (river = river.trim()).length() == 0) {
-            logger.warn("No river specified. Cannot return distance info!");
-            return XMLUtils.newDocument();
-        }
-
-        logger.debug("Search distances for river: " + river);
-
-        FastAnnotations fas = LocationProvider.getAnnotations(river);
-
-        FastAnnotations.Filter filter = selectFilter(filterName);
-
-        return buildDocument(fas.filter(filter));
-    }
-
-    protected Document buildDocument(
-        Iterator<FastAnnotations.Annotation> iter
-    ) {
-        Document result = XMLUtils.newDocument();
-
-        Element all = result.createElement("distances");
-
-        while (iter.hasNext()) {
-            all.appendChild(buildNode(result, iter.next()));
-        }
-
-        result.appendChild(all);
-
-        return result;
-    }
-
-    protected static FastAnnotations.Filter selectFilter(String name) {
-
-        if (name != null) {
-            if ("locations".equals(name)) return FastAnnotations.IS_POINT;
-            if ("distances".equals(name)) return FastAnnotations.IS_RANGE;
-            if ("measuringpoint".equals(name))
-                return new FastAnnotations.NameFilter("Messstelle");
-        }
-
-        return FastAnnotations.ALL;
-    }
-
-    /**
-     * Builds an Element for a distance info.
-     *
-     * @param an The Annotation that provides information about the distance.
-     *
-     * @return an Element that contains information about a distance.
-     */
-    protected static Element buildNode(
-        Document                   document,
-        FastAnnotations.Annotation an
-    ) {
-        Element distance = document.createElement("distance");
-
-        distance.setAttribute("description", an.getPosition());
-
-        String riverSide = an.getAttribute();
-
-        if (riverSide != null && riverSide.length() > 0) {
-            distance.setAttribute("riverside", riverSide);
-        }
-
-        distance.setAttribute("from", String.valueOf(an.getA()));
-
-        double b      = an.getB();
-        double bottom = an.getBottom();
-        double top    = an.getTop();
-
-        if (!Double.isNaN(b)) {
-            distance.setAttribute("to", String.valueOf(b));
-        }
-
-        if (!Double.isNaN(bottom)) {
-            distance.setAttribute("bottom", String.valueOf(bottom));
-        }
-
-        if (!Double.isNaN(top)) {
-            distance.setAttribute("top", String.valueOf(top));
-        }
-
-        return distance;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/FLYSService.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-package de.intevation.flys.artifacts.services;
-
-import org.w3c.dom.Document;
-
-import org.apache.log4j.Logger;
-
-import org.hibernate.Session;
-
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.GlobalContext;
-
-import de.intevation.artifactdatabase.XMLService;
-
-import de.intevation.flys.backend.SessionHolder;
-
-
-public abstract class FLYSService extends XMLService {
-
-    private static final Logger logger = Logger.getLogger(FLYSService.class);
-
-
-    @Override
-    public Document processXML(
-        Document      data,
-        GlobalContext globalContext,
-        CallMeta      callMeta
-    ) {
-        init();
-
-        try {
-            return doProcess(data, globalContext, callMeta);
-        }
-        finally {
-            shutdown();
-        }
-    }
-
-
-    protected abstract Document doProcess(
-        Document      data,
-        GlobalContext globalContext,
-        CallMeta      callMeta);
-
-
-    protected void init() {
-        logger.debug("init");
-        SessionHolder.acquire();
-    }
-
-
-    protected void shutdown() {
-        logger.debug("shutdown");
-        Session session = SessionHolder.HOLDER.get();
-        session.close();
-
-        SessionHolder.release();
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/FileUploadService.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,115 +0,0 @@
-package de.intevation.flys.artifacts.services;
-
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.GlobalContext;
-import de.intevation.artifacts.common.ArtifactNamespaceContext;
-import de.intevation.artifacts.common.utils.FileTools;
-import de.intevation.artifacts.common.utils.XMLUtils;
-import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
-import de.intevation.flys.utils.FLYSUtils;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-
-import org.apache.commons.codec.binary.Base64;
-import org.apache.log4j.Logger;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-/**
- * Service that receives XML-packed Shapefile bundles from the client.
- * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
- */
-public class FileUploadService extends FLYSService {
-
-    /** The logger used in this service.*/
-    private static Logger logger = Logger.getLogger(FileUploadService.class);
-
-    /** XPath that points to the artifact uuid.*/
-    public static final String XPATH_ARTIFACT_UUID =
-        "/upload/artifact-uuid/text()";
-
-    /** XPath that points to the base64 encoded data.*/
-    public static final String XPATH_DATA = "/upload/data/text()";
-
-    public FileUploadService() {
-    }
-
-    @Override
-    protected Document doProcess(
-        Document data,
-        GlobalContext context,
-        CallMeta callMeta
-    ) {
-        logger.debug("FileUploadService.process");
-
-        Document doc = XMLUtils.newDocument();
-        ElementCreator ec = new ElementCreator(doc, null, null);
-        Element resp   = ec.create("response");
-        Element status = ec.create("status");
-        resp.appendChild(status);
-        doc.appendChild(resp);
-
-        String uuid = extractUuid(data);
-
-        byte[] fileData = extractData(data);
-        if (fileData != null) {
-            try {
-                String shapePath = FLYSUtils.getXPathString(
-                    FLYSUtils.XPATH_FLOODMAP_SHAPEFILE_DIR);
-
-                File artifactDir = FileTools.getDirectory(shapePath, uuid);
-                FileOutputStream fos =
-                    new FileOutputStream(
-                    new File(artifactDir, "user-rgd.zip"));
-                try {
-                    fos.write(fileData);
-
-                    // Write operation successful
-                    status.setTextContent("Upload erfolgreich!"); // TODO: i18n
-                }
-                finally {
-                    fos.close();
-                }
-            }
-            catch (IOException ioe) {
-                logger.warn(ioe, ioe);
-                status.setTextContent("Upload fehlgeschlagen!");
-            }
-        }
-        else {
-            logger.debug("No data in uploaded xml.");
-            status.setTextContent("Upload fehlgeschlagen!");
-        }
-
-        return doc;
-    }
-
-    /**
-     * Extracts the UUID from the XML document.
-     * @param data
-     * @return
-     */
-    protected String extractUuid(Document data) {
-        return XMLUtils.xpathString(
-            data, XPATH_ARTIFACT_UUID, ArtifactNamespaceContext.INSTANCE);
-    }
-
-    /**
-     * Extracts the base64 encoded ZIP file from the XML document.
-     * @param data
-     * @return
-     */
-    protected byte[] extractData(Document data) {
-        String b64Data = XMLUtils.xpathString(
-            data, XPATH_DATA, ArtifactNamespaceContext.INSTANCE);
-
-        if (b64Data != null && b64Data.length() > 0) {
-           byte[] fileData = Base64.decodeBase64(b64Data);
-           return fileData;
-        }
-        return null;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/FixingsKMChartService.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,489 +0,0 @@
-package de.intevation.flys.artifacts.services;
-
-import de.intevation.artifactdatabase.DefaultService;
-
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.GlobalContext;
-import de.intevation.artifacts.Service;
-
-import de.intevation.flys.artifacts.model.FixingsColumn;
-import de.intevation.flys.artifacts.model.FixingsColumnFactory;
-import de.intevation.flys.artifacts.model.FixingsFilterBuilder;
-
-import de.intevation.flys.artifacts.model.FixingsOverview.Fixing;
-
-import de.intevation.flys.artifacts.model.FixingsOverview;
-import de.intevation.flys.artifacts.model.FixingsOverviewFactory;
-import de.intevation.flys.artifacts.model.GaugeFinder;
-import de.intevation.flys.artifacts.model.GaugeFinderFactory;
-import de.intevation.flys.artifacts.model.GaugeRange;
-
-import de.intevation.flys.artifacts.model.fixings.QWI;
-
-import de.intevation.flys.artifacts.resources.Resources;
-
-import de.intevation.flys.backend.SessionHolder;
-
-import de.intevation.flys.jfree.ShapeRenderer;
-
-import de.intevation.flys.utils.Formatter;
-import de.intevation.flys.utils.Pair;
-
-import java.awt.BasicStroke;
-import java.awt.Color;
-import java.awt.Dimension;
-import java.awt.Transparency;
-
-import java.awt.geom.Rectangle2D;
-
-import java.awt.image.BufferedImage;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.imageio.ImageIO;
-
-import org.apache.log4j.Logger;
-
-import org.jfree.chart.ChartFactory;
-import org.jfree.chart.JFreeChart;
-import org.jfree.chart.LegendItemCollection;
-
-import org.jfree.chart.axis.NumberAxis;
-
-import org.jfree.chart.plot.Marker;
-import org.jfree.chart.plot.PlotOrientation;
-import org.jfree.chart.plot.ValueMarker;
-import org.jfree.chart.plot.XYPlot;
-
-import org.jfree.data.Range;
-
-import org.jfree.ui.RectangleAnchor;
-import org.jfree.ui.TextAnchor;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.NodeList;
-
-
-/** Serve chart of Fixings at certain km. */
-public class FixingsKMChartService
-extends      DefaultService
-{
-    private static final Logger log =
-        Logger.getLogger(FixingsKMChartService.class);
-
-    public static final int DEFAULT_WIDTH  = 240;
-    public static final int DEFAULT_HEIGHT = 180;
-
-    public static final String [] I18N_Q_SECTOR_BOARDERS = {
-        "fix.km.chart.q.sector.border0",
-        "fix.km.chart.q.sector.border1",
-        "fix.km.chart.q.sector.border2"
-    };
-
-    public static final String [] DEFAULT_Q_SECTOR_BORDERS = {
-        "(MNQ + MQ)/2",
-        "(MQ + MHQ)/2",
-        "HQ5"
-    };
-
-    public static final String I18N_CHART_LABEL_DATE =
-        "fix.km.chart.label.date";
-
-    public static final String DEFAULT_CHART_LABEL_DATE =
-        "yyyy/MM/dd";
-
-    public static final String I18N_CHART_TITLE =
-        "fix.km.chart.title";
-
-    public static final String DEFAULT_CHART_TITLE =
-        "Fixings {0} km {1,number,#.###}";
-
-    public static final String I18N_Q_AXIS =
-        "fix.km.chart.q.axis";
-
-    public static final String DEFAULT_Q_AXIS =
-        "Q [m\u00b3/s]";
-
-    public static final String I18N_W_AXIS =
-        "fix.km.chart.w.axis";
-
-    public static final String DEFAULT_W_AXIS =
-        "W [NN + m]";
-
-    public static final String I18N_MEASURED =
-        "fix.km.chart.measured";
-
-    public static final String DEFAULT_MEASURED =
-        "measured";
-
-    public static final String I18N_INTERPOLATED =
-        "fix.km.chart.interpolated";
-
-    public static final String DEFAULT_INTERPOLATED =
-        "interpolated";
-
-    public static final String DEFAULT_FORMAT = "png";
-
-    // TODO: Load fancy image from resources.
-    public static final byte [] EMPTY = {
-        (byte)0x89, (byte)0x50, (byte)0x4e, (byte)0x47,
-        (byte)0x0d, (byte)0x0a, (byte)0x1a, (byte)0x0a,
-        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x0d,
-        (byte)0x49, (byte)0x48, (byte)0x44, (byte)0x52,
-        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01,
-        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01,
-        (byte)0x08, (byte)0x00, (byte)0x00, (byte)0x00,
-        (byte)0x00, (byte)0x3a, (byte)0x7e, (byte)0x9b,
-        (byte)0x55, (byte)0x00, (byte)0x00, (byte)0x00,
-        (byte)0x01, (byte)0x73, (byte)0x52, (byte)0x47,
-        (byte)0x42, (byte)0x00, (byte)0xae, (byte)0xce,
-        (byte)0x1c, (byte)0xe9, (byte)0x00, (byte)0x00,
-        (byte)0x00, (byte)0x09, (byte)0x70, (byte)0x48,
-        (byte)0x59, (byte)0x73, (byte)0x00, (byte)0x00,
-        (byte)0x0b, (byte)0x13, (byte)0x00, (byte)0x00,
-        (byte)0x0b, (byte)0x13, (byte)0x01, (byte)0x00,
-        (byte)0x9a, (byte)0x9c, (byte)0x18, (byte)0x00,
-        (byte)0x00, (byte)0x00, (byte)0x07, (byte)0x74,
-        (byte)0x49, (byte)0x4d, (byte)0x45, (byte)0x07,
-        (byte)0xdc, (byte)0x04, (byte)0x04, (byte)0x10,
-        (byte)0x30, (byte)0x15, (byte)0x7d, (byte)0x77,
-        (byte)0x36, (byte)0x0b, (byte)0x00, (byte)0x00,
-        (byte)0x00, (byte)0x08, (byte)0x74, (byte)0x45,
-        (byte)0x58, (byte)0x74, (byte)0x43, (byte)0x6f,
-        (byte)0x6d, (byte)0x6d, (byte)0x65, (byte)0x6e,
-        (byte)0x74, (byte)0x00, (byte)0xf6, (byte)0xcc,
-        (byte)0x96, (byte)0xbf, (byte)0x00, (byte)0x00,
-        (byte)0x00, (byte)0x0a, (byte)0x49, (byte)0x44,
-        (byte)0x41, (byte)0x54, (byte)0x08, (byte)0xd7,
-        (byte)0x63, (byte)0xf8, (byte)0x0f, (byte)0x00,
-        (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x00,
-        (byte)0x1b, (byte)0xb6, (byte)0xee, (byte)0x56,
-        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
-        (byte)0x49, (byte)0x45, (byte)0x4e, (byte)0x44,
-        (byte)0xae, (byte)0x42, (byte)0x60, (byte)0x82
-    };
-
-    private static final Output empty() {
-        return new Output(EMPTY, "image/png");
-    }
-
-    @Override
-    public Service.Output process(
-        Document      data,
-        GlobalContext globalContext,
-        CallMeta      callMeta
-    ) {
-        log.debug("FixingsKMChartService.process");
-
-        SessionHolder.acquire();
-        try {
-            return doProcess(data, globalContext, callMeta);
-        }
-        finally {
-            SessionHolder.HOLDER.get().close();
-            SessionHolder.release();
-        }
-    }
-
-    protected Service.Output doProcess(
-        Document      input,
-        GlobalContext globalContext,
-        CallMeta      callMeta
-    ) {
-        String    river  = getRiverName(input);
-        Double    km     = getKM(input);
-        Dimension extent = getExtent(input);
-        String    format = getFormat(input);
-
-        if (river == null || km == null) {
-            log.warn("River and/or km invalid.");
-            return empty();
-        }
-
-        FixingsOverview overview = FixingsOverviewFactory.getOverview(river);
-
-        if (overview == null) {
-            log.warn("No overview found for river '" + river + "'");
-            return empty();
-        }
-
-        FixingsFilterBuilder ffb = new FixingsFilterBuilder(input);
-
-        List<Fixing.Column> columns = overview.filter(
-            ffb.getRange(),
-            ffb.getFilter());
-
-        List<Pair<Fixing.Column, FixingsColumn>> cols =
-            new ArrayList<Pair<Fixing.Column, FixingsColumn>>();
-
-        for (Fixing.Column col: columns) {
-            FixingsColumn data =
-                FixingsColumnFactory.INSTANCE.getColumnData(col);
-            if (data != null) {
-                cols.add(new Pair<Fixing.Column, FixingsColumn>(col, data));
-            }
-        }
-
-        JFreeChart chart = createChart(cols, river, km, callMeta);
-
-        return encode(chart, extent, format);
-    }
-
-    protected static Output encode(
-        JFreeChart chart,
-        Dimension  extent,
-        String     format
-    ) {
-        BufferedImage image = chart.createBufferedImage(
-            extent.width, extent.height,
-            Transparency.BITMASK,
-            null);
-
-        ByteArrayOutputStream out = new ByteArrayOutputStream();
-
-        try {
-            ImageIO.write(image, format, out);
-        }
-        catch (IOException ioe) {
-            log.warn("writing image failed", ioe);
-            return empty();
-        }
-
-        return new Output(out.toByteArray(), "image/" + format);
-    }
-
-    protected static JFreeChart createChart(
-        List<Pair<Fixing.Column, FixingsColumn>> cols,
-        String      river,
-        double      km,
-        CallMeta    callMeta
-    ) {
-        String labelFormat = Resources.getMsg(
-            callMeta, I18N_CHART_LABEL_DATE, DEFAULT_CHART_LABEL_DATE);
-
-        QWSeriesCollection.LabelGenerator lg =
-            new QWSeriesCollection.DateFormatLabelGenerator(labelFormat);
-
-        QWSeriesCollection dataset = new QWSeriesCollection(lg);
-
-        double [] w = new double[1];
-        for (Pair<Fixing.Column, FixingsColumn> col: cols) {
-            boolean interpolated = !col.getB().getW(km, w);
-            double q = col.getB().getQ(km);
-            if (!Double.isNaN(w[0]) && !Double.isNaN(q)) {
-                QWI qw = new QWI(
-                    q, w[0],
-                    col.getA().getDescription(),
-                    col.getA().getStartTime(),
-                    interpolated, 0);
-                dataset.add(qw);
-            }
-        }
-
-        String title = Resources.format(
-            callMeta, I18N_CHART_TITLE, DEFAULT_CHART_TITLE, river, km);
-
-        String qAxis = Resources.getMsg(
-            callMeta, I18N_Q_AXIS, DEFAULT_Q_AXIS);
-
-        String wAxis = Resources.getMsg(
-            callMeta, I18N_W_AXIS, DEFAULT_W_AXIS);
-
-        JFreeChart chart = ChartFactory.createXYLineChart(
-            title,
-            qAxis,
-            wAxis,
-            null,
-            PlotOrientation.VERTICAL,
-            true,
-            true,
-            false);
-
-        XYPlot plot = (XYPlot)chart.getPlot();
-
-        NumberAxis qA = (NumberAxis)plot.getDomainAxis();
-        qA.setNumberFormatOverride(Formatter.getWaterlevelQ(callMeta));
-
-        NumberAxis wA = (NumberAxis)plot.getRangeAxis();
-        wA.setNumberFormatOverride(Formatter.getWaterlevelW(callMeta));
-
-        plot.setRenderer(0, dataset.createRenderer());
-        plot.setDataset(0, dataset);
-
-        Rectangle2D area = dataset.getArea();
-
-        if (area != null) {
-            double height = area.getHeight();
-            double wInset = Math.max(height, 0.01) * 0.25d;
-
-            wA.setAutoRangeIncludesZero(false);
-            wA.setRange(new Range(
-                area.getMinY() - wInset,
-                area.getMaxY() + wInset));
-        }
-
-        final String measuredS = Resources.getMsg(
-            callMeta, I18N_MEASURED, DEFAULT_MEASURED);
-
-        final String interpolatedS = Resources.getMsg(
-            callMeta, I18N_INTERPOLATED, DEFAULT_INTERPOLATED);
-
-        LegendItemCollection lic = plot.getLegendItems();
-        dataset.addLegendItems(lic, new ShapeRenderer.LabelGenerator() {
-            @Override
-            public String createLabel(ShapeRenderer.Entry entry) {
-                return entry.getFilled() ? measuredS : interpolatedS;
-            }
-        });
-        plot.setFixedLegendItems(lic);
-
-        applyQSectorMarkers(plot, river, km, callMeta);
-
-        chart.setBackgroundPaint(Color.white);
-        plot.setBackgroundPaint(Color.white);
-        plot.setDomainGridlinePaint(Color.gray);
-        plot.setRangeGridlinePaint(Color.gray);
-        plot.setDomainGridlinesVisible(true);
-        plot.setRangeGridlinesVisible(true);
-
-        return chart;
-    }
-
-    /** Add domain markers to plot that indicate Q-sectors. */
-    protected static void applyQSectorMarkers(
-        XYPlot   plot,
-        String   river,
-        double   km,
-        CallMeta meta
-    ) {
-        GaugeFinderFactory ggf = GaugeFinderFactory.getInstance();
-        GaugeFinder        gf  = ggf.getGaugeFinder(river);
-
-        if (gf == null) {
-            log.warn("No gauge finder found for river '" + river + "'");
-            return;
-        }
-
-        GaugeRange gr = gf.find(km);
-        if (gr == null) {
-            log.debug("No gauge range found for km "
-                + km + " on river " + river + ".");
-            return;
-        }
-
-        if (log.isDebugEnabled()) {
-            log.debug(gr);
-        }
-
-        for (int i = 0; i < I18N_Q_SECTOR_BOARDERS.length; ++i) {
-            String key   = I18N_Q_SECTOR_BOARDERS[i];
-            String def   = DEFAULT_Q_SECTOR_BORDERS[i];
-            String label = Resources.getMsg(meta, key, def);
-
-            Marker m = createQSectorMarker(
-                gr.getSectorBorder(i),
-                label);
-
-            if (m != null) {
-                plot.addDomainMarker(m);
-            }
-        }
-    }
-
-    /** Create Marker at value with label. */
-    protected static Marker createQSectorMarker(
-         double value, String label
-    ) {
-        if (Double.isNaN(value)) {
-            return null;
-        }
-        Marker m = new ValueMarker(value);
-        m.setPaint(Color.black);
-        m.setStroke(new BasicStroke());
-        m.setLabel(label);
-        m.setLabelAnchor(RectangleAnchor.TOP_LEFT);
-        m.setLabelTextAnchor(TextAnchor.TOP_LEFT);
-        return m;
-    }
-
-    protected static String getRiverName(Document input) {
-        NodeList rivers = input.getElementsByTagName("river");
-
-        if (rivers.getLength() == 0) {
-            return null;
-        }
-
-        String river = ((Element)rivers.item(0)).getAttribute("name");
-
-        return river.length() > 0 ? river : null;
-    }
-
-    protected static Double getKM(Document input) {
-        NodeList kms = input.getElementsByTagName("km");
-
-        if (kms.getLength() == 0) {
-            return null;
-        }
-
-        String km = ((Element)kms.item(0)).getAttribute("value");
-
-        try {
-            return Double.valueOf(km);
-        }
-        catch (NumberFormatException nfe) {
-            log.warn("Km '" + km + " is not a valid number.");
-            return null;
-        }
-    }
-
-    protected static Dimension getExtent(Document input) {
-
-        int width  = DEFAULT_WIDTH;
-        int height = DEFAULT_HEIGHT;
-
-        NodeList extents = input.getElementsByTagName("extent");
-
-        if (extents.getLength() > 0) {
-            Element element = (Element)extents.item(0);
-            String w = element.getAttribute("width");
-            String h = element.getAttribute("height");
-
-            try {
-                width = Math.max(1, Integer.parseInt(w));
-            }
-            catch (NumberFormatException nfe) {
-                log.warn("width '" + w + "' is not a valid.");
-            }
-
-            try {
-                height = Math.max(1, Integer.parseInt(h));
-            }
-            catch (NumberFormatException nfe) {
-                log.warn("height '" + h + "' is not a valid");
-            }
-        }
-
-        return new Dimension(width, height);
-    }
-
-    protected static String getFormat(Document input) {
-        String format = DEFAULT_FORMAT;
-
-        NodeList formats = input.getElementsByTagName("format");
-
-        if (formats.getLength() > 0) {
-            String type = ((Element)formats.item(0)).getAttribute("type");
-            if (type.length() > 0) {
-                format = type;
-            }
-        }
-
-        return format;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/FixingsOverviewService.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,63 +0,0 @@
-package de.intevation.flys.artifacts.services;
-
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.GlobalContext;
-
-import de.intevation.artifacts.common.utils.XMLUtils;
-
-import de.intevation.flys.artifacts.model.FixingsFilterBuilder;
-
-import de.intevation.flys.artifacts.model.FixingsOverview.Fixing.Filter;
-
-import de.intevation.flys.artifacts.model.Range;
-
-import de.intevation.flys.artifacts.model.FixingsOverview;
-import de.intevation.flys.artifacts.model.FixingsOverviewFactory;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.NodeList;
-
-public class FixingsOverviewService
-extends      FLYSService
-{
-    private static Logger log =
-        Logger.getLogger(FixingsOverviewService.class);
-
-    public FixingsOverviewService() {
-    }
-
-    @Override
-    public Document doProcess(
-        Document      data,
-        GlobalContext globalContext,
-        CallMeta      callMeta
-    ) {
-        log.debug("FixingsOverviewService.doProcess");
-
-        Document document = XMLUtils.newDocument();
-
-        NodeList nodes = data.getElementsByTagName("river");
-
-        String river = nodes.getLength() > 0
-            ? ((Element)nodes.item(0)).getAttribute("name")
-            : "";
-
-        FixingsOverview overview = FixingsOverviewFactory.getOverview(river);
-
-        if (overview != null) {
-            FixingsFilterBuilder ffb = new FixingsFilterBuilder(data);
-            Range  range  = ffb.getRange();
-            Filter filter = ffb.getFilter();
-            overview.generateOverview(document, range, filter);
-        }
-        else {
-            log.warn("No overview for river '" + river + "' available.");
-        }
-
-        return document;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/GaugeInfoService.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,160 +0,0 @@
-package de.intevation.flys.artifacts.services;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.common.ArtifactNamespaceContext;
-import de.intevation.artifacts.common.utils.XMLUtils;
-import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
-
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.GlobalContext;
-
-import de.intevation.flys.model.Gauge;
-import de.intevation.flys.model.Range;
-import de.intevation.flys.model.River;
-
-import de.intevation.flys.artifacts.model.RiverFactory;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class GaugeInfoService extends FLYSService {
-
-    interface Filter {
-        boolean apply(Gauge gauge);
-    }
-
-
-    private static final class ReferenceNumberFilter implements Filter {
-        private long refNr;
-
-        public ReferenceNumberFilter(long refNr) {
-            this.refNr = refNr;
-        }
-
-        @Override
-        public boolean apply(Gauge  gauge) {
-            if (logger.isDebugEnabled()) {
-                logger.debug("Test gauge '" + gauge.getName() + "'");
-            }
-
-            return gauge != null && gauge.getOfficialNumber() == refNr;
-        }
-    } // end of ReferenceNumberFilter class
-
-
-    /** The logger that is used by this service.*/
-    private static Logger logger = Logger.getLogger(GaugeInfoService.class);
-
-
-    public static final String XPATH_RIVERNAME = "/art:river/@name";
-
-    public static final String XPATH_REFERENCE_NR
-        = "/art:river/art:filter/art:gauge/text()";
-
-
-    public GaugeInfoService() {
-    }
-
-
-    @Override
-    public Document doProcess(
-        Document      data,
-        GlobalContext context,
-        CallMeta      callMeta
-    ) {
-        logger.debug("GaugeInfoService.process");
-
-        if (logger.isDebugEnabled()) {
-            logger.debug(XMLUtils.toString(data));
-        }
-
-        River river = getRiverFromRequest(data);
-
-        List<Filter> filters  = getFilters(data);
-        List<Gauge> allGauges = river.getGauges();
-        List<Gauge> filtered  = new ArrayList<Gauge>();
-
-        for (Gauge g: allGauges) {
-            for (Filter f: filters) {
-                if (f.apply(g)) {
-                    filtered.add(g);
-                    break;
-                }
-            }
-        }
-
-        return buildInfoDocument(filtered);
-    }
-
-
-    protected River getRiverFromRequest(Document data) {
-        String rivername = XMLUtils.xpathString(
-            data,
-            XPATH_RIVERNAME,
-            ArtifactNamespaceContext.INSTANCE);
-
-        logger.debug("Return Gauge info for River '" + rivername + "'");
-
-        return rivername != null ? RiverFactory.getRiver(rivername) : null;
-    }
-
-
-    protected List<Filter> getFilters(Document data) {
-        List<Filter> filters = new ArrayList<Filter>();
-
-        String refNr = XMLUtils.xpathString(
-            data,
-            XPATH_REFERENCE_NR,
-            ArtifactNamespaceContext.INSTANCE);
-
-        if (refNr != null && refNr.length() > 0) {
-            try {
-                filters.add(
-                    new ReferenceNumberFilter(Long.parseLong(refNr)));
-            }
-            catch (NumberFormatException nfe) {
-                logger.warn(nfe, nfe);
-            }
-        }
-
-        return filters;
-    }
-
-
-    protected Document buildInfoDocument(List<Gauge> gauges) {
-        Document doc = XMLUtils.newDocument();
-
-        ElementCreator cr = new ElementCreator(
-            doc,
-            ArtifactNamespaceContext.NAMESPACE_URI,
-            ArtifactNamespaceContext.NAMESPACE_PREFIX);
-
-        Element service = cr.create("service");
-
-        logger.debug("Append " + gauges.size() + " gauges to info doc.");
-
-        for (Gauge g: gauges) {
-            Range r = g.getRange();
-
-            Element el = cr.create("gauge");
-            cr.addAttr(el, "name", g.getName());
-            cr.addAttr(el, "lower", String.valueOf(r.getA().doubleValue()));
-            cr.addAttr(el, "upper", String.valueOf(r.getB().doubleValue()));
-
-            service.appendChild(el);
-        }
-
-        doc.appendChild(service);
-
-        return doc;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/GaugeOverviewInfoService.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,113 +0,0 @@
-package de.intevation.flys.artifacts.services;
-
-import java.math.BigDecimal;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.GlobalContext;
-
-import de.intevation.flys.model.Gauge;
-import de.intevation.flys.model.MinMaxWQ;
-import de.intevation.flys.model.Range;
-
-
-/**
- * @author <a href="mailto:bjoern.ricks@intevation.de">Björn Ricks</a>
- */
-public class GaugeOverviewInfoService extends RiverInfoService {
-
-    private static final Logger logger = Logger.getLogger(
-            GaugeOverviewInfoService.class);
-
-    @Override
-    public Document doProcess(
-        Document      data,
-        GlobalContext globalContext,
-        CallMeta      callMeta
-    ) {
-        Document result = super.doProcess(data, globalContext, callMeta);
-
-        logger.debug("GaugeOverviewInfoService.process");
-
-        Element egs = ec.create("gauges");
-
-        List<Gauge> gauges = river.getGauges();
-
-        if (logger.isDebugEnabled()) {
-            logger.debug("Loaded gauges: " + gauges);
-        }
-
-        for (Gauge gauge: river.getGauges()) {
-            Element eg = ec.create("gauge");
-
-            String name = gauge.getName();
-            if (name != null) {
-                ec.addAttr(eg, "name", gauge.getName(), true);
-            }
-
-            String aeo = getStringValue(gauge.getAeo());
-            if (aeo != null) {
-                ec.addAttr(eg, "aeo", aeo, true);
-            }
-
-            String datum = getStringValue(gauge.getDatum());
-            if (datum != null) {
-                ec.addAttr(eg, "datum", datum, true);
-            }
-
-            Range range = gauge.getRange();
-            if (range != null) {
-                BigDecimal a = range.getA();
-                if (a != null) {
-                    double min = a.doubleValue();
-                    ec.addAttr(eg, "start", Double.toString(min), true);
-                }
-
-                BigDecimal b = range.getB();
-                if (b != null) {
-                    double max = range.getB().doubleValue();
-                    ec.addAttr(eg, "end", Double.toString(max), true);
-                }
-            }
-            MinMaxWQ minmaxwq = gauge.fetchMaxMinWQ();
-            String minw = getStringValue(minmaxwq.getMinW());
-            String maxw = getStringValue(minmaxwq.getMaxW());
-            String minq = getStringValue(minmaxwq.getMinQ());
-            String maxq = getStringValue(minmaxwq.getMaxQ());
-
-            if (minw != null) {
-                ec.addAttr(eg, "minw", minw, true);
-            }
-            if (maxw != null) {
-                ec.addAttr(eg, "maxw", maxw, true);
-            }
-            if (minq != null) {
-                ec.addAttr(eg, "minq", minq, true);
-            }
-            if (maxq != null) {
-                ec.addAttr(eg, "maxq", maxq, true);
-            }
-
-            String station = getStringValue(gauge.getStation());
-            if (station != null) {
-                ec.addAttr(eg, "station", station, true);
-            }
-
-            Long official = gauge.getOfficialNumber();
-            if (official != null) {
-                ec.addAttr(eg, "official", official.toString(), true);
-            }
-
-            egs.appendChild(eg);
-        }
-
-        riverele.appendChild(egs);
-
-        return result;
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/MainValuesService.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,304 +0,0 @@
-package de.intevation.flys.artifacts.services;
-
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.GlobalContext;
-
-import de.intevation.artifacts.common.ArtifactNamespaceContext;
-import de.intevation.artifacts.common.utils.XMLUtils;
-import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
-
-import de.intevation.flys.model.Gauge;
-import de.intevation.flys.model.MainValue;
-import de.intevation.flys.model.MainValueType;
-import de.intevation.flys.model.NamedMainValue;
-import de.intevation.flys.model.Range;
-import de.intevation.flys.model.River;
-
-import de.intevation.flys.artifacts.model.RiverFactory;
-
-
-/**
- * This service returns the main values of a river's gauge based on the start
- * and end point of the river.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class MainValuesService extends FLYSService {
-
-    /** The logger that is used by this service.*/
-    private static Logger logger = Logger.getLogger(MainValuesService.class);
-
-
-    /** The XPath that points to the river definition of the incoming request.*/
-    public static final String XPATH_RIVER = "/art:mainvalues/art:river/text()";
-
-    /** The XPath that points to the start definition of the incoming request.*/
-    public static final String XPATH_START = "/art:mainvalues/art:start/text()";
-
-    /** The XPath that points to the end definition of the incoming request.*/
-    public static final String XPATH_END = "/art:mainvalues/art:end/text()";
-
-    /**
-     * The default constructor.
-     */
-    public MainValuesService() {
-    }
-
-    private static final Document error(String msg) {
-        logger.debug(msg);
-        return XMLUtils.newDocument();
-    }
-
-
-    @Override
-    public Document doProcess(
-        Document      data,
-        GlobalContext context,
-        CallMeta      callMeta
-    ) {
-        logger.debug("MainValuesService.process");
-
-        River river = getRequestedRiver(data);
-        if (river == null) {
-            return error("no river found.");
-        }
-
-        double[] minmax = getRequestedStartEnd(data, river);
-        Gauge gauge = river.determineGauge(minmax[0], minmax[1]);
-
-        if (gauge == null) {
-            return error("no gauge found.");
-        }
-
-        List<MainValue> mainValues = getMainValues(river, gauge);
-
-        return buildDocument(river, gauge, mainValues, context);
-    }
-
-
-    /**
-     * This method extracts the river from the incoming request. If no river
-     * string was found or no river is found in the database based on this
-     * string a NullPointerException is thrown.
-     *
-     * @param data The incoming request data.
-     *
-     * @return the River object.
-     */
-    protected River getRequestedRiver(Document data)
-    throws    NullPointerException
-    {
-        logger.debug("MainValuesService.getRiver");
-
-        String riverStr = XMLUtils.xpathString(
-            data, XPATH_RIVER, ArtifactNamespaceContext.INSTANCE);
-
-         return riverStr != null && (riverStr = riverStr.trim()).length() > 0
-            ? RiverFactory.getRiver(riverStr)
-            : null;
-    }
-
-
-    /**
-     * This method extracts the start and end point from incoming request
-     * document and returns both values in an array. If no start and end strings
-     * are found in the document, the min/max values of the <i>river</i> are
-     * returned.
-     *
-     * @param data The incoming request data.
-     * @param river The river of the request.
-     *
-     * @return the start and end point.
-     */
-    protected double[] getRequestedStartEnd(Document data, River river) {
-        logger.debug("MainValuesService.getStartEnd");
-
-        String startStr = XMLUtils.xpathString(
-            data, XPATH_START, ArtifactNamespaceContext.INSTANCE);
-
-        String endStr = XMLUtils.xpathString(
-            data, XPATH_END, ArtifactNamespaceContext.INSTANCE);
-
-        if (startStr == null || endStr == null) {
-            return river.determineMinMaxDistance();
-        }
-
-        try {
-            double start = Double.parseDouble(startStr);
-            double end   = Double.parseDouble(endStr);
-
-            if (logger.isDebugEnabled()) {
-                logger.debug("Found start: " + start);
-                logger.debug("Found end: " + end);
-            }
-
-            return new double[] { start, end };
-        }
-        catch (NumberFormatException nfe) {
-            logger.warn(nfe, nfe);
-            return river.determineMinMaxDistance();
-        }
-    }
-
-
-    /**
-     * This method creates the result document that includes the main values of
-     * the specified <i>gauge</i>.
-     *
-     * @param river The river.
-     * @param gauge The gauge.
-     *
-     * @return a document that includes the main values of the specified river
-     * at the specified gauge.
-     */
-    protected List<MainValue> getMainValues(River river, Gauge gauge) {
-
-        if (logger.isDebugEnabled()) {
-            logger.debug("MainValuesService.buildMainValues");
-            logger.debug("River: " + river.getName());
-            logger.debug("Gauge: " + gauge.getName());
-        }
-
-        List<MainValue> mainValues = gauge.getMainValues();
-
-        if (logger.isDebugEnabled()) {
-            logger.debug(mainValues.size() + " main values found.");
-        }
-
-        return mainValues;
-    }
-
-
-    protected Document buildDocument(
-        River           river,
-        Gauge           gauge,
-        List<MainValue> mainValues,
-        Object          context)
-    {
-        logger.debug("MainValuesService.buildDocument");
-
-        Document doc = XMLUtils.newDocument();
-
-        ElementCreator cr = new ElementCreator(
-            doc,
-            ArtifactNamespaceContext.NAMESPACE_URI,
-            ArtifactNamespaceContext.NAMESPACE_PREFIX);
-
-        Element rootEl = cr.create("service");
-        cr.addAttr(rootEl, "name", "mainvalues");
-
-        doc.appendChild(rootEl);
-
-        appendMetaInformation(doc, rootEl, river, gauge, context);
-        appendMainValues(doc, rootEl, mainValues, context);
-
-        return doc;
-    }
-
-
-    /**
-     * This method appends some meta information to the result document.
-     * Currently, the river's and gauge's names and the gauge's range are
-     * appended.
-     *
-     * @param root The root element of the result document.
-     * @param river The river.
-     * @param gauge The gauge.
-     * @param context The context object.
-     */
-    protected void appendMetaInformation(
-        Document doc,
-        Element  root,
-        River    river,
-        Gauge    gauge,
-        Object   context)
-    {
-        logger.debug("MainValuesService.appendMetaInformation");
-
-        ElementCreator cr = new ElementCreator(
-            doc,
-            ArtifactNamespaceContext.NAMESPACE_URI,
-            ArtifactNamespaceContext.NAMESPACE_PREFIX);
-
-        Range range = gauge.getRange();
-
-        Element riverEl = cr.create("river");
-        cr.addAttr(riverEl, "name", river.getName());
-
-        Element gaugeEl = cr.create("gauge");
-        cr.addAttr(gaugeEl, "name", gauge.getName());
-        cr.addAttr(gaugeEl, "from", range.getA().toString());
-        cr.addAttr(gaugeEl, "to", range.getB().toString());
-
-        root.appendChild(riverEl);
-        root.appendChild(gaugeEl);
-    }
-
-
-    protected void appendMainValues(
-        Document        doc,
-        Element         root,
-        List<MainValue> mainValues,
-        Object          context)
-    {
-        logger.debug("MainValuesService.appendMainValues");
-
-        ElementCreator cr = new ElementCreator(
-            doc,
-            ArtifactNamespaceContext.NAMESPACE_URI,
-            ArtifactNamespaceContext.NAMESPACE_PREFIX);
-
-        Element list = cr.create("mainvalues");
-
-        for (MainValue mainValue: mainValues) {
-            Element newEl = buildMainValueElement(doc, mainValue, context);
-
-            if (newEl != null) {
-                list.appendChild(newEl);
-            }
-        }
-
-        root.appendChild(list);
-    }
-
-
-    /**
-     * This method builds a concrete mainvalue element. This element consists of
-     * three attributes: the value, its name and its type.
-     *
-     * @param doc The owner document.
-     * @param mainValue The mainvalue.
-     * @param context The context object.
-     *
-     * @return a mainvalue element.
-     */
-    protected Element buildMainValueElement(
-        Document  doc,
-        MainValue mainValue,
-        Object    context)
-    {
-        ElementCreator cr = new ElementCreator(
-            doc,
-            ArtifactNamespaceContext.NAMESPACE_URI,
-            ArtifactNamespaceContext.NAMESPACE_PREFIX);
-
-        NamedMainValue namedMainValue = mainValue.getMainValue();
-        MainValueType  mainValueType  = namedMainValue.getType();
-
-        Element el = cr.create("mainvalue");
-
-        cr.addAttr(el, "value", mainValue.getValue().toString());
-        cr.addAttr(el, "name", namedMainValue.getName());
-        cr.addAttr(el, "type", mainValueType.getName());
-
-        return el;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/MapInfoService.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,180 +0,0 @@
-package de.intevation.flys.artifacts.services;
-
-import com.vividsolutions.jts.geom.Envelope;
-
-import de.intevation.artifactdatabase.XMLService;
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.GlobalContext;
-import de.intevation.artifacts.common.ArtifactNamespaceContext;
-import de.intevation.artifacts.common.utils.Config;
-import de.intevation.artifacts.common.utils.XMLUtils;
-import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
-import de.intevation.flys.utils.GeometryUtils;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.xml.xpath.XPathConstants;
-
-import org.apache.log4j.Logger;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-
-/**
- * This service provides information about the supported rivers by this
- * application.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class MapInfoService extends XMLService {
-
-    /** XPath that points to the river.*/
-    public static final String XPATH_RIVER = "/mapinfo/river/text()";
-
-    public static final String XPATH_MAPTYPE = "/mapinfo/maptype/text()";
-
-    private static final String XPATH_RIVER_PROJECTION =
-        "/artifact-database/*[local-name()=$maptype]/river[@name=$river]/srid/@value";
-
-    private static final String XPATH_RIVER_BACKGROUND =
-        "/artifact-database/*[local-name()=$maptype]/river[@name=$river]/background-wms";
-
-    private static final String XPATH_RIVER_WMS =
-        "/artifact-database/*[local-name()=$maptype]/river[@name=$river]/river-wms";
-
-
-    /** The logger used in this service.*/
-    private static Logger logger = Logger.getLogger(MapInfoService.class);
-
-
-    /**
-     * The default constructor.
-     */
-    public MapInfoService() {
-    }
-
-    protected static String getStringXPath(
-        String              query,
-        Map<String, String> variables
-    ) {
-        return (String)XMLUtils.xpath(
-            Config.getConfig(), query, XPathConstants.STRING,
-            null, variables);
-    }
-
-    protected static Node getNodeXPath(
-        String              query,
-        Map<String, String> variables
-    ) {
-        return (Node)XMLUtils.xpath(
-            Config.getConfig(), query, XPathConstants.NODE,
-            null, variables);
-    }
-
-    @Override
-    public Document processXML(
-        Document      data,
-        GlobalContext globalContext,
-        CallMeta      callMeta
-    ) {
-        logger.debug("MapInfoService.process");
-
-        Document result   = XMLUtils.newDocument();
-        ElementCreator cr = new ElementCreator(result, null, null);
-
-        Element mapinfo = cr.create("mapinfo");
-        result.appendChild(mapinfo);
-
-        String river = extractRiver(data);
-        if (river == null || river.length() == 0) {
-            logger.warn("Cannot generate information: river is empty!");
-            return result;
-        }
-
-        String mapType = extractMaptype(data);
-        if (mapType == null
-        || !(mapType.equals("floodmap") || mapType.equals("rivermap"))) {
-            mapType = "floodmap";
-        }
-
-        Element root = cr.create("river");
-        cr.addAttr(root, "name", river);
-        mapinfo.appendChild(root);
-
-        Envelope env = GeometryUtils.getRiverBoundary(river);
-        if (env != null) {
-            String bounds = GeometryUtils.jtsBoundsToOLBounds(env);
-            if (logger.isDebugEnabled()) {
-                logger.debug("River '" + river + "' bounds: " + bounds);
-            }
-
-            Element bbox = cr.create("bbox");
-            cr.addAttr(bbox, "value", bounds);
-            root.appendChild(bbox);
-        }
-
-        Map<String, String> vars = new HashMap<String, String>();
-        vars.put("maptype", mapType);
-        vars.put("river", river);
-
-        String sridStr = getStringXPath(XPATH_RIVER_PROJECTION, vars);
-
-        if (sridStr != null && sridStr.length() > 0) {
-            Element srid = cr.create("srid");
-            cr.addAttr(srid, "value", sridStr);
-            root.appendChild(srid);
-        }
-
-        if (logger.isDebugEnabled()) {
-            logger.debug("processXML: " + XMLUtils.toString(root));
-        }
-
-        root.appendChild(
-            createWMSElement("background-wms",
-                XPATH_RIVER_BACKGROUND, vars, cr));
-
-        root.appendChild(
-            createWMSElement("river-wms",
-                XPATH_RIVER_WMS, vars, cr));
-
-        return result;
-    }
-
-
-    protected Element createWMSElement(
-        String elementName,
-        String xpath,
-        Map<String, String> vars,
-        ElementCreator cr)
-    {
-        logger.debug("createWMSElement()");
-
-        Element el = cr.create(elementName);
-        Element wms = (Element)getNodeXPath(xpath, vars);
-
-        if (wms != null) {
-            cr.addAttr(el, "url", wms.getAttribute("url"));
-            cr.addAttr(el, "layers", wms.getAttribute("layers"));
-
-            logger.debug("createWMSElement: " + XMLUtils.toString(el));
-        }
-        else {
-            logger.debug("createWMSElement: wms == null");
-        }
-
-        return el;
-    }
-
-
-    private static String extractRiver(Document data) {
-        return XMLUtils.xpathString(
-            data, XPATH_RIVER, ArtifactNamespaceContext.INSTANCE);
-    }
-
-    private static String extractMaptype(Document data) {
-        return XMLUtils.xpathString(
-            data, XPATH_MAPTYPE, ArtifactNamespaceContext.INSTANCE);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/MeasurementStationInfoService.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,124 +0,0 @@
-package de.intevation.flys.artifacts.services;
-
-import java.math.BigDecimal;
-import java.text.DateFormat;
-import java.util.Date;
-import java.util.List;
-import java.util.Locale;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.GlobalContext;
-
-import de.intevation.flys.model.MeasurementStation;
-import de.intevation.flys.model.Range;
-import de.intevation.flys.model.TimeInterval;
-
-/**
- * @author <a href="mailto:bjoern.ricks@intevation.de">Björn Ricks</a>
- */
-public class MeasurementStationInfoService extends RiverInfoService {
-
-    private static final Logger logger = Logger.getLogger(
-            MeasurementStationInfoService.class);
-
-    public static final DateFormat DATE_FORMAT = DateFormat.getDateInstance(
-        DateFormat.SHORT, Locale.GERMANY);
-
-    @Override
-    public Document doProcess(
-        Document      data,
-        GlobalContext globalContext,
-        CallMeta      callMeta
-    ) {
-        Document result = super.doProcess(data, globalContext, callMeta);
-
-        Element egs = ec.create("measurement-stations");
-
-        List<MeasurementStation> mstations = river.getMeasurementStations();
-
-        if (logger.isDebugEnabled()) {
-            logger.debug("Loaded stations: " + mstations);
-        }
-
-        for (MeasurementStation mstation: mstations) {
-            Element eg = ec.create("measurement-station");
-
-            String name = mstation.getName();
-            if (name != null) {
-                ec.addAttr(eg, "name", name, true);
-            }
-
-            Integer id = mstation.getId();
-            if (id != null) {
-                ec.addAttr(eg, "id", Integer.toString(id), true);
-            }
-
-            String type = mstation.getMeasurementType();
-            if (type != null) {
-                ec.addAttr(eg, "type", type, true);
-            }
-
-            String riverside = mstation.getRiverside();
-            if (riverside != null) {
-                ec.addAttr(eg, "riverside", riverside, true);
-            }
-
-            Double station = mstation.getStation();
-            if (station != null) {
-                ec.addAttr(eg, "station", Double.toString(station), true);
-            }
-
-            Range range = mstation.getRange();
-            if (range != null) {
-                BigDecimal a = range.getA();
-                if (a != null) {
-                    ec.addAttr(eg, "start", getStringValue(a), true);
-                }
-
-                BigDecimal b = range.getB();
-                if (b != null) {
-                    ec.addAttr(eg, "end", getStringValue(b), true);
-                }
-            }
-
-            String moperator = mstation.getOperator();
-            if (moperator != null) {
-                ec.addAttr(eg, "operator", moperator, true);
-            }
-
-            TimeInterval tinterval = mstation.getObservationTimerange();
-            if (tinterval != null) {
-                Date tstart = tinterval.getStartTime();
-                if (tstart != null) {
-                    ec.addAttr(eg, "starttime", DATE_FORMAT.format(tstart),
-                            true);
-                }
-                Date tstop = tinterval.getStopTime();
-                if (tstop != null) {
-                    ec.addAttr(eg, "stoptime", DATE_FORMAT.format(tstop),
-                            true);
-                }
-            }
-
-            String gaugename= mstation.getGaugeName();
-
-            if (gaugename != null) {
-                Element egauge = ec.create("gauge");
-                ec.addAttr(egauge, "name", gaugename, true);
-                eg.appendChild(egauge);
-            }
-
-            egs.appendChild(eg);
-        }
-
-        this.riverele.appendChild(egs);
-
-        return result;
-    }
-
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,191 +0,0 @@
-package de.intevation.flys.artifacts.services;
-
-import org.w3c.dom.Document;
-
-import org.apache.log4j.Logger;
-
-import java.util.Map;
-import java.util.HashMap;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.GlobalContext;
-import de.intevation.artifacts.ArtifactDatabase;
-import de.intevation.artifacts.ArtifactDatabaseException;
-
-import de.intevation.artifacts.common.utils.XMLUtils;
-import de.intevation.artifacts.common.utils.StringUtils;
-
-import de.intevation.artifacts.common.ArtifactNamespaceContext;
-
-import de.intevation.flys.artifacts.datacage.Recommendations;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-
-/**
- * Following XPaths are evaluated on the incoming document.
- *
- *  "/art:meta/art:artifact-id/@value" The UUID of the artifact. Optional.
- *                                     Used to fill the template enviroment.
- *  "/art:meta/art:user-id/@value"     The UUID of the user. Optional.
- *                                     If given the user specific template is filled.
- *  "/art:meta/art:outs/@value"        The list of outs used to recommend for the
- *                                     various outputs.
- *  "/art:meta/art:parameters/@value"  A list of key/value pairs to inject more
- *                                     filters to the templating, as "key:value;key2:value2"
- */
-public class MetaDataService
-extends      FLYSService
-{
-    private static Logger log = Logger.getLogger(MetaDataService.class);
-
-    public static final String XPATH_ARTIFACT_ID = "/art:meta/art:artifact-id/@value";
-    public static final String XPATH_USER_ID     = "/art:meta/art:user-id/@value";
-    public static final String XPATH_OUTS        = "/art:meta/art:outs/@value";
-    public static final String XPATH_PARAMETERS  = "/art:meta/art:parameters/@value";
-
-    /** The global context key of the artifact database. */
-    public static final String ARTIFACT_DATA_BASE_KEY =
-        "global.artifact.database";
-
-    public MetaDataService() {
-    }
-
-    @Override
-    protected Document doProcess(
-        Document      data,
-        GlobalContext globalContext,
-        CallMeta      callMeta
-    ) {
-        log.debug("MetaDataService.process");
-
-        String artifactId = XMLUtils.xpathString(
-            data, XPATH_ARTIFACT_ID, ArtifactNamespaceContext.INSTANCE);
-
-        if (artifactId != null
-        && (artifactId = artifactId.trim()).length() == 0) {
-            artifactId = null;
-        }
-
-        String userId = XMLUtils.xpathString(
-            data, XPATH_USER_ID, ArtifactNamespaceContext.INSTANCE);
-
-        if (userId != null
-        && (userId = userId.trim()).length() == 0) {
-            userId = null;
-        }
-
-        String outs = XMLUtils.xpathString(
-            data, XPATH_OUTS, ArtifactNamespaceContext.INSTANCE);
-
-        String parameters = XMLUtils.xpathString(
-            data, XPATH_PARAMETERS, ArtifactNamespaceContext.INSTANCE);
-
-        return doService(
-            artifactId, userId, outs, parameters, globalContext);
-    }
-
-
-    /**
-     * Split parameterstring in the form of key1:value1;key2:value2
-     * into hash (key1->value1, key2->value2).
-     * @param parameters "key1:value1;key2:value2"
-     * @param data Map into wich to put parameter hash and return.
-     * @return parameter data
-     */
-    protected static Map<String, Object> splitParameters(
-        String              parameters,
-        Map<String, Object> data
-    ) {
-        if (parameters != null) {
-            String [] parts = parameters.split("\\s*;\\s*");
-            for (String part: parts) {
-                String [] kv = part.split("\\s*:\\s*");
-                if (kv.length < 2 || (kv[0] = kv[0].trim()).length() == 0) {
-                    continue;
-                }
-                String [] values = kv[1].split("\\s*,\\s*");
-                data.put(kv[0], values.length == 1 ? values[0] : values);
-            }
-        }
-        return data;
-    }
-
-    /** Return the document containing matched stuff from meta-data.xml. */
-    protected Document doService(
-        String        artifactId,
-        String        userId,
-        String        outsString,
-        String        parameters,
-        GlobalContext globalContext
-    ) {
-        Document result = XMLUtils.newDocument();
-
-        FLYSArtifact flysArtifact;
-
-        if (log.isDebugEnabled()) {
-            log.debug("artifact  : " + artifactId);
-            log.debug("user      : " + userId);
-            log.debug("outs      : " + outsString);
-            log.debug("parameters: " + parameters);
-        }
-
-        if (userId != null && !StringUtils.checkUUID(userId)) {
-            log.warn("'" + userId + "' is not a UUID");
-            return result;
-        }
-
-        if (artifactId != null) {
-            if (!StringUtils.checkUUID(artifactId)) {
-                log.warn("'" + artifactId + "' is not a UUID");
-                return result;
-            }
-
-            Object dbObject =
-                (ArtifactDatabase)globalContext.get(ARTIFACT_DATA_BASE_KEY);
-
-            if (!(dbObject instanceof ArtifactDatabase)) {
-                log.error("Cannot find artifact database");
-                return result;
-            }
-
-            ArtifactDatabase db = (ArtifactDatabase)dbObject;
-
-            Artifact artifact;
-
-            try {
-                artifact = db.getRawArtifact(artifactId);
-            }
-            catch (ArtifactDatabaseException adbe) {
-                log.warn("fetching artifact failed", adbe);
-                return result;
-            }
-
-            if (!(artifact instanceof FLYSArtifact)) {
-                log.warn("artifact is not a FLYS artifact.");
-                return result;
-            }
-
-            flysArtifact = (FLYSArtifact)artifact;
-        }
-        else {
-            flysArtifact = null;
-        }
-
-
-        Map<String, Object> data = splitParameters(
-            parameters, new HashMap<String, Object>());
-
-        String [] outs = outsString == null
-            ? new String [0]
-            : outsString.split("\\s*,\\s*");
-
-        Recommendations rec = Recommendations.getInstance();
-        rec.recommend(
-            flysArtifact, userId, outs, data, result);
-
-        return result;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/ModuleService.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,62 +0,0 @@
-package de.intevation.flys.artifacts.services;
-
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-import de.intevation.artifacts.common.utils.XMLUtils;
-
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.GlobalContext;
-import de.intevation.artifacts.ArtifactNamespaceContext;
-import de.intevation.flys.artifacts.model.Module;
-import de.intevation.flys.artifacts.context.FLYSContext;
-import de.intevation.flys.artifacts.resources.Resources;
-
-public class ModuleService extends FLYSService {
-
-    private static final String MODULE = "module";
-
-    private static Logger logger = Logger.getLogger(ModuleService.class);
-
-    protected Document doProcess(
-        Document      data,
-        GlobalContext globalContext,
-        CallMeta      callMeta
-    ) {
-        logger.debug("ModuleService.process");
-
-        Document result = XMLUtils.newDocument();
-
-        XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(
-            result,
-            ArtifactNamespaceContext.NAMESPACE_URI,
-            ArtifactNamespaceContext.NAMESPACE_PREFIX);
-
-        Element em = ec.create("modules");
-        List<Module> modules = (List<Module>)globalContext.get(FLYSContext.MODULES);
-
-        for (Module module : modules) {
-            Element m = ec.create("module");
-            ec.addAttr(m, "name", module.getName(), true);
-            String localname = Resources.getMsg(callMeta,
-                    MODULE + "." + module.getName(), module.getName());
-            ec.addAttr(m, "localname", localname, true);
-
-            if (module.isSelected()) {
-                ec.addAttr(m, "selected", "true", true);
-            }
-
-            em.appendChild(m);
-        }
-
-        result.appendChild(em);
-
-        return result;
-    }
-}
-
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 tw=80:
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/QWSeriesCollection.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,227 +0,0 @@
-package de.intevation.flys.artifacts.services;
-
-import de.intevation.flys.artifacts.model.fixings.QWI;
-
-import de.intevation.flys.java2d.ShapeUtils;
-
-import de.intevation.flys.jfree.ShapeRenderer;
-
-import java.awt.BasicStroke;
-import java.awt.Color;
-import java.awt.Paint;
-import java.awt.Shape;
-
-import java.awt.geom.Rectangle2D;
-
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.jfree.chart.LegendItem;
-import org.jfree.chart.LegendItemCollection;
-
-import org.jfree.chart.labels.XYItemLabelGenerator;
-
-import org.jfree.chart.renderer.xy.StandardXYItemRenderer;
-
-import org.jfree.data.xy.XYDataset;
-import org.jfree.data.xy.XYSeries;
-import org.jfree.data.xy.XYSeriesCollection;
-
-public class QWSeriesCollection
-extends      XYSeriesCollection
-implements   XYItemLabelGenerator
-{
-    public interface LabelGenerator {
-        String createLabel(QWI qw);
-    } // interface LabelGenerator
-
-    public static class DateFormatLabelGenerator
-    implements          LabelGenerator
-    {
-        protected DateFormat format;
-
-        public DateFormatLabelGenerator() {
-            this("dd.MM.yyyy");
-        }
-
-        public DateFormatLabelGenerator(String format) {
-            this(new SimpleDateFormat(format));
-        }
-
-        public DateFormatLabelGenerator(DateFormat format) {
-            this.format = format;
-        }
-
-        @Override
-        public String createLabel(QWI qw) {
-            Date date = qw.getDate();
-            return date != null ? format.format(date) : "";
-        }
-    } // class DateFormatLabelGenerator
-
-    public static final LabelGenerator SIMPLE_GENERATOR =
-        new DateFormatLabelGenerator();
-
-    protected Date minDate;
-    protected Date maxDate;
-
-    protected List<List<QWI>> labels;
-
-    protected Rectangle2D area;
-
-    protected LabelGenerator labelGenerator;
-
-    protected Map<ShapeRenderer.Entry, Integer> knownShapes =
-        new HashMap<ShapeRenderer.Entry, Integer>();
-
-    public QWSeriesCollection() {
-        labels = new ArrayList<List<QWI>>();
-        labelGenerator = SIMPLE_GENERATOR;
-    }
-
-    public QWSeriesCollection(LabelGenerator labelGenerator) {
-        this();
-        this.labelGenerator = labelGenerator;
-    }
-
-    protected static ShapeRenderer.Entry classify(QWI qw) {
-        boolean interpolated = qw.getInterpolated();
-
-        Shape shape = interpolated
-            ? ShapeUtils.INTERPOLATED_SHAPE
-            : ShapeUtils.MEASURED_SHAPE;
-
-        boolean filled = !interpolated;
-        Color color = Color.blue;
-
-        return new ShapeRenderer.Entry(shape, color, filled);
-    }
-
-    public void add(QWI qw) {
-
-        ShapeRenderer.Entry key = classify(qw);
-
-        Integer seriesNo = knownShapes.get(key);
-
-        XYSeries series;
-
-        if (seriesNo == null) {
-            seriesNo = Integer.valueOf(getSeriesCount());
-            knownShapes.put(key, seriesNo);
-            series = new XYSeries(seriesNo, false);
-            addSeries(series);
-            labels.add(new ArrayList<QWI>());
-        }
-        else {
-            series = getSeries(seriesNo);
-        }
-
-        series.add(qw.getQ(), qw.getW());
-
-        labels.get(seriesNo).add(qw);
-
-        extendDateRange(qw);
-        extendArea(qw);
-    }
-
-    protected void extendDateRange(QWI qw) {
-        Date date = qw.getDate();
-        if (date != null) {
-            if (minDate == null) {
-                minDate = maxDate = date;
-            }
-            else {
-                if (date.compareTo(minDate) < 0) {
-                    minDate = date;
-                }
-                if (date.compareTo(maxDate) > 0) {
-                    maxDate = date;
-                }
-            }
-        }
-    }
-
-    protected void extendArea(QWI qw) {
-        if (area == null) {
-            area = new Rectangle2D.Double(
-                qw.getQ(), qw.getW(), 0d, 0d);
-        }
-        else {
-            area.add(qw.getQ(), qw.getW());
-        }
-    }
-
-    public Rectangle2D getArea() {
-        return area;
-    }
-
-    public Date getMinDate() {
-        return minDate;
-    }
-
-    public Date getMaxDate() {
-        return maxDate;
-    }
-
-    public LabelGenerator getLabelGenerator() {
-        return labelGenerator;
-    }
-
-    @Override
-    public String generateLabel(XYDataset dataset, int series, int item) {
-        return labelGenerator.createLabel(labels.get(series).get(item));
-    }
-
-    public StandardXYItemRenderer createRenderer() {
-        StandardXYItemRenderer renderer = new ShapeRenderer(knownShapes);
-        renderer.setBaseItemLabelGenerator(this);
-        renderer.setBaseSeriesVisibleInLegend(false);
-        renderer.setBaseItemLabelsVisible(true);
-        return renderer;
-    }
-
-    public static final LegendItem legendItem(
-        String  label,
-        Paint   paint,
-        Shape   shape,
-        boolean filled
-    ) {
-        BasicStroke stroke = new BasicStroke();
-        return new LegendItem(
-            label,  // label
-            null,   // description
-            null,   // tooltip
-            null,   // url
-            true,   // shape visible
-            shape,  // shape
-            filled, // shape filled
-            filled ? paint : Color.white, // fill paint
-            true,   // shape outline
-            paint,  // outline paint
-            stroke, // outline stroke
-            false,  // line visible
-            shape,  // line
-            stroke, // line stroke
-            Color.white);
-    }
-
-    public void addLegendItems(
-        LegendItemCollection         lic,
-        ShapeRenderer.LabelGenerator lg
-    ) {
-        for (ShapeRenderer.Entry entry: knownShapes.keySet()) {
-            lic.add(legendItem(
-                lg.createLabel(entry),
-                entry.getPaint(),
-                entry.getShape(),
-                entry.getFilled()));
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/RiverInfoService.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,85 +0,0 @@
-package de.intevation.flys.artifacts.services;
-
-import java.math.BigDecimal;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.GlobalContext;
-import de.intevation.artifacts.common.ArtifactNamespaceContext;
-import de.intevation.artifacts.common.utils.XMLUtils;
-
-import de.intevation.flys.artifacts.model.RiverFactory;
-import de.intevation.flys.model.River;
-
-/**
- * @author <a href="mailto:bjoern.ricks@intevation.de">Björn Ricks</a>
- */
-public class RiverInfoService extends FLYSService {
-
-    private static final Logger logger = Logger.getLogger(
-            RiverInfoService.class);
-
-    protected static final String RIVER_XPATH = "/art:river/text()";
-
-    protected XMLUtils.ElementCreator ec;
-    protected River river;
-    protected Element riverele;
-
-    protected Document doProcess(
-        Document      data,
-        GlobalContext globalContext,
-        CallMeta      callMeta
-    ) {
-        String rivername = XMLUtils.xpathString(
-            data, RIVER_XPATH, ArtifactNamespaceContext.INSTANCE);
-
-        river = RiverFactory.getRiver(rivername);
-
-        Document result = XMLUtils.newDocument();
-
-        if (river == null) {
-            logger.warn("No river with name " + rivername + " found.");
-            return null;
-        }
-
-        ec = new XMLUtils.ElementCreator(
-            result,
-            ArtifactNamespaceContext.NAMESPACE_URI,
-            ArtifactNamespaceContext.NAMESPACE_PREFIX);
-
-        riverele = ec.create("river-info");
-
-        double[] minmax  = river.determineMinMaxDistance();
-        double[] minmaxq = river.determineMinMaxQ();
-
-        Element r = ec.create("river");
-        ec.addAttr(r, "name", river.getName(), true);
-        ec.addAttr(r, "start", Double.toString(minmax[0]), true);
-        ec.addAttr(r, "end", Double.toString(minmax[1]), true);
-        ec.addAttr(r, "wstunit", river.getWstUnit().getName(), true);
-        ec.addAttr(r, "kmup", Boolean.toString(river.getKmUp()), true);
-        ec.addAttr(r, "minq", Double.toString(minmaxq[0]), true);
-        ec.addAttr(r, "maxq", Double.toString(minmaxq[1]), true);
-        ec.addAttr(r, "official", Long.toString(river.getOfficialNumber()),
-                    true);
-
-        riverele.appendChild(r);
-        result.appendChild(riverele);
-
-        return result;
-    }
-
-    /**
-     * Returns a Double as String from a BigDecimal value.
-     *
-     * If value is null an empty String is returned.
-     */
-    protected static String getStringValue(BigDecimal value) {
-        return value != null
-            ? Double.toString(value.doubleValue()) : "";
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/RiverService.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,63 +0,0 @@
-package de.intevation.flys.artifacts.services;
-
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.GlobalContext;
-
-import de.intevation.artifacts.common.ArtifactNamespaceContext;
-import de.intevation.artifacts.common.utils.XMLUtils;
-
-import de.intevation.flys.model.River;
-
-import de.intevation.flys.artifacts.model.RiverFactory;
-
-
-/**
- * This service provides information about the supported rivers by this
- * application.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class RiverService extends FLYSService {
-
-    /** The logger used in this service.*/
-    private static Logger logger = Logger.getLogger(RiverService.class);
-
-
-    protected Document doProcess(
-        Document      data,
-        GlobalContext globalContext,
-        CallMeta      callMeta
-    ) {
-        logger.debug("RiverService.process");
-
-        Document result = XMLUtils.newDocument();
-
-        XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(
-            result,
-            ArtifactNamespaceContext.NAMESPACE_URI,
-            ArtifactNamespaceContext.NAMESPACE_PREFIX);
-
-        List<River> allRivers = RiverFactory.getRivers();
-
-        Element rivers = ec.create("rivers");
-
-        for (River river: allRivers) {
-            Element r = ec.create("river");
-            ec.addAttr(r, "name", river.getName(), true);
-
-            rivers.appendChild(r);
-        }
-
-        result.appendChild(rivers);
-
-        return result;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/SQKMChartService.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,316 +0,0 @@
-package de.intevation.flys.artifacts.services;
-
-import de.intevation.artifactdatabase.DefaultService;
-
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.GlobalContext;
-import de.intevation.artifacts.Service;
-
-import de.intevation.flys.artifacts.model.SQOverview;
-import de.intevation.flys.artifacts.model.SQOverviewFactory;
-
-import de.intevation.flys.artifacts.resources.Resources;
-
-import de.intevation.flys.backend.SedDBSessionHolder;
-
-import de.intevation.flys.utils.KMIndex;
-
-import java.awt.Color;
-import java.awt.Dimension;
-import java.awt.Transparency;
-
-import java.awt.image.BufferedImage;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-
-import java.util.Date;
-import java.util.List;
-
-import javax.imageio.ImageIO;
-
-import org.apache.log4j.Logger;
-
-import org.jfree.chart.ChartFactory;
-import org.jfree.chart.JFreeChart;
-
-import org.jfree.chart.axis.DateAxis;
-
-import org.jfree.chart.plot.PlotOrientation;
-import org.jfree.chart.plot.XYPlot;
-
-import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
-
-import org.jfree.data.xy.XYSeries;
-import org.jfree.data.xy.XYSeriesCollection;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.NodeList;
-
-public class SQKMChartService
-extends DefaultService {
-
-    private static final Logger log =
-        Logger.getLogger(SQKMChartService.class);
-
-    public static final int DEFAULT_WIDTH  = 240;
-    public static final int DEFAULT_HEIGHT = 180;
-
-    public static final String I18N_CHART_LABEL =
-        "sq.km.chart.label";
-
-    public static final String DEFAULT_CHART_LABEL =
-        "Measuring Points";
-
-    public static final String I18N_CHART_TITLE =
-        "sq.km.chart.title";
-
-    public static final String DEFAULT_CHART_TITLE =
-        "Measuring points";
-
-    public static final String I18N_KM_AXIS =
-        "sq.km.chart.km.axis";
-
-    public static final String DEFAULT_KM_AXIS =
-        "km";
-
-    public static final String I18N_DATE_AXIS =
-        "sq.km.chart.date.axis";
-
-    public static final String DEFAULT_DATE_AXIS =
-        "Date";
-
-    public static final String DEFAULT_FORMAT = "png";
-
-    // TODO: Load fancy image from resources.
-    public static final byte [] EMPTY = {
-        (byte)0x89, (byte)0x50, (byte)0x4e, (byte)0x47,
-        (byte)0x0d, (byte)0x0a, (byte)0x1a, (byte)0x0a,
-        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x0d,
-        (byte)0x49, (byte)0x48, (byte)0x44, (byte)0x52,
-        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01,
-        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01,
-        (byte)0x08, (byte)0x00, (byte)0x00, (byte)0x00,
-        (byte)0x00, (byte)0x3a, (byte)0x7e, (byte)0x9b,
-        (byte)0x55, (byte)0x00, (byte)0x00, (byte)0x00,
-        (byte)0x01, (byte)0x73, (byte)0x52, (byte)0x47,
-        (byte)0x42, (byte)0x00, (byte)0xae, (byte)0xce,
-        (byte)0x1c, (byte)0xe9, (byte)0x00, (byte)0x00,
-        (byte)0x00, (byte)0x09, (byte)0x70, (byte)0x48,
-        (byte)0x59, (byte)0x73, (byte)0x00, (byte)0x00,
-        (byte)0x0b, (byte)0x13, (byte)0x00, (byte)0x00,
-        (byte)0x0b, (byte)0x13, (byte)0x01, (byte)0x00,
-        (byte)0x9a, (byte)0x9c, (byte)0x18, (byte)0x00,
-        (byte)0x00, (byte)0x00, (byte)0x07, (byte)0x74,
-        (byte)0x49, (byte)0x4d, (byte)0x45, (byte)0x07,
-        (byte)0xdc, (byte)0x04, (byte)0x04, (byte)0x10,
-        (byte)0x30, (byte)0x15, (byte)0x7d, (byte)0x77,
-        (byte)0x36, (byte)0x0b, (byte)0x00, (byte)0x00,
-        (byte)0x00, (byte)0x08, (byte)0x74, (byte)0x45,
-        (byte)0x58, (byte)0x74, (byte)0x43, (byte)0x6f,
-        (byte)0x6d, (byte)0x6d, (byte)0x65, (byte)0x6e,
-        (byte)0x74, (byte)0x00, (byte)0xf6, (byte)0xcc,
-        (byte)0x96, (byte)0xbf, (byte)0x00, (byte)0x00,
-        (byte)0x00, (byte)0x0a, (byte)0x49, (byte)0x44,
-        (byte)0x41, (byte)0x54, (byte)0x08, (byte)0xd7,
-        (byte)0x63, (byte)0xf8, (byte)0x0f, (byte)0x00,
-        (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x00,
-        (byte)0x1b, (byte)0xb6, (byte)0xee, (byte)0x56,
-        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
-        (byte)0x49, (byte)0x45, (byte)0x4e, (byte)0x44,
-        (byte)0xae, (byte)0x42, (byte)0x60, (byte)0x82
-    };
-
-    private static final Output empty() {
-        return new Output(EMPTY, "image/png");
-    }
-
-    @Override
-    public Service.Output process(
-        Document      data,
-        GlobalContext globalContext,
-        CallMeta      callMeta
-    ) {
-        log.debug("SQKMChartService.process");
-
-        SedDBSessionHolder.acquire();
-        try {
-            return doProcess(data, globalContext, callMeta);
-        }
-        finally {
-            SedDBSessionHolder.HOLDER.get().close();
-            SedDBSessionHolder.release();
-        }
-    }
-
-    protected Service.Output doProcess(
-        Document      input,
-        GlobalContext globalContext,
-        CallMeta      callMeta
-    ) {
-        String    river  = getRiverName(input);
-        Dimension extent = getExtent(input);
-        String    format = getFormat(input);
-
-        if (river == null) {
-            log.warn("River invalid.");
-            return empty();
-        }
-
-        SQOverview overview = SQOverviewFactory.getOverview(river);
-
-        if (overview == null) {
-            log.warn("No overview found for river '" + river + "'");
-            return empty();
-        }
-
-        KMIndex<List<Date>> entries = overview.filter(SQOverview.ACCEPT);
-
-        JFreeChart chart = createChart(entries, river, callMeta);
-
-        return encode(chart, extent, format);
-    }
-
-    protected static Output encode(
-        JFreeChart chart,
-        Dimension  extent,
-        String     format
-    ) {
-        BufferedImage image = chart.createBufferedImage(
-            extent.width, extent.height,
-            Transparency.BITMASK,
-            null);
-
-        ByteArrayOutputStream out = new ByteArrayOutputStream();
-
-        try {
-            ImageIO.write(image, format, out);
-        }
-        catch (IOException ioe) {
-            log.warn("writing image failed", ioe);
-            return empty();
-        }
-
-        return new Output(out.toByteArray(), "image/" + format);
-    }
-
-    protected static JFreeChart createChart(
-        KMIndex<List<Date>> entries,
-        String      river,
-        CallMeta    callMeta
-    ) {
-
-        XYSeriesCollection dataset = new XYSeriesCollection();
-        String key = Resources.format(
-            callMeta, I18N_CHART_LABEL, DEFAULT_CHART_LABEL, river);
-
-        XYSeries series = new XYSeries(key);
-        for (KMIndex.Entry<List<Date>> e: entries) {
-            double km = e.getKm();
-            List<Date> ds = e.getValue();
-            for (Date d: ds) {
-                series.add(km, d.getTime());
-            }
-        }
-
-        dataset.addSeries(series);
-        String title = Resources.format(
-            callMeta, I18N_CHART_TITLE, DEFAULT_CHART_TITLE, river);
-
-        String kmAxis = Resources.getMsg(
-            callMeta, I18N_KM_AXIS, DEFAULT_KM_AXIS);
-
-        String dateAxis = Resources.getMsg(
-            callMeta, I18N_DATE_AXIS, DEFAULT_DATE_AXIS);
-
-        JFreeChart chart = ChartFactory.createXYLineChart(
-            title,
-            kmAxis,
-            dateAxis,
-            null,
-            PlotOrientation.VERTICAL,
-            true,
-            true,
-            false);
-
-        XYPlot plot = (XYPlot)chart.getPlot();
-
-        DateAxis dA = new DateAxis();
-        plot.setRangeAxis(dA);
-        plot.setDataset(0, dataset);
-
-        chart.setBackgroundPaint(Color.white);
-        plot.setBackgroundPaint(Color.white);
-        plot.setDomainGridlinePaint(Color.gray);
-        plot.setRangeGridlinePaint(Color.gray);
-        plot.setDomainGridlinesVisible(true);
-        plot.setRangeGridlinesVisible(true);
-        XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer) plot.getRenderer();
-
-        renderer.setSeriesPaint(0, Color.gray);
-        renderer.setSeriesLinesVisible(0, false);
-        renderer.setSeriesShapesVisible(0, true);
-        renderer.setDrawOutlines(true);
-        return chart;
-    }
-
-
-    protected static String getRiverName(Document input) {
-        NodeList rivers = input.getElementsByTagName("river");
-
-        if (rivers.getLength() == 0) {
-            return null;
-        }
-
-        String river = ((Element)rivers.item(0)).getAttribute("name");
-
-        return river.length() > 0 ? river : null;
-    }
-
-    protected static Dimension getExtent(Document input) {
-
-        int width  = DEFAULT_WIDTH;
-        int height = DEFAULT_HEIGHT;
-
-        NodeList extents = input.getElementsByTagName("extent");
-
-        if (extents.getLength() > 0) {
-            Element element = (Element)extents.item(0);
-            String w = element.getAttribute("width");
-            String h = element.getAttribute("height");
-
-            try {
-                width = Math.max(1, Integer.parseInt(w));
-            }
-            catch (NumberFormatException nfe) {
-                log.warn("width '" + w + "' is not a valid.");
-            }
-
-            try {
-                height = Math.max(1, Integer.parseInt(h));
-            }
-            catch (NumberFormatException nfe) {
-                log.warn("height '" + h + "' is not a valid");
-            }
-        }
-
-        return new Dimension(width, height);
-    }
-
-    protected static String getFormat(Document input) {
-        String format = DEFAULT_FORMAT;
-
-        NodeList formats = input.getElementsByTagName("format");
-
-        if (formats.getLength() > 0) {
-            String type = ((Element)formats.item(0)).getAttribute("type");
-            if (type.length() > 0) {
-                format = type;
-            }
-        }
-
-        return format;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/SedimentLoadInfoService.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,92 +0,0 @@
-package de.intevation.flys.artifacts.services;
-
-import java.util.Calendar;
-
-import org.apache.log4j.Logger;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-import de.intevation.artifacts.ArtifactNamespaceContext;
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.GlobalContext;
-import de.intevation.artifacts.common.utils.XMLUtils;
-import de.intevation.flys.artifacts.model.minfo.SedimentLoad;
-import de.intevation.flys.artifacts.model.minfo.SedimentLoadFactory;
-
-
-public class SedimentLoadInfoService
-extends FLYSService
-{
-    /** The logger used in this service. */
-    private static Logger logger = Logger.getLogger(SedimentLoadInfoService.class);
-
-    public static final String RIVER_XPATH = "/art:river/text()";
-    public static final String TYPE_XPATH = "/art:river/art:type/text()";
-    public static final String FROM_XPATH = "/art:river/art:location/art:from/text()";
-    public static final String TO_XPATH = "/art:river/art:location/art:to/text()";
-
-    @Override
-    protected Document doProcess(
-        Document data,
-        GlobalContext globalContext,
-        CallMeta callMeta) {
-        String river = XMLUtils.xpathString(
-            data,
-            RIVER_XPATH,
-            ArtifactNamespaceContext.INSTANCE);
-        String type = XMLUtils.xpathString(
-            data,
-            TYPE_XPATH,
-            ArtifactNamespaceContext.INSTANCE);
-        String from = XMLUtils.xpathString(
-            data,
-            FROM_XPATH,
-            ArtifactNamespaceContext.INSTANCE);
-        String to = XMLUtils.xpathString(
-            data,
-            TO_XPATH,
-            ArtifactNamespaceContext.INSTANCE);
-        double f, t;
-        try {
-            f = Double.parseDouble(from);
-            t = Double.parseDouble(to);
-        }
-        catch (NumberFormatException nfe) {
-            logger.warn("Invalid locations. Cannot return sediment loads.");
-            return XMLUtils.newDocument();
-        }
-
-        SedimentLoad[] loads = SedimentLoadFactory.getLoads(river, type, f, t);
-        return buildDocument(loads);
-    }
-
-    protected Document buildDocument(SedimentLoad[] loads) {
-        Document result = XMLUtils.newDocument();
-        Element all = result.createElement("sedimentloads");
-        for (SedimentLoad sl : loads) {
-            Element load = result.createElement("sedimentload");
-            load.setAttribute("description", sl.getDescription());
-            if (sl.isEpoch()) {
-                Calendar calendarS = Calendar.getInstance();
-                calendarS.setTime(sl.getStart());
-                Calendar calendarE = Calendar.getInstance();
-                calendarE.setTime(sl.getEnd());
-                load.setAttribute(
-                    "date",
-                    calendarS.get(Calendar.YEAR) +
-                        " - " +
-                        calendarE.get(Calendar.YEAR));
-            }
-            else {
-                Calendar calendar = Calendar.getInstance();
-                calendar.setTime(sl.getStart());
-                load.setAttribute(
-                    "date",
-                    String.valueOf(calendar.get(Calendar.YEAR)));
-            }
-            all.appendChild(load);
-        }
-        result.appendChild(all);
-        return result;
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/ThemeListingService.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,74 +0,0 @@
-package de.intevation.flys.artifacts.services;
-
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.GlobalContext;
-
-import de.intevation.artifacts.common.utils.XMLUtils;
-
-import de.intevation.flys.themes.Theme;
-import de.intevation.flys.themes.ThemeGroup;
-
-import de.intevation.flys.themes.ThemeFactory;
-import de.intevation.flys.artifacts.context.FLYSContext;
-
-/**
- * This service provides a list of themes filtered by the theme name.
- *
- * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
- */
-public class ThemeListingService extends FLYSService {
-
-    /** The logger used in this service.*/
-    private static Logger logger = Logger.getLogger(ThemeListingService.class);
-
-    private static final String XPATH_THEME_NAME = "/theme/@name";
-
-    protected Document doProcess(
-        Document      data,
-        GlobalContext context,
-        CallMeta      callMeta
-    ) {
-        logger.debug("ThemeListingService.process");
-        String name = XMLUtils.xpathString(data, XPATH_THEME_NAME, null);
-
-        if (name == null) {
-            logger.warn("No theme name provided.");
-        }
-        Document result = XMLUtils.newDocument();
-
-        XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(
-            result,
-            null,
-            null);
-
-        List<ThemeGroup> tgs =
-            ThemeFactory.getThemeGroups((FLYSContext) context);
-
-        Element te = ec.create("themes");
-
-        for (ThemeGroup tg: tgs) {
-            Element elem = ec.create("themegroup");
-            if (tg.getName().equals("virtual")) {
-                continue;
-            }
-            ec.addAttr(elem, "name", tg.getName());
-            Theme theme = tg.getThemeByName(name);
-            Document d = theme.toXML();
-            Node imported = result.importNode(d.getDocumentElement(), true);
-            elem.appendChild(imported);
-            te.appendChild(elem);
-        }
-
-        result.appendChild(te);
-        return result;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/AnnotationRiverState.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,71 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.artifactdatabase.state.Facet;
-
-import de.intevation.flys.artifacts.AnnotationArtifact;
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.flys.artifacts.model.AnnotationFacet;
-import de.intevation.flys.artifacts.model.FacetTypes;
-
-import de.intevation.flys.artifacts.resources.Resources;
-
-
-/**
- * The only state for an AnnotationArtifact (River is known).
- */
-public class AnnotationRiverState
-extends      DefaultState
-implements   FacetTypes
-{
-    /** Developer-centric description of facet. */
-    public static final String I18N_DESCRIPTION = "facet.longitudinal_section.annotations";
-
-    /** The logger that is used in this state. */
-    private static final Logger logger = Logger.getLogger(AnnotationRiverState.class);
-
-
-    /**
-     * Add an AnnotationFacet to list of Facets.
-     *
-     * @param artifact Ignored.
-     * @param hash Ignored.
-     * @param context Ignored.
-     * @param meta CallMeta to be used for internationalization.
-     * @param facets List to add AnnotationFacet to.
-     *
-     * @return null.
-     */
-    @Override
-    public Object computeInit(
-        FLYSArtifact artifact,
-        String       hash,
-        Object       context,
-        CallMeta     meta,
-        List<Facet>  facets
-    ) {
-        logger.debug("AnnotationRiverState.computeInit()");
-
-        AnnotationArtifact annotationArtifact = (AnnotationArtifact) artifact;
-
-        String facetName = annotationArtifact.getFilterName();
-        if (facetName == null) {
-            facetName = Resources.getMsg(meta, I18N_DESCRIPTION, I18N_DESCRIPTION);
-        }
-
-        AnnotationFacet facet = new AnnotationFacet(
-            0,
-            LONGITUDINAL_ANNOTATION,
-            facetName);
-        facets.add(facet);
-
-        return null;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/AreaCreationState.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,86 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.model.AreaFacet;
-
-import de.intevation.artifactdatabase.state.Facet;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.AreaArtifact;
-import de.intevation.flys.artifacts.model.FacetTypes;
-
-
-/** Trivial state to create areafacets, no caching. */
-public class AreaCreationState
-extends      DefaultState
-implements   FacetTypes
-{
-    /** The logger that is used in this state. */
-    private static Logger logger = Logger.getLogger(AreaCreationState.class);
-
-
-    /**
-     * From this state can only be continued trivially.
-     */
-    @Override
-    protected String getUIProvider() {
-        return "continue";
-    }
-
-
-    /** Just reproduce the Facet. */
-    protected Object compute(
-        FLYSArtifact  areaArtifact,
-        CallContext   cc,
-        String        hash,
-        List<Facet>   facets,
-        Object        old
-    ) {
-        logger.debug("AreaCreationState.compute");
-
-        if (facets != null) {
-            AreaArtifact aArt = (AreaArtifact) areaArtifact;
-
-            facets.add(new AreaFacet(0, aArt.getFacetName(), aArt.getAreaName()));
-        }
-
-        // TODO use compute to exploit caching strategies.
-
-        return null;
-    }
-
-
-    /**
-     */
-    @Override
-    public Object computeFeed(
-        FLYSArtifact artifact,
-        String       hash,
-        CallContext  context,
-        List<Facet>  facets,
-        Object       old
-    ) {
-        return compute((FLYSArtifact) artifact, context, hash, facets, old);
-    }
-
-
-    /**
-     *
-     */
-    @Override
-    public Object computeAdvance(
-        FLYSArtifact artifact,
-        String       hash,
-        CallContext  context,
-        List<Facet>  facets,
-        Object       old
-    ) {
-        return compute((FLYSArtifact) artifact, context, hash, facets, old);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/CalculationSelect.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,142 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Element;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.artifacts.common.utils.XMLUtils;
-
-import de.intevation.artifactdatabase.data.StateData;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.resources.Resources;
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class CalculationSelect extends DefaultState {
-
-    /** The logger that is used in this class. */
-    private static Logger logger = Logger.getLogger(CalculationSelect.class);
-
-    /** Name of data item. */
-    public static final String FIELD_MODE = "calculation_mode";
-
-    /** Constant value for the reference line calculation. */
-    public static final String CALCULATION_SURFACE_CURVE =
-        "calc.surface.curve";
-
-    /** Constant value for the differences calculation. */
-    public static final String CALCULATION_DURATION_CURVE =
-        "calc.duration.curve";
-
-    /** Constant value for the flood map calculation. */
-    public static final String CALCULATION_FLOOD_MAP =
-        "calc.flood.map";
-
-    /** Constant value for the profile calculation. */
-    public static final String CALCULATION_DISCHARGE_LONGITUDINAL_CURVE =
-        "calc.discharge.longitudinal.section";
-
-    /** Constant value for the state discharge curve calculation. */
-    public static final String CALCULATION_DISCHARGE_CURVE =
-        "calc.discharge.curve";
-
-    /** Constant value for the state w differences calculation. */
-    public static final String CALCULATION_W_DIFFERENCES =
-        "calc.w.differences";
-
-    /** Constant value for the state reference curve calculation. */
-    public static final String CALCULATION_REFERENCE_CURVE =
-        "calc.reference.curve";
-
-    /** Constant value for the historical discharge curve calculation. */
-    public static final String CALCULATION_HISTORICAL_DISCHARGE_CURVE =
-        "calc.historical.discharge.curve";
-
-    /** Constant value for the extreme W curve calculation. */
-    public static final String CALCULATION_EXTREME =
-        "calc.extreme.curve";
-
-    /** An array that holds all available calculation modes. */
-    public static final String[] CALCULATIONS = {
-        CALCULATION_SURFACE_CURVE,
-        CALCULATION_FLOOD_MAP,
-        CALCULATION_DISCHARGE_CURVE,
-        CALCULATION_HISTORICAL_DISCHARGE_CURVE,
-        CALCULATION_DURATION_CURVE,
-        CALCULATION_DISCHARGE_LONGITUDINAL_CURVE,
-        CALCULATION_W_DIFFERENCES,
-        CALCULATION_REFERENCE_CURVE,
-        CALCULATION_EXTREME};
-
-
-    /** Error message that is thrown if no mode has been chosen. */
-    public static final String ERROR_NO_CALCULATION_MODE =
-        "error_feed_no_calculation_mode";
-
-    /** Error message that is thrown if an invalid calculation mode has been
-     * chosen. */
-    public static  final String ERROR_INVALID_CALCULATION_MODE =
-        "error_feed_invalid_calculation_mode";
-
-
-    public CalculationSelect() {
-    }
-
-
-    /** Create choices (i18ned). */
-    @Override
-    protected Element[] createItems(
-        XMLUtils.ElementCreator cr,
-        Artifact    artifact,
-        String      name,
-        CallContext context)
-    {
-        CallMeta meta   = context.getMeta();
-        Element[] calcs = new Element[CALCULATIONS.length];
-
-        for (int i = 0; i < CALCULATIONS.length; ++i) {
-            String calc = CALCULATIONS[i];
-            calcs[i] = createItem(
-                cr, new String[] {
-                    Resources.getMsg(meta, calc, calc),
-                    calc
-                });
-        }
-
-        return calcs;
-    }
-
-
-    /** Validate the chosen calculation. */
-    @Override
-    public boolean validate(Artifact artifact)
-    throws IllegalArgumentException
-    {
-        logger.debug("CalculationSelect.validate");
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-
-        StateData data = getData(flys, FIELD_MODE);
-        String    calc = (data != null) ? (String) data.getValue() : null;
-
-        if (calc == null) {
-            throw new IllegalArgumentException(ERROR_NO_CALCULATION_MODE);
-        }
-
-        calc = calc.trim().toLowerCase();
-
-        for (String mode: CALCULATIONS) {
-            if (mode.equals(calc)) {
-                return true;
-            }
-        }
-
-        throw new IllegalArgumentException(ERROR_INVALID_CALCULATION_MODE);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/CalculationSelectMinfo.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,109 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Element;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.artifacts.common.utils.XMLUtils;
-
-import de.intevation.artifactdatabase.data.StateData;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.resources.Resources;
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class CalculationSelectMinfo extends DefaultState {
-
-    /** The logger that is used in this class. */
-    private static Logger logger = Logger.getLogger(CalculationSelectMinfo.class);
-
-
-    public static final String FIELD_MODE = "calculation_mode";
-
-    public static final String CALC_BED_MIDDLE    = "calc.bed.middle";
-    public static final String CALC_BED_DIFF      = "calc.bed.diff";
-    public static final String CALC_BED_QUALITY   = "calc.bed.quality";
-    public static final String CALC_SEDIMENT_LOAD = "calc.sediment.load";
-    public static final String CALC_FLOW_VELOCITY = "calc.flow.velocity";
-    public static final String CALC_SQ_RELATION   = "calc.sq.relation";
-
-    /** An array that holds all available calculation modes. */
-    public static final String[] CALCULATIONS = {
-        CALC_BED_MIDDLE,
-        CALC_BED_DIFF,
-        CALC_BED_QUALITY,
-        CALC_SEDIMENT_LOAD,
-        CALC_FLOW_VELOCITY,
-        CALC_SQ_RELATION
-    };
-
-
-    /** Error message that is thrown if no mode has been chosen. */
-    public static final String ERROR_NO_CALCULATION_MODE =
-        "error_feed_no_calculation_mode";
-
-    /** Error message that is thrown if an invalid calculation mode has been
-     * chosen. */
-    public static  final String ERROR_INVALID_CALCULATION_MODE =
-        "error_feed_invalid_calculation_mode";
-
-
-    public CalculationSelectMinfo() {
-    }
-
-
-    @Override
-    protected Element[] createItems(
-        XMLUtils.ElementCreator cr,
-        Artifact    artifact,
-        String      name,
-        CallContext context)
-    {
-        CallMeta meta   = context.getMeta();
-        Element[] calcs = new Element[CALCULATIONS.length];
-
-        for (int i = 0; i < CALCULATIONS.length; ++i) {
-            String calc = CALCULATIONS[i];
-            calcs[i] = createItem(
-                cr, new String[] {
-                    Resources.getMsg(meta, calc, calc),
-                    calc
-                });
-        }
-
-        return calcs;
-    }
-
-
-    @Override
-    public boolean validate(Artifact artifact)
-    throws IllegalArgumentException
-    {
-        logger.debug("CalculationSelect.validate");
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-
-        StateData data = getData(flys, FIELD_MODE);
-        String    calc = (data != null) ? (String) data.getValue() : null;
-
-        if (calc == null) {
-            throw new IllegalArgumentException(ERROR_NO_CALCULATION_MODE);
-        }
-
-        calc = calc.trim().toLowerCase();
-
-        for (String mode: CALCULATIONS) {
-            if (mode.equals(calc)) {
-                return true;
-            }
-        }
-
-        throw new IllegalArgumentException(ERROR_INVALID_CALCULATION_MODE);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/ComputationRangeState.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,145 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Element;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.artifacts.common.utils.XMLUtils;
-
-import de.intevation.artifactdatabase.ProtocolUtils;
-import de.intevation.artifactdatabase.data.StateData;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.resources.Resources;
-
-import de.intevation.flys.utils.FLYSUtils;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class ComputationRangeState
-extends      RangeState
-implements   FacetTypes
-{
-    private static Logger logger =
-        Logger.getLogger(ComputationRangeState.class);
-
-    /** The default step width. */
-    public static final int DEFAULT_STEP = 100;
-
-
-    public ComputationRangeState() {
-    }
-
-
-    @Override
-    protected Element createData(
-        XMLUtils.ElementCreator cr,
-        Artifact    artifact,
-        StateData   data,
-        CallContext context)
-    {
-        Element select = ProtocolUtils.createArtNode(
-            cr, "select", null, null);
-
-        cr.addAttr(select, "name", data.getName(), true);
-
-        Element label = ProtocolUtils.createArtNode(
-            cr, "label", null, null);
-
-        // XXX: DEAD CODE
-        /*
-        Element choices = ProtocolUtils.createArtNode(
-            cr, "choices", null, null);
-        */
-
-        label.setTextContent(Resources.getMsg(
-            context.getMeta(),
-            data.getName(),
-            data.getName()));
-
-        select.appendChild(label);
-
-        return select;
-    }
-
-
-    @Override
-    protected Element[] createItems(
-        XMLUtils.ElementCreator cr,
-        Artifact    artifact,
-        String      name,
-        CallContext context)
-    {
-        double[] minmax = getMinMax(artifact);
-
-        double minVal = Double.MIN_VALUE;
-        double maxVal = Double.MAX_VALUE;
-
-        if (minmax != null) {
-            minVal = minmax[0];
-            maxVal = minmax[1];
-        }
-        else {
-            logger.warn("Could not read min/max distance values!");
-        }
-
-        if (name.equals("ld_from")) {
-            Element min = createItem(
-                cr,
-                new String[] {"min", new Double(minVal).toString()});
-
-            return new Element[] { min };
-        }
-        else if (name.equals("ld_to")) {
-            Element max = createItem(
-                cr,
-                new String[] {"max", new Double(maxVal).toString()});
-
-            return new Element[] { max };
-        }
-        else {
-            Element step = createItem(
-                cr,
-                new String[] {"step", String.valueOf(getDefaultStep())});
-            return new Element[] { step };
-        }
-
-    }
-
-
-    protected Element createItem(XMLUtils.ElementCreator cr, Object obj) {
-        Element item  = ProtocolUtils.createArtNode(cr, "item", null, null);
-        Element label = ProtocolUtils.createArtNode(cr, "label", null, null);
-        Element value = ProtocolUtils.createArtNode(cr, "value", null, null);
-
-        String[] arr = (String[]) obj;
-
-        label.setTextContent(arr[0]);
-        value.setTextContent(arr[1]);
-
-        item.appendChild(label);
-        item.appendChild(value);
-
-        return item;
-    }
-
-
-    @Override
-    protected double[] getMinMax(Artifact artifact) {
-        FLYSArtifact flysArtifact = (FLYSArtifact) artifact;
-        return FLYSUtils.getRiverMinMax(flysArtifact);
-    }
-
-
-    protected double getDefaultStep() {
-        return DEFAULT_STEP;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/ComputedDischargeCurveState.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,105 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.artifactdatabase.state.Facet;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.WINFOArtifact;
-import de.intevation.flys.artifacts.ChartArtifact;
-
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.WaterlevelFacet;
-import de.intevation.flys.artifacts.model.DataFacet;
-import de.intevation.flys.artifacts.model.ReportFacet;
-import de.intevation.flys.artifacts.model.EmptyFacet;
-import de.intevation.flys.artifacts.model.WQKms;
-import de.intevation.flys.artifacts.model.CalculationResult;
-
-import de.intevation.flys.artifacts.resources.Resources;
-
-import de.intevation.flys.utils.FLYSUtils;
-
-/**
- * The final state that will be reached after the discharge curve calculation
- * mode has been chosen.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class ComputedDischargeCurveState
-extends      DefaultState
-implements   FacetTypes
-{
-    /** The logger that is used in this state. */
-    private static Logger logger =
-        Logger.getLogger(ComputedDischargeCurveState.class);
-
-
-    public ComputedDischargeCurveState() {
-    }
-
-
-    /**
-     * Get computed discharge curve data from cache (if available) or
-     * compute anew. Create Waterlevel and DataFacets.
-     */
-    @Override
-    public Object computeAdvance(
-        FLYSArtifact artifact,
-        String       hash,
-        CallContext  context,
-        List<Facet>  facets,
-        Object       old
-    ) {
-        logger.debug("ComputedDischargeCurveState.computeAdvance");
-        if(artifact instanceof WINFOArtifact) {
-            WINFOArtifact winfo = (WINFOArtifact)artifact;
-
-            CalculationResult res = old instanceof CalculationResult
-                ? (CalculationResult)old
-                : winfo.getComputedDischargeCurveData();
-
-            WQKms [] wqkms = (WQKms [])res.getData();
-
-            if (facets != null && wqkms.length > 0) {
-                for (int i = 0; i < wqkms.length; ++i) {
-
-                    Object[] args = new Object[] {
-                        FLYSUtils.getRiver(winfo).getName(),
-                        // Parse Double to allow i18n.
-                        Double.parseDouble(wqkms[i].getName())
-                    };
-
-                    String name = Resources.getMsg(
-                        context.getMeta(),
-                        "chart.computed.discharge.curve.curve.label",
-                        "",
-                        args);
-
-                    facets.add(new WaterlevelFacet(i, COMPUTED_DISCHARGE_Q, name));
-                    facets.add(new WaterlevelFacet(i, AT, "AT data"));
-                }
-
-                facets.add(new DataFacet(CSV, "CSV data"));
-                facets.add(new DataFacet(PDF, "PDF data"));
-
-                if (res.getReport().hasProblems()) {
-                    facets.add(new ReportFacet());
-                }
-            }
-
-            return res;
-        }
-        else if(artifact instanceof ChartArtifact) {
-            ChartArtifact chart = (ChartArtifact)artifact;
-            facets.add(new EmptyFacet());
-            return null;
-        }
-        return null;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/DGMSelect.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,140 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import java.io.File;
-
-import org.w3c.dom.Element;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
-
-import de.intevation.flys.model.DGM;
-import de.intevation.flys.model.River;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.utils.FLYSUtils;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class DGMSelect extends DefaultState {
-
-    private static final Logger logger = Logger.getLogger(DGMSelect.class);
-
-    public static final String ERR_EMPTY         = "error_no_dgm_selected";
-    public static final String ERR_INVALID_DGM   = "error_invalid_dgm_selected";
-    public static final String ERR_BAD_DGM_RANGE = "error_bad_dgm_range";
-    public static final String ERR_BAD_DGM_RIVER = "error_bad_dgm_river";
-
-
-    @Override
-    protected String getUIProvider() {
-        return "dgm_datacage_panel";
-    }
-
-
-    @Override
-    protected Element createStaticData(
-        FLYSArtifact   flys,
-        ElementCreator creator,
-        CallContext    cc,
-        String         name,
-        String         value,
-        String         type
-    ) {
-        Element dataElement = creator.create("data");
-        creator.addAttr(dataElement, "name", name, true);
-        creator.addAttr(dataElement, "type", type, true);
-
-        Element itemElement = creator.create("item");
-        creator.addAttr(itemElement, "value", value, true);
-
-        creator.addAttr(itemElement, "label", getLabel(cc, value), true);
-        dataElement.appendChild(itemElement);
-
-        return dataElement;
-    }
-
-
-    public static String getLabel(CallContext cc, String value) {
-        logger.debug("Create label for value: " + value);
-
-        try {
-            DGM dgm = DGM.getDGM(Integer.parseInt(value));
-
-            File file = new File(dgm.getPath());
-            return file.getName();
-        }
-        catch (NumberFormatException nfe) {
-            logger.warn("Cannot parse int value: '" + value + "'");
-        }
-
-        return "";
-    }
-
-
-    @Override
-    public boolean validate(Artifact artifact)
-    throws IllegalArgumentException
-    {
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-
-        DGM dgm = getDGM(flys);
-
-        if (dgm == null) {
-            throw new IllegalArgumentException(ERR_INVALID_DGM);
-        }
-
-        double l = dgm.getRange().getA().doubleValue();
-        double u = dgm.getRange().getB().doubleValue();
-
-        double[] range = FLYSUtils.getKmFromTo(flys);
-
-        if (range[0] < l || range[0] > u || range[1] < l || range[1] > u) {
-            throw new IllegalArgumentException(ERR_BAD_DGM_RANGE);
-        }
-
-        River selectedRiver = FLYSUtils.getRiver(flys);
-        River dgmRiver      = dgm.getRiver();
-
-        if (selectedRiver != dgmRiver) {
-            throw new IllegalArgumentException(ERR_BAD_DGM_RIVER);
-        }
-
-        return true;
-    }
-
-
-    /**
-     * Returns the DGM specified in the parameters of <i>flys</i>.
-     *
-     * @param flys The FLYSArtifact that knows the ID of a DGM.
-     *
-     * @throws IllegalArgumentException If the FLYSArtifact doesn't know the ID
-     * of a DGM.
-     *
-     * @return the DGM specified by FLYSArtifact's parameters.
-     */
-    public static DGM getDGM(FLYSArtifact flys)
-    throws IllegalArgumentException
-    {
-        try {
-            Integer dgmId = flys.getDataAsInteger("dgm");
-            if (dgmId == null) {
-                throw new IllegalArgumentException(ERR_EMPTY);
-            }
-
-            logger.debug("Found selected dgm: '" + dgmId + "'");
-
-            return DGM.getDGM(dgmId);
-        }
-        catch (NumberFormatException nfe) {
-            throw new IllegalArgumentException(ERR_INVALID_DGM);
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/DefaultState.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,476 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import java.text.NumberFormat;
-import java.util.Locale;
-import java.util.Map;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.ArtifactNamespaceContext;
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.artifacts.common.utils.XMLUtils;
-import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
-
-import de.intevation.artifactdatabase.ProtocolUtils;
-
-import de.intevation.artifactdatabase.data.StateData;
-
-import de.intevation.artifactdatabase.state.AbstractState;
-import de.intevation.artifactdatabase.state.Facet;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.flys.artifacts.resources.Resources;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public abstract class DefaultState extends AbstractState {
-
-    /** The logger that is used in this class. */
-    private static Logger logger = Logger.getLogger(DefaultState.class);
-
-
-    /** Determines, if the DESCRIBE document should contain default values or
-     * not. */
-    public static final boolean USE_DEFAULTS =
-        Boolean.getBoolean("flys.use.default.values");
-
-    /** The three possible compute types. */
-    public static enum ComputeType {
-        FEED, ADVANCE, INIT
-    }
-
-
-    protected StateData getData(FLYSArtifact artifact,  String name) {
-        return artifact.getData(name);
-    }
-
-
-    /**
-     * Append to a node and return xml description relevant for gui.
-     */
-    public Element describeStatic(
-        Artifact    artifact,
-        Document    document,
-        Node        root,
-        CallContext context,
-        String      uuid)
-    {
-        ElementCreator creator = new ElementCreator(
-            document,
-            ArtifactNamespaceContext.NAMESPACE_URI,
-            ArtifactNamespaceContext.NAMESPACE_PREFIX);
-
-        CallMeta meta = context.getMeta();
-
-        String helpText = getHelpText() != null ?
-            Resources.getMsg(meta, getHelpText(), getHelpText())
-            : null;
-
-        String label = Resources.getMsg(meta, getID(), getID());
-        Element ui   = ProtocolUtils.createArtNode(
-            creator, "state",
-            new String[] { "name", "uiprovider", "label", "helpText"},
-            new String[] { getID(), getUIProvider(), label, helpText });
-
-        Map<String, StateData> theData = getData();
-        if (theData == null) {
-            return ui;
-        }
-
-        FLYSArtifact flys = (FLYSArtifact)artifact;
-
-        for (String name: theData.keySet()) {
-            appendStaticData(flys, context, creator, ui, name);
-        }
-
-        return ui;
-    }
-
-
-    protected void appendStaticData(
-        FLYSArtifact   flys,
-        CallContext    context,
-        ElementCreator cr,
-        Element        ui,
-        String         name
-    ) {
-        StateData data  = getData(flys, name);
-        String    value = (data != null) ? (String) data.getValue() : null;
-
-        if (value == null) {
-            return;
-        }
-
-        String type = data.getType();
-
-        if (logger.isDebugEnabled()) {
-            logger.debug(
-                "Append element " + type + "'" +
-                name + "' (" + value + ")");
-        }
-
-        Element e = createStaticData(flys, cr, context, name, value, type);
-
-        ui.appendChild(e);
-
-    }
-
-
-    /**
-     * Creates a <i>data</i> element used in the static part of the DESCRIBE
-     * document.
-     *
-     * @param creator The ElementCreator that is used to build new Elements.
-     * @param cc The CallContext object used for nested i18n retrieval.
-     * @param name The name of the data item.
-     * @param value The value as string.
-     *
-     * @return an Element.
-     */
-    protected Element createStaticData(
-        FLYSArtifact   flys,
-        ElementCreator creator,
-        CallContext    cc,
-        String         name,
-        String         value,
-        String         type
-    ) {
-        Element dataElement = creator.create("data");
-        creator.addAttr(dataElement, "name", name, true);
-        creator.addAttr(dataElement, "type", type, true);
-
-        Element itemElement = creator.create("item");
-        creator.addAttr(itemElement, "value", value, true);
-
-        creator.addAttr(
-            itemElement,
-            "label",
-            getLabelFor(cc, name, value, type),
-            true);
-
-        dataElement.appendChild(itemElement);
-
-        return dataElement;
-    }
-
-
-    /**
-     * @param cc
-     * @param name
-     * @param value
-     * @param type
-     *
-     * @return
-     */
-    protected String getLabelFor(
-        CallContext cc,
-        String      name,
-        String      value,
-        String      type
-    ) {
-        CallMeta meta = cc.getMeta();
-
-        try {
-            // XXX A better way to format the output would be to use the
-            // 'type' value of the data objects.
-            double doubleVal = Double.parseDouble(value);
-            Locale         l = Resources.getLocale(meta);
-            NumberFormat  nf = NumberFormat.getInstance(l);
-
-            return nf.format(doubleVal);
-        }
-        catch (NumberFormatException nfe) {
-            return Resources.getMsg(meta, value, value);
-        }
-    }
-
-
-    /**
-     * This method returns the default value and description for <i>data</i>.
-     * Note, that this method returns the defaults only if <i>USE_DEFAULTS</i>
-     * is set; otherwise, null is returned.
-     * @param context The CallContext used for i18n.
-     * @param data The data objects that the defaults are for.
-     * @return a String[] with [default value, default label].
-     */
-    protected String[] getDefaultsFor(CallContext context, StateData data) {
-        if (USE_DEFAULTS) {
-            String defValue = (String) data.getValue();
-            String defDesc  = null;
-
-            if (defValue != null && defValue.length() > 0) {
-                defDesc = Resources.getMsg(
-                    context.getMeta(),
-                    defValue,
-                    defValue);
-            }
-
-            if (defValue != null && defDesc != null) {
-                return new String[] { defValue, defDesc };
-            }
-        }
-
-        return null;
-    }
-
-
-    public Element describe(
-        Artifact    artifact,
-        Document    document,
-        Node        root,
-        CallContext context,
-        String      uuid)
-    {
-        ElementCreator creator = new ElementCreator(
-            document,
-            ArtifactNamespaceContext.NAMESPACE_URI,
-            ArtifactNamespaceContext.NAMESPACE_PREFIX);
-
-        String helpText = Resources.getMsg(
-            context.getMeta(), getHelpText(), getHelpText());
-
-        Element ui        = null;
-        String uiprovider = getUIProvider();
-        if (uiprovider != null) {
-            ui = ProtocolUtils.createArtNode(
-                creator, "dynamic",
-                new String[] { "uiprovider", "helpText" },
-                new String[] { uiprovider, helpText });
-        }
-        else {
-            ui = ProtocolUtils.createArtNode(
-                creator, "dynamic",
-                new String[] { "helpText" },
-                new String[] { helpText });
-        }
-
-        Map<String, StateData> theData = getData();
-        if (theData == null) {
-            return ui;
-        }
-
-        FLYSArtifact flys = (FLYSArtifact)artifact;
-
-        for (String name: theData.keySet()) {
-            StateData data = getData(flys, name);
-
-            if (data == null) {
-                data = getData(name);
-            }
-
-            Element select = createData(creator, artifact, data, context);
-
-            String[] defaults = getDefaultsFor(context, data);
-            if (defaults != null && defaults.length > 1) {
-                creator.addAttr(select, "defaultValue", defaults[0], true);
-                creator.addAttr(select, "defaultLabel", defaults[1], true);
-            }
-
-            appendItems(artifact, creator, name, context, select);
-            ui.appendChild(select);
-        }
-
-        return ui;
-    }
-
-
-    /**
-     * @param artifact
-     * @param creator
-     * @param name
-     * @param context
-     * @param select
-     */
-    protected void appendItems(
-        Artifact       artifact,
-        ElementCreator creator,
-        String         name,
-        CallContext    context,
-        Element        select
-    ) {
-        Element choices = ProtocolUtils.createArtNode(
-            creator, "choices", null, null);
-
-        select.appendChild(choices);
-
-        Element[] items = createItems(creator, artifact, name, context);
-        if (items != null) {
-            for (Element item: items) {
-                choices.appendChild(item);
-            }
-        }
-    }
-
-
-    /**
-     * This method creates the root node that contains the list of selectable
-     * items.
-     *
-     * @param cr The ElementCreator.
-     *
-     * @return the root node of the item list.
-     */
-    protected Element createData(
-        ElementCreator cr,
-        Artifact    artifact,
-        StateData   data,
-        CallContext context)
-    {
-        Element select = ProtocolUtils.createArtNode(
-            cr, "select", null, null);
-        cr.addAttr(select, "name", data.getName(), true);
-
-        Element label = ProtocolUtils.createArtNode(
-            cr, "label", null, null);
-
-        select.appendChild(label);
-
-        label.setTextContent(Resources.getMsg(
-            context.getMeta(),
-            getID(),
-            getID()));
-
-        return select;
-    }
-
-
-    /**
-     * This method creates a list of items. These items represent the amount of
-     * input data that is possible for this state.
-     *
-     * @param cr The ElementCreator.
-     * @param name The name of the amount of data.
-     *
-     * @return a list of items.
-     */
-    protected Element[] createItems(
-        ElementCreator cr,
-        Artifact    artifact,
-        String      name,
-        CallContext context
-    ) {
-        return null;
-    }
-
-
-    /**
-     * This method is used to create an <i>item</i> Element that contains two
-     * further elements <i>label</i> and <i>value</i>. The label and value
-     * elements both have text nodes.
-     *
-     * @param cr The ElementCreator used to build new Elements.
-     * @param obj This implementation awaits a String array with [0] = label and
-     * [1] = value.
-     *
-     * @return an Element.
-     */
-    protected Element createItem(XMLUtils.ElementCreator cr, Object obj) {
-        Element item  = ProtocolUtils.createArtNode(cr, "item", null, null);
-        Element label = ProtocolUtils.createArtNode(cr, "label", null, null);
-        Element value = ProtocolUtils.createArtNode(cr, "value", null, null);
-
-        String[] arr = (String[]) obj;
-
-        label.setTextContent(arr[0]);
-        value.setTextContent(arr[1]);
-
-        item.appendChild(label);
-        item.appendChild(value);
-
-        return item;
-    }
-
-
-    /**
-     * This method transform a given value into a StateData object.
-     *
-     * @param flys The FLYSArtifact.
-     * @param name The name of the data object.
-     * @param val The value of the data object.
-     *
-     * @return a StateData object with <i>name</i> and <i>val</i>ue.
-     */
-    public StateData transform(
-        FLYSArtifact flys,
-        CallContext  cc,
-        StateData    stateData,
-        String       name,
-        String       val
-    ) {
-        if (logger.isDebugEnabled()) {
-            logger.debug("Transform data ('" + name + "','" + val + "')");
-        }
-
-        stateData.setValue(val);
-
-        return stateData;
-    }
-
-
-    /**
-     * This method validates the inserted data and returns true, if everything
-     * was correct, otherwise an exception is thrown.
-     *
-     * @param artifact A reference to the owner artifact.
-     *
-     * @return true, if everything was fine.
-     */
-    public boolean validate(Artifact artifact)
-    throws IllegalArgumentException
-    {
-        return true;
-    }
-
-
-    /**
-     * Returns which UIProvider shall be used to aid user input.
-     */
-    protected String getUIProvider() {
-        return null;
-    }
-
-
-    public Object computeAdvance(
-        FLYSArtifact artifact,
-        String       hash,
-        CallContext  context,
-        List<Facet>  facets,
-        Object       old
-    ) {
-        return null;
-    }
-
-
-    public Object computeFeed(
-        FLYSArtifact artifact,
-        String       hash,
-        CallContext  context,
-        List<Facet>  facets,
-        Object       old
-    ) {
-        return null;
-    }
-
-
-    public Object computeInit(
-        FLYSArtifact artifact,
-        String       hash,
-        Object       context,
-        CallMeta     meta,
-        List<Facet>  facets)
-    {
-        return null;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/DischargeLongitudinalSection.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,155 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.FacetActivity;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.ChartArtifact;
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.flys.artifacts.access.Calculation4Access;
-
-import de.intevation.flys.artifacts.model.Calculation4;
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.ConstantWQKms;
-import de.intevation.flys.artifacts.model.CrossSectionWaterLineFacet;
-import de.intevation.flys.artifacts.model.DataFacet;
-import de.intevation.flys.artifacts.model.EmptyFacet;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.ReportFacet;
-import de.intevation.flys.artifacts.model.WQCKms;
-import de.intevation.flys.artifacts.model.WQKms;
-import de.intevation.flys.artifacts.model.WaterlevelFacet;
-
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-public class DischargeLongitudinalSection
-extends      DefaultState
-implements   FacetTypes
-{
-    private static Logger log =
-        Logger.getLogger(DischargeLongitudinalSection.class);
-
-    static {
-        // Active/deactivate facets.
-        FacetActivity.Registry.getInstance().register(
-            "winfo",
-            new FacetActivity() {
-                @Override
-                public Boolean isInitialActive(
-                    Artifact artifact,
-                    Facet    facet,
-                    String   output
-                ) {
-                    String name = facet.getName();
-                    if (name.equals(DISCHARGE_LONGITUDINAL_Q_INFOLD_CUT)) {
-                        return Boolean.FALSE;
-                    }
-                    return Boolean.TRUE;
-                }
-            });
-    }
-
-    @Override
-    public Object computeAdvance(
-        FLYSArtifact artifact,
-        String       hash,
-        CallContext  context,
-        List<Facet>  facets,
-        Object       old
-    ) {
-        if (artifact instanceof ChartArtifact) {
-            ChartArtifact chart = (ChartArtifact)artifact;
-            facets.add(new EmptyFacet());
-            return null;
-        }
-
-        Calculation4Access access = new Calculation4Access(artifact);
-
-        CalculationResult res = old instanceof CalculationResult
-            ? (CalculationResult)old
-            : new Calculation4(access).calculate();
-
-        if (facets == null) {
-            return res;
-        }
-
-        WQKms [] wqkms = (WQKms [])res.getData();
-
-        for (int i = 0; i < wqkms.length; i++) {
-            String nameW = null;
-            String nameQ = null;
-
-            if (access.isQ()) {
-                nameQ = wqkms[i].getName();
-                nameW = "W(" + nameQ + ")";
-            }
-            else {
-                nameW = wqkms[i].getName();
-                nameQ = "Q(" + nameW + ")";
-            }
-
-            // Do not generate Waterlevel/Waterline facets
-            // for Q only curves.
-            if (!(wqkms[i] instanceof ConstantWQKms)) {
-
-                Facet w = new WaterlevelFacet(
-                    i, DISCHARGE_LONGITUDINAL_W, nameW);
-
-                Facet s = new CrossSectionWaterLineFacet(i, nameW);
-
-                Facet q = new WaterlevelFacet(
-                    i, DISCHARGE_LONGITUDINAL_Q, nameQ);
-                facets.add(s);
-                facets.add(w);
-                facets.add(q);
-            }
-            else {
-                Facet q;
-                if (nameQ.contains("geschnitten")) {
-                    q = new WaterlevelFacet(
-                        i, DISCHARGE_LONGITUDINAL_Q_INFOLD_CUT, nameQ);
-                }
-                else {
-                    q = new WaterlevelFacet(
-                        i, DISCHARGE_LONGITUDINAL_Q_INFOLD, nameQ);
-                }
-                facets.add(q);
-            }
-
-            if (wqkms[i] instanceof WQCKms) {
-                // TODO DO i18n
-
-                String nameC = nameW.replace(
-                    "benutzerdefiniert",
-                    "benutzerdefiniert [korrigiert]");
-
-                Facet c = new WaterlevelFacet(
-                    i, DISCHARGE_LONGITUDINAL_C, nameC);
-
-                // Here, avoid index clash with Facet "s" above and
-                // signal the WINFO later that we want to access Cs.
-                Facet r = new CrossSectionWaterLineFacet(i + 1, nameC);
-
-                facets.add(c);
-                facets.add(r);
-            }
-        }
-
-        if (wqkms.length > 0) {
-            facets.add(new DataFacet(CSV, "CSV data"));
-            facets.add(new DataFacet(WST, "WST data"));
-        }
-
-        if (res.getReport().hasProblems()) {
-            facets.add(new ReportFacet());
-        }
-
-        return res;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/DischargeState.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,161 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.artifacts.common.model.KVP;
-
-import de.intevation.flys.model.DischargeZone;
-import de.intevation.flys.model.River;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.utils.FLYSUtils;
-
-
-public class DischargeState extends MultiIntArrayState {
-
-    public static final String MAIN_CHANNEL  = "main_channel";
-    public static final String TOTAL_CHANNEL = "total_channel";
-
-
-    private static final Logger logger = Logger.getLogger(DischargeState.class);
-
-
-    /** Let client display a matrix. */
-    @Override
-    public String getUIProvider() {
-        return "parameter-matrix";
-    }
-
-
-    /**
-     * This method fetches all DischargeZones for a given river (extracted from
-     * <i>artifact</i>) and returns a KVP[] where the key is the ID of the
-     * DischargeZone and the value is a string that consists of lower discharge
-     * and upper discharge.
-     *
-     * @param artifact Needs to be a FLYSArtifact that provides river
-     * information.
-     * @param parameterName The name of a parameter.
-     *
-     * @return a KVP[].
-     */
-    @Override
-    protected KVP<Integer, String>[] getOptions(
-        Artifact artifact,
-        String   parameterName
-    )
-    throws IllegalArgumentException
-    {
-        if (!testParameterName(parameterName)) {
-            throw new IllegalArgumentException(
-                "Invalid parameter for state: '" + parameterName + "'");
-        }
-
-        List<DischargeZone> zones = getDischargeZones(artifact);
-
-        KVP[] kvp = new KVP[zones.size()];
-
-        for (int i = 0, Z = zones.size(); i < Z; i++) {
-            DischargeZone zone = zones.get(i);
-
-            String lower = zone.getLowerDischarge();
-            String upper = zone.getUpperDischarge();
-
-            if (lower.equals(upper)) {
-                kvp[i] = new KVP(zone.getId(), lower);
-            }
-            else {
-                kvp[i] = new KVP(zone.getId(), lower + " - " + upper);
-            }
-        }
-
-        return kvp;
-    }
-
-
-    @Override
-    protected String getLabelFor(
-        CallContext cc,
-        String      parameterName,
-        int         value
-    ) throws IllegalArgumentException
-    {
-        if (!testParameterName(parameterName)) {
-            throw new IllegalArgumentException(
-                "Invalid parameter for state: '" + parameterName + "'");
-        }
-
-        DischargeZone zone = DischargeZone.getDischargeZoneById(value);
-
-        if (zone == null) {
-            throw new IllegalArgumentException(
-                "Invalid id for DischargeZone: '" + value + "'");
-        }
-
-        String lo = zone.getLowerDischarge();
-        String hi = zone.getUpperDischarge();
-
-        return hi != null && !lo.equals(hi)
-            ? lo + " - " + hi
-            : lo;
-    }
-
-
-    /**
-     * This method might be used to test, if a parameter name is handled by this
-     * state.
-     *
-     * @param parameterName The name of a parameter.
-     *
-     * @return true, if parameterName is one of <i>MAIN_CHANNEL</i> or
-     * <i>TOTAL_CHANNEL</i>. Otherwise false.
-     */
-    protected boolean testParameterName(String parameterName) {
-        if (parameterName == null || parameterName.length() == 0) {
-            return false;
-        }
-        else if (parameterName.equals(MAIN_CHANNEL)) {
-            return true;
-        }
-        else if (parameterName.equals(TOTAL_CHANNEL)) {
-            return true;
-        }
-        else {
-            return false;
-        }
-    }
-
-
-    /**
-     * Returns all discharge zones for a given river. The river information is
-     * extracted from <i>artifact</i> using FLYSUtils.getRiver().
-     *
-     * @param artifact Needs to be a FLYSArtifact that stores a rivername.
-     *
-     * @return a list of DischargeZones.
-     *
-     * @throws IllegalArgumentException if no river information is provided by
-     * <i>artifact</i>.
-     */
-    protected List<DischargeZone> getDischargeZones(Artifact artifact)
-    throws IllegalArgumentException
-    {
-        River river = FLYSUtils.getRiver((FLYSArtifact) artifact);
-
-        if (river == null) {
-            throw new IllegalArgumentException("No river found");
-        }
-
-        List<DischargeZone> zones = DischargeZone.getDischargeZones(river);
-
-        logger.debug("Found " + zones.size() + " DischargeZones.");
-
-        return zones;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/DistanceOnlySelect.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,45 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.Artifact;
-
-import de.intevation.flys.artifacts.access.RangeAccess;
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-
-public class DistanceOnlySelect extends DistanceSelect {
-
-    private static Logger logger = Logger.getLogger(DistanceOnlySelect.class);
-
-    @Override
-    protected String getUIProvider() {
-        return "distance_only_panel";
-    }
-
-
-    @Override
-    public boolean validate(Artifact artifact)
-    throws IllegalArgumentException
-    {
-        // TODO think about better hierarchy wrt RangeState#validate.
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-
-        try {
-            RangeAccess rangeAccess = new RangeAccess(flys, null);
-            double from = rangeAccess.getFrom();
-            double to   = rangeAccess.getTo();
-
-            double[] minmax = getMinMax(flys);
-
-            return validateBounds(minmax[0], minmax[1], from, to);
-        }
-        catch (NumberFormatException nfe) {
-            throw new IllegalArgumentException("error_invalid_double_value");
-        }
-        catch (NullPointerException npe) {
-            throw new IllegalArgumentException("error_empty_state");
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/DistanceSelect.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,24 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import org.apache.log4j.Logger;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class DistanceSelect extends ComputationRangeState {
-
-    /** The logger used in this class. */
-    private static Logger logger = Logger.getLogger(DistanceSelect.class);
-
-
-    public DistanceSelect() {
-    }
-
-
-    @Override
-    protected String getUIProvider() {
-        return "distance_panel";
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/DurationCurveState.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,113 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.artifactdatabase.state.Facet;
-
-import de.intevation.flys.artifacts.access.RangeAccess;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.WINFOArtifact;
-import de.intevation.flys.artifacts.ChartArtifact;
-
-import de.intevation.flys.artifacts.model.DurationCurveFacet;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.WQDay;
-
-import de.intevation.flys.artifacts.model.DataFacet;
-import de.intevation.flys.artifacts.model.ReportFacet;
-import de.intevation.flys.artifacts.model.EmptyFacet;
-import de.intevation.flys.artifacts.model.CalculationResult;
-
-import de.intevation.flys.artifacts.resources.Resources;
-
-import de.intevation.flys.utils.FLYSUtils;
-
-
-/**
- * The final state that will be reached after the duration curve calculation
- * mode has been chosen.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class DurationCurveState
-extends      DefaultState
-implements   FacetTypes
-{
-    /** The logger that is used in this state. */
-    private static Logger logger = Logger.getLogger(DurationCurveState.class);
-
-    public DurationCurveState() {
-    }
-
-
-    @Override
-    public Object computeAdvance(
-        FLYSArtifact artifact,
-        String       hash,
-        CallContext  context,
-        List<Facet>  facets,
-        Object       old
-    ) {
-        if (artifact instanceof WINFOArtifact) {
-            WINFOArtifact winfo = (WINFOArtifact)artifact;
-
-            CalculationResult res;
-
-            if (old instanceof CalculationResult) {
-                res = (CalculationResult)old;
-            }
-            else {
-                res = winfo.getDurationCurveData();
-            }
-
-            WQDay wqday = (WQDay)res.getData();
-
-            if (wqday != null && facets != null) {
-                RangeAccess rangeAccess = new RangeAccess(winfo, context);
-                // Create an i18ed name for a (w or q) duration curve facet.
-                Object[] args = new Object[] {
-                    FLYSUtils.getRiver(winfo).getName(),
-                    rangeAccess.getLocations()[0]
-                };
-
-                String nameW = Resources.getMsg(
-                    context.getMeta(),
-                    "chart.duration.curve.curve.w",
-                    "",
-                    args);
-
-                String nameQ = Resources.getMsg(
-                    context.getMeta(),
-                    "chart.duration.curve.curve.q",
-                    "",
-                    args);
-
-                Facet w = new DurationCurveFacet(DURATION_W, nameW);
-                Facet q = new DurationCurveFacet(DURATION_Q, nameQ);
-
-                facets.add(w);
-                facets.add(q);
-
-                facets.add(new DataFacet(CSV, "CSV data"));
-                facets.add(new DataFacet(PDF, "PDF data"));
-
-                if (res.getReport().hasProblems()) {
-                    facets.add(new ReportFacet());
-                }
-            }
-
-            return res;
-        }
-        else if (artifact instanceof ChartArtifact) {
-            ChartArtifact chart = (ChartArtifact)artifact;
-            facets.add(new EmptyFacet());
-            return null;
-        }
-        return null;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/EnterLocationState.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.flys.utils.FLYSUtils;
-
-
-/**
- * Get me a double (km).
- */
-public class EnterLocationState extends InputDoubleState {
-
-    /** Provoke this kind of provider in the UI. */
-    @Override
-    protected String getUIProvider() {
-        return "location_panel";
-    }
-
-
-    /** Allow from min km of river. */
-    @Override
-    protected Object getLower(FLYSArtifact flys) {
-        double[] lowerUpper = FLYSUtils.getRiverMinMax(flys);
-
-        return lowerUpper != null
-            ? lowerUpper[0]
-            : 0;
-    }
-
-
-    /** Allow to max km of river. */
-    @Override
-    protected Object getUpper(FLYSArtifact flys) {
-        double[] lowerUpper = FLYSUtils.getRiverMinMax(flys);
-
-        return lowerUpper != null
-            ? lowerUpper[1]
-            : 0;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/EnterMultipleLocationsState.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,72 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import org.apache.log4j.Logger;
-import org.w3c.dom.Element;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.common.utils.StringUtils;
-import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
-import de.intevation.flys.artifacts.WINFOArtifact;
-
-
-/**
- * Get me doubles (km).
- */
-public class EnterMultipleLocationsState extends EnterLocationState {
-    /** The logger for this class. */
-    private static Logger logger = Logger.getLogger(EnterMultipleLocationsState.class);
-
-    @Override
-    protected String getUIProvider() {
-        logger.debug("multi location panel");
-        return "multi_location_panel";
-    }
-
-
-    /** Deal with multiple double values. */
-    @Override
-    protected String getLabelFor(
-        CallContext cc,
-        String      name,
-        String      value,
-        String      type
-    ) {
-        String[] vals = value.split(" ");
-        for (int i = 0; i < vals.length; i++) {
-            vals[i] = super.getLabelFor(cc, name, vals[i], type);
-        }
-
-        return StringUtils.join(" ", vals);
-    }
-
-    /**
-     * This method creates a list of items. These items represent the amount of
-     * input data that is possible for this state.
-     *
-     * @param cr The ElementCreator.
-     * @param name The name of the amount of data.
-     *
-     * @return a list of items.
-     */
-    @Override
-    protected Element[] createItems(
-        ElementCreator cr,
-        Artifact    artifact,
-        String      name,
-        CallContext context
-    ) {
-        if (name.equals("reference_endpoint")) {
-            Element[] elements = new Element[1];
-            WINFOArtifact winfo = (WINFOArtifact) artifact;
-            Double km = winfo.getReferenceStartKm();
-            elements[0] = createItem(
-                cr,
-                new String[] {"start_km", km.toString()});
-            return elements;
-        }
-        return null;
-    }
-
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1018 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import org.apache.velocity.Template;
-
-import org.geotools.feature.FeatureCollection;
-import org.geotools.feature.FeatureCollections;
-
-import org.geotools.feature.simple.SimpleFeatureBuilder;
-
-import org.hibernate.HibernateException;
-
-import org.opengis.feature.simple.SimpleFeature;
-import org.opengis.feature.simple.SimpleFeatureType;
-
-import com.vividsolutions.jts.geom.Coordinate;
-import com.vividsolutions.jts.geom.Geometry;
-import com.vividsolutions.jts.geom.LineString;
-import com.vividsolutions.jts.geom.Polygon;
-
-import de.intevation.artifactdatabase.state.Facet;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.GlobalContext;
-
-import de.intevation.artifacts.common.utils.FileTools;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.flys.artifacts.access.RangeAccess;
-
-import de.intevation.flys.artifacts.context.FLYSContext;
-
-import de.intevation.flys.artifacts.model.CalculationMessage;
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.LayerInfo;
-import de.intevation.flys.artifacts.model.WQKms;
-
-import de.intevation.flys.artifacts.model.map.HWS;
-import de.intevation.flys.artifacts.model.map.HWSContainer;
-import de.intevation.flys.artifacts.model.map.HWSFactory;
-import de.intevation.flys.artifacts.model.map.WMSLayerFacet;
-import de.intevation.flys.artifacts.model.map.WSPLGENCalculation;
-import de.intevation.flys.artifacts.model.map.WSPLGENJob;
-import de.intevation.flys.artifacts.model.map.WSPLGENReportFacet;
-
-import de.intevation.flys.artifacts.resources.Resources;
-
-import de.intevation.flys.exports.WstWriter;
-
-import de.intevation.flys.model.CrossSectionTrack;
-import de.intevation.flys.model.DGM;
-import de.intevation.flys.model.Floodplain;
-import de.intevation.flys.model.RiverAxis;
-
-import de.intevation.flys.utils.ArtifactMapfileGenerator;
-import de.intevation.flys.utils.FLYSUtils;
-import de.intevation.flys.utils.GeometryUtils;
-import de.intevation.flys.utils.MapfileGenerator;
-
-import de.intevation.flys.wsplgen.FacetCreator;
-import de.intevation.flys.wsplgen.JobObserver;
-import de.intevation.flys.wsplgen.Scheduler;
-
-public class FloodMapState
-extends      DefaultState
-implements   FacetTypes
-{
-    /** The logger that is used in this state. */
-    private static Logger logger = Logger.getLogger(FloodMapState.class);
-
-
-    public static final String KEEP_ARTIFACT_DIR =
-        System.getProperty("flys.uesk.keep.artifactsdir", "false");
-
-
-    public static final String OUTPUT_NAME = "floodmap";
-
-    public static final String WSP_ARTIFACT = "wsp";
-
-    public static final String WINFO_WSP_STATE_ID = "state.winfo.waterlevel";
-
-    public static final String WSPLGEN_PARAMETER_FILE = "wsplgen.par";
-    public static final String WSPLGEN_BARRIERS_LINES = "barrier_lines.shp";
-    public static final String WSPLGEN_BARRIERS_POLY  = "barrier_polygons.shp";
-    public static final String WSPLGEN_AXIS           = "axis.shp";
-    public static final String WSPLGEN_QPS            = "qps.shp";
-    public static final String WSPLGEN_FLOODPLAIN     = "talaue.shp";
-    public static final String WSPLGEN_WSP_FILE       = "waterlevel.wst";
-    public static final String WSPLGEN_OUTPUT_FILE    = "wsplgen.shp";
-    public static final String WSPLGEN_USER_SHAPE     = "user-rgd.shp";
-    public static final String WSPLGEN_USER_ZIP       = "user-rgd.zip";
-    public static final String WSPLGEN_USER_FILENAME  = "user-rgd";
-
-    public static final String WSPLGEN_QPS_NAME = "qps";
-
-    public static final int WSPLGEN_DEFAULT_OUTPUT = 0;
-
-    private static final String HWS_LINES_SHAPE = "hws-lines.shp";
-
-    private static final String I18N_HWS_POINTS_OFFICIAL = "floodmap.hws.points.official";
-    private static final String I18N_HWS_LINES_OFFICIAL = "floodmap.hws.lines.official";
-    private static final String HWS_LINES = "hws-lines";
-    private static final String HWS_POINT_SHAPE = "hws-points.shp";
-    private static final String HWS_POINTS = "hws-points";
-
-    /**
-     * @param orig
-     * @param owner
-     * @param context
-     * @param callMeta
-     */
-    @Override
-    public void initialize(
-        Artifact orig,
-        Artifact owner,
-        Object   context,
-        CallMeta callMeta
-    ) {
-        logger.info("Initialize State with Artifact: " + orig.identifier());
-
-        copyShapeDir(orig, owner);
-        modifyFacets(orig, owner, context, callMeta);
-
-        ArtifactMapfileGenerator amfg = new ArtifactMapfileGenerator();
-        try {
-            amfg.generate();
-        }
-        catch (IOException e) {
-            logger.error(e.getMessage(), e);
-        }
-    }
-
-
-    protected void copyShapeDir(Artifact orig, Artifact owner) {
-        File origDir = getDirectory((FLYSArtifact) orig);
-        File thisDir = getDirectory((FLYSArtifact) owner);
-
-        FileTools.copyDirectory(origDir, thisDir);
-    }
-
-
-    protected void modifyFacets(
-        Artifact orig,
-        Artifact owner,
-        Object   context,
-        CallMeta callMeta
-    ) {
-        FLYSArtifact flys  = (FLYSArtifact) owner;
-        List<Facet> facets = flys.getFacets();
-        if (facets == null || facets.isEmpty()) {
-            logger.warn("No facets for '" + OUTPUT_NAME + "' given!");
-            return;
-        }
-
-        for (Facet facet: facets) {
-            if (facet instanceof WMSLayerFacet) {
-                WMSLayerFacet wms = (WMSLayerFacet) facet;
-
-                List<String> layers = wms.getLayers();
-
-                for (String layer: layers) {
-                    if (layer.startsWith(MapfileGenerator.MS_WSPLGEN_PREFIX)) {
-                        wms.removeLayer(layer);
-
-                        String newLayer = MapfileGenerator.MS_WSPLGEN_PREFIX +
-                            owner.identifier();
-
-                        wms.addLayer(newLayer);
-
-                        logger.debug(
-                            "Replaced layer: " + layer + " with " + newLayer);
-                    }
-                }
-            }
-        }
-    }
-
-
-    @Override
-    public Object computeAdvance(
-        FLYSArtifact artifact,
-        String       hash,
-        CallContext  context,
-        List<Facet>  facets,
-        Object       old
-    ) {
-        logger.debug("FloodMapState.computeAdvance");
-
-        File artifactDir = getDirectory(artifact);
-
-        if (artifactDir == null) {
-            logger.error("Could not create directory for WSPLGEN results!");
-            return null;
-        }
-
-        WSPLGENCalculation calculation = new WSPLGENCalculation();
-
-        FacetCreator facetCreator = new FacetCreator(
-            artifact, context, hash, getID(), facets);
-
-        WSPLGENJob job = prepareWSPLGENJob(
-            artifact,
-            facetCreator,
-            artifactDir,
-            context,
-            calculation);
-
-        CalculationResult  res   = new CalculationResult(null, calculation);
-        WSPLGENReportFacet report= new WSPLGENReportFacet(
-            ComputeType.ADVANCE, hash, getID(), res);
-
-        facets.add(report);
-
-        if (job == null) {
-            if (KEEP_ARTIFACT_DIR.equals("false")) {
-                removeDirectory(artifact);
-            }
-
-            calculation.addError(-1, Resources.getMsg(
-                context.getMeta(),
-                "wsplgen.job.error",
-                "wsplgen.job.error"));
-
-            logger.error("No WSPLGEN processing has been started!");
-
-            return null;
-        }
-
-        context.afterCall(CallContext.BACKGROUND);
-        context.addBackgroundMessage(new CalculationMessage(
-            JobObserver.STEPS.length,
-            0,
-            Resources.getMsg(
-                context.getMeta(),
-                "wsplgen.job.queued",
-                "wsplgen.job.queued")
-        ));
-
-        GlobalContext gc    = (GlobalContext) context.globalContext();
-        Scheduler scheduler = (Scheduler) gc.get(FLYSContext.SCHEDULER);
-        scheduler.addJob(job);
-
-        return null;
-    }
-
-
-    /**
-     * Returns (and creates if not existing) the directory for storing WSPLEN
-     * data for the owner artifact.
-     *
-     * @param artifact The owner Artifact.
-     *
-     * @return the directory for WSPLEN data.
-     */
-    protected File getDirectory(FLYSArtifact artifact) {
-        String shapePath = FLYSUtils.getXPathString(
-            FLYSUtils.XPATH_FLOODMAP_SHAPEFILE_DIR);
-
-        File artifactDir = FileTools.getDirectory(
-            shapePath, artifact.identifier());
-
-        return artifactDir;
-    }
-
-
-    /**
-     * Removes the directory and all its content where the required data and the
-     * results of WSPLGEN are stored. Should be called in endOfLife().
-     */
-    protected void removeDirectory(FLYSArtifact artifact) {
-        String shapePath = FLYSUtils.getXPathString(
-            FLYSUtils.XPATH_FLOODMAP_SHAPEFILE_DIR);
-
-        File artifactDir = new File(shapePath, artifact.identifier());
-
-        if (artifactDir.exists()) {
-            logger.info("Delete directory: " + artifactDir.getAbsolutePath());
-            if (!FileTools.deleteRecursive(artifactDir)) {
-                logger.warn("Could not delete directory: "
-                        + artifactDir.getAbsolutePath());
-            }
-        }
-        else {
-            logger.debug("There is no directory to remove.");
-        }
-    }
-
-
-    @Override
-    public void endOfLife(Artifact artifact, Object callContext) {
-        logger.info("FloodMapState.endOfLife: " + artifact.identifier());
-
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-
-        Scheduler scheduler = Scheduler.getInstance();
-        scheduler.cancelJob(flys.identifier());
-    }
-
-
-    protected WSPLGENJob prepareWSPLGENJob(
-        FLYSArtifact       artifact,
-        FacetCreator       facetCreator,
-        File               artifactDir,
-        CallContext        context,
-        WSPLGENCalculation calculation
-    ) {
-        logger.debug("FloodMapState.prepareWSPLGENJob");
-        String scenario = artifact.getDataAsString("scenario");
-
-        WSPLGENJob job = new WSPLGENJob(
-            artifact,
-            artifactDir,
-            facetCreator,
-            context,
-            calculation);
-
-        File paraFile = new File(artifactDir, WSPLGEN_PARAMETER_FILE);
-
-        setOut(artifact, job);
-        setRange(artifact, job);
-        setDelta(artifact, job);
-        setGel(artifact, job);
-        setDist(artifact, job);
-        setAxis(artifact, artifactDir, job);
-        setPro(artifact, artifactDir, job);
-        setDgm(artifact, job, context);
-        setArea(artifact, artifactDir, job);
-        setOutFile(artifact, job);
-        setWsp(artifact, context, artifactDir, job);    // WSP
-        if (scenario.equals("scenario.current")) {
-            setOfficialHWS(artifact, facetCreator, artifactDir, job);
-        }
-        else if (scenario.equals("scenario.scenario")) {
-            setAdditionalHWS(artifact, facetCreator, artifactDir, job);
-            setLine(artifact, facetCreator, artifactDir, job);
-            setUserShape(artifact, facetCreator, artifactDir, job);
-        }
-        // TODO
-        // setWspTag(artifact, job);
-
-        try {
-            job.toFile(paraFile);
-
-            return job;
-        }
-        catch (IOException ioe) {
-            logger.warn("Cannot write PAR file: " + ioe.getMessage());
-        }
-        catch (IllegalArgumentException iae) {
-            logger.warn("Cannot write PAR file: " + iae.getMessage());
-        }
-
-        return null;
-    }
-
-
-    private void setAdditionalHWS(
-        FLYSArtifact artifact,
-        FacetCreator facetCreator,
-        File dir,
-        WSPLGENJob job) {
-        File line = new File(dir, HWS_LINES_SHAPE);
-        boolean lines = line.exists();
-        logger.debug("shp file exists: " + lines);
-        if (lines) {
-            job.addLin(dir + "/" + HWS_LINES_SHAPE);
-            facetCreator.createShapeFacet(I18N_HWS_LINES_OFFICIAL,
-                MapfileGenerator.MS_LAYER_PREFIX + HWS_LINES,
-                FLOODMAP_LINES, 2);
-        }
-        File point = new File(dir, HWS_POINT_SHAPE);
-        boolean points = point.exists();
-        logger.debug("shp file exists: " + points);
-        if (points) {
-            facetCreator.createShapeFacet(I18N_HWS_POINTS_OFFICIAL,
-                MapfileGenerator.MS_LAYER_PREFIX + HWS_POINTS,
-                FLOODMAP_FIXPOINTS, 3);
-        }
-    }
-
-
-    private void setOfficialHWS(
-        FLYSArtifact artifact,
-        FacetCreator facetCreator,
-        File artifactDir,
-        WSPLGENJob job) {
-        String river = artifact.getDataAsString("river");
-
-        HWSContainer hwsLines = HWSFactory.getHWSLines(river);
-        List<HWS> selectedLines = hwsLines.getOfficialHWS();
-
-        FeatureCollection collectionLines = FeatureCollections.newCollection();
-        SimpleFeatureType lineType = null;
-        for (HWS h : selectedLines) {
-            lineType = h.getFeatureType();
-            collectionLines.add(h.getFeature());
-        }
-        boolean successLines = false;
-        if (lineType != null && collectionLines.size() > 0) {
-            File shapeLines = new File(artifactDir, HWS_LINES_SHAPE);
-            successLines = GeometryUtils.writeShapefile(
-                shapeLines, lineType, collectionLines);
-        }
-       if (successLines) {
-            createMapfile(
-                artifact,
-                artifactDir,
-                MapfileGenerator.MS_LAYER_PREFIX + "hws-lines",
-                HWS_LINES_SHAPE,
-                "LINE",
-                "31467",
-                "hws");
-            job.addLin(artifactDir + "/" + HWS_LINES_SHAPE);
-            facetCreator.createShapeFacet(I18N_HWS_LINES_OFFICIAL,
-                MapfileGenerator.MS_LAYER_PREFIX + HWS_LINES,
-                FLOODMAP_HWS_LINES,2);
-        }
-    }
-
-
-    private void createMapfile(
-        FLYSArtifact artifact,
-        File artifactDir,
-        String name,
-        String hwsShapefile,
-        String type,
-        String srid,
-        String group
-    ) {
-        LayerInfo info = new LayerInfo();
-        info.setName(name + artifact.identifier());
-        info.setType(type);
-        info.setDirectory(artifact.identifier());
-        info.setTitle(name);
-        info.setData(hwsShapefile);
-        info.setSrid(srid);
-        info.setGroupTitle(group);
-        MapfileGenerator generator = new ArtifactMapfileGenerator();
-        Template tpl = generator.getTemplateByName(MapfileGenerator.SHP_LAYER_TEMPLATE);
-        try {
-            File layer = new File(artifactDir.getCanonicalPath() + "/" + name);
-            generator.writeLayer(info, layer, tpl);
-            List<String> layers = new ArrayList<String>();
-            layers.add(layer.getAbsolutePath());
-            generator.generate();
-        }
-        catch(FileNotFoundException fnfe) {
-            logger.warn("Could not find mapfile for hws layer");
-        }
-        catch (Exception ioe) {
-            logger.warn("Could not create mapfile for hws layer");
-            logger.warn(Arrays.toString(ioe.getStackTrace()));
-        }
-    }
-
-
-    protected void setOut(FLYSArtifact artifact, WSPLGENJob job) {
-        job.setOut(WSPLGEN_DEFAULT_OUTPUT);
-    }
-
-
-    protected void setRange(FLYSArtifact artifact, WSPLGENJob job) {
-        RangeAccess rangeAccess = new RangeAccess(artifact, null);
-        double[] range = rangeAccess.getKmRange();
-
-        job.setStart(range[0]);
-        job.setEnd(range[1]);
-    }
-
-
-    protected void setDelta(FLYSArtifact artifact, WSPLGENJob job) {
-        String from = artifact.getDataAsString("diff_from");
-        String to   = artifact.getDataAsString("diff_to");
-        String diff = artifact.getDataAsString("diff_diff");
-
-        try {
-            job.setFrom(Double.parseDouble(from));
-        }
-        catch (NumberFormatException nfe) {
-        }
-
-        try {
-            job.setTo(Double.parseDouble(to));
-        }
-        catch (NumberFormatException nfe) {
-        }
-
-        try {
-            job.setDiff(Double.parseDouble(diff));
-        }
-        catch (NumberFormatException nfe) {
-        }
-    }
-
-
-    protected void setGel(FLYSArtifact artifact, WSPLGENJob job) {
-        String gel = artifact.getDataAsString("scenario");
-
-        logger.debug("Selected gel = '" + gel + "'");
-
-        if (gel == null || gel.length() == 0) {
-            job.setGel(WSPLGENJob.GEL_NOSPERRE);
-        }
-        else if (gel.equals("scenario.current")) {
-            job.setGel(WSPLGENJob.GEL_SPERRE);
-        }
-        else if (gel.equals("scenario.scenario")) {
-            job.setGel(WSPLGENJob.GEL_SPERRE);
-        }
-        else {
-            job.setGel(WSPLGENJob.GEL_NOSPERRE);
-        }
-    }
-
-
-    protected void setDist(FLYSArtifact artifact, WSPLGENJob job) {
-        String dist = artifact.getDataAsString("profile_distance");
-
-        try {
-            job.setDist(Double.parseDouble(dist));
-        }
-        catch (NumberFormatException nfe) {
-            // nothing to do here
-        }
-    }
-
-
-    protected void setLine(
-        FLYSArtifact artifact,
-        FacetCreator facetCreator,
-        File         dir,
-        WSPLGENJob   job
-    ) {
-        String river   = artifact.getDataAsString("river");
-        String geoJSON = artifact.getDataAsString("uesk.barriers");
-        String srid    = FLYSUtils.getRiverDGMSrid(river);
-        String srs     = "EPSG:" + srid;
-
-        if (geoJSON == null || geoJSON.length() == 0) {
-            logger.debug("No barrier features in parameterization existing.");
-            return;
-        }
-
-        SimpleFeatureType ft = getBarriersFeatureType(
-            "barriers", srs, Geometry.class);
-
-        List<SimpleFeature> features = GeometryUtils.parseGeoJSON(geoJSON, ft);
-        if (features == null || features.isEmpty()) {
-            logger.debug("No barrier features extracted.");
-            return;
-        }
-
-        FeatureCollection[] fcs = splitLinesAndPolygons(features);
-
-        File shapeLines = new File(dir, WSPLGEN_BARRIERS_LINES);
-        File shapePolys = new File(dir, WSPLGEN_BARRIERS_POLY);
-
-        Object[][] obj = new Object[][] {
-            new Object[] { "typ", String.class }
-        };
-
-        String scenario = job.getGel();
-
-        boolean l = GeometryUtils.writeShapefile(
-            shapeLines,
-            GeometryUtils.buildFeatureType("lines", srs, LineString.class, obj),
-            fcs[0]);
-
-        if (l) {
-            logger.debug(
-                "Successfully created barrier line shapefile. " +
-                "Write shapefile path into WSPLGEN job.");
-            createMapfile(
-                artifact,
-                dir,
-                MapfileGenerator.MS_LAYER_PREFIX + "barriers-lines",
-                WSPLGEN_BARRIERS_LINES,
-                "LINE",
-                srid,
-                "barriers");
-
-            if (scenario.equals(WSPLGENJob.GEL_NOSPERRE)) {
-                logger.debug("WSPLGEN will not use barrier features.");
-            }
-            else {
-                job.addLin(shapeLines.getAbsolutePath());
-            }
-        }
-
-        boolean p = GeometryUtils.writeShapefile(
-            shapePolys,
-            GeometryUtils.buildFeatureType("polygons", srs, Polygon.class, obj),
-            fcs[1]);
-
-
-        if (p) {
-            logger.debug(
-                "Successfully created barrier polygon shapefile. " +
-                "Write shapefile path into WSPLGEN job.");
-            createMapfile(
-                artifact,
-                dir,
-                MapfileGenerator.MS_LAYER_PREFIX + "barriers-poly",
-                shapePolys.getAbsolutePath(),
-                "POLYGON",
-                srid,
-                "barriers");
-
-            if (scenario.equals(WSPLGENJob.GEL_NOSPERRE)) {
-                logger.debug("WSPLGEN will not use barrier features.");
-            }
-            else {
-                job.addLin(shapePolys.getAbsolutePath());
-            }
-        }
-
-        if (p || l) {
-            facetCreator.createBarrierFacet();
-        }
-    }
-
-
-    protected void setUserShape(
-        FLYSArtifact artifact,
-        FacetCreator facetCreator,
-        File         dir,
-        WSPLGENJob   job
-    ) {
-        File archive = new File(dir, WSPLGEN_USER_SHAPE);
-        boolean exists = archive.exists();
-        logger.debug("shp file exists: " + exists);
-        if (exists) {
-            job.addLin(dir + "/" + WSPLGEN_USER_SHAPE);
-            facetCreator.createShapeFacet(FacetCreator.I18N_USERSHAPE,
-                MapfileGenerator.MS_LAYER_PREFIX + "user-rgd",
-                FLOODMAP_USERSHAPE,
-                4);
-        }
-    }
-
-    protected SimpleFeatureType getBarriersFeatureType(
-        String name,
-        String srs,
-        Class  type
-    ) {
-        Object[][] attrs = new Object[3][];
-        attrs[0] = new Object[] { "typ", String.class };
-        attrs[1] = new Object[] { "elevation", Double.class };
-        attrs[2] = new Object[] { "mark.selected", Integer.class };
-
-        return GeometryUtils.buildFeatureType(name, srs, type, attrs);
-    }
-
-
-    protected FeatureCollection[] splitLinesAndPolygons(List<SimpleFeature> f) {
-        FeatureCollection lines    = FeatureCollections.newCollection();
-        FeatureCollection polygons = FeatureCollections.newCollection();
-
-        for (SimpleFeature feature: f) {
-            Geometry geom = (Geometry) feature.getDefaultGeometry();
-
-
-            if (geom instanceof LineString) {
-                geom = applyElevationAttribute(feature, geom);
-                lines.add(feature);
-            }
-            else if (geom instanceof Polygon) {
-                geom = applyElevationAttribute(feature, geom);
-                polygons.add(feature);
-            }
-            else {
-                logger.warn("Feature not supported: " + geom.getClass());
-            }
-        }
-
-        logger.debug("Found " + lines.size() + " barrier lines.");
-        logger.debug("Found " + polygons.size() + " barrier polygons.");
-
-        return new FeatureCollection[] { lines, polygons };
-    }
-
-
-    protected static Geometry applyElevationAttribute(
-        SimpleFeature feature,
-        Geometry      geom
-    ) {
-        logger.debug("Apply elevations for: " + geom.getClass());
-
-        List<Double> elevations = extractElevations(feature);
-        int           numPoints = geom.getNumPoints();
-        int        numElevation = elevations.size();
-
-        String typ = (String) feature.getAttribute("typ");
-
-        if (numPoints > numElevation) {
-            logger.warn("More vertices in Geometry than elevations given.");
-        }
-
-        Coordinate[] c = geom.getCoordinates();
-        for (int i = 0; i < numPoints; i++) {
-            if (i < numElevation) {
-                c[i].z = elevations.get(i);
-            }
-            else if (typ != null && typ.equals("Graben")) {
-                c[i].z = -9999d;
-            }
-            else {
-                c[i].z = 9999d;
-            }
-        }
-
-        return geom;
-    }
-
-
-    protected static List<Double> extractElevations(SimpleFeature feature) {
-        String tmp = (String) feature.getAttribute("elevation");
-        String typ = (String) feature.getAttribute("typ");
-
-        String[] elevations = tmp == null ? null : tmp.split(" ");
-
-        int num = elevations != null ? elevations.length : 0;
-
-        List<Double> list = new ArrayList<Double>(num);
-
-        for (int i = 0; i < num; i++) {
-            try {
-                list.add(Double.parseDouble(elevations[i]));
-            }
-            catch (NumberFormatException nfe) {
-                logger.warn("Error while parsing elevation at pos: " + i);
-                if (typ != null && typ.equals("Graben")) {
-                    list.add(new Double(-9999.0));
-                }
-                else {
-                    list.add(new Double(9999.0));
-                }
-            }
-        }
-
-        return list;
-    }
-
-
-    protected void setAxis(FLYSArtifact artifact, File dir, WSPLGENJob job) {
-        String river = artifact.getDataAsString("river");
-        String srid    = FLYSUtils.getRiverDGMSrid(river);
-        String srs     = "EPSG:" + srid;
-
-        List<RiverAxis> axes = null;
-        try {
-            axes = RiverAxis.getRiverAxis(river);
-        }
-        catch (HibernateException iae) {
-            logger.warn("No valid river axis found for " + river);
-            return;
-        }
-        if (axes == null || axes.isEmpty()) {
-            logger.warn("Could not find river axis for: '" + river + "'");
-            return;
-        }
-
-        SimpleFeatureType ft = GeometryUtils.buildFeatureType(
-            "axis", srs, LineString.class);
-
-        SimpleFeatureBuilder builder = new SimpleFeatureBuilder(ft);
-        FeatureCollection collection = FeatureCollections.newCollection();
-
-        for (int i = 0, n = axes.size(); i < n; i++) {
-            RiverAxis axis = axes.get(i);
-
-            builder.add(axis.getGeom());
-            collection.add(builder.buildFeature(String.valueOf(i)));
-
-            builder.reset();
-        }
-
-        File axisShape = new File(dir, WSPLGEN_AXIS);
-
-        boolean a = GeometryUtils.writeShapefile(
-            axisShape,
-            GeometryUtils.buildFeatureType("axis", srs, LineString.class),
-            collection);
-
-        if (a) {
-            job.setAxis(axisShape.getAbsolutePath());
-        }
-    }
-
-
-    protected void setPro(FLYSArtifact artifact, File dir, WSPLGENJob job) {
-        String river = artifact.getDataAsString("river");
-        String srid    = FLYSUtils.getRiverDGMSrid(river);
-        String srs     = "EPSG:" + srid;
-
-        List<CrossSectionTrack> cst =
-            CrossSectionTrack.getCrossSectionTrack(river, WSPLGEN_QPS_NAME);
-
-        logger.debug("Found " + cst.size() + " CrossSectionTracks.");
-
-        Object[][] attrs = new Object[2][];
-        attrs[0] = new Object[] { "ELEVATION", Double.class };
-        attrs[1] = new Object[] { "KILOMETER", Double.class };
-
-        SimpleFeatureType ft = GeometryUtils.buildFeatureType(
-            "qps", srs, LineString.class, attrs);
-
-        SimpleFeatureBuilder builder = new SimpleFeatureBuilder(ft);
-        FeatureCollection collection = FeatureCollections.newCollection();
-
-        int i = 0;
-        for (CrossSectionTrack track: cst) {
-            builder.reset();
-            builder.add(track.getGeom());
-            builder.add(track.getZ().doubleValue());
-            builder.add(track.getKm().doubleValue());
-
-            collection.add(builder.buildFeature(String.valueOf(i++)));
-        }
-
-        File qpsShape = new File(dir, WSPLGEN_QPS);
-
-        boolean q = GeometryUtils.writeShapefile(
-            qpsShape,
-            GeometryUtils.buildFeatureType("qps", srs, LineString.class, attrs),
-            collection);
-
-        if (q) {
-            job.setPro(qpsShape.getAbsolutePath());
-        }
-    }
-
-
-    protected void setDgm(
-        FLYSArtifact artifact,
-        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);
-
-        if (dgm == null) {
-            logger.warn("Could not find specified DGM.");
-
-            return;
-        }
-
-        File dgmPath = new File (dgm.getPath());
-        if (dgmPath.isAbsolute()) {
-            job.setDgm(dgm.getPath());
-        }
-        else {
-            FLYSContext fc = (FLYSContext)context.globalContext();
-            String prefix = (String) fc.get("dgm-path");
-            job.setDgm(prefix.trim() + dgm.getPath().trim());
-        }
-    }
-
-
-    protected void setArea(FLYSArtifact artifact, File dir, WSPLGENJob job) {
-        String useFloodplain = artifact.getDataAsString("use_floodplain");
-        if (!Boolean.valueOf(useFloodplain)) {
-            logger.debug("WSPLGEN will not use floodplain.");
-            return;
-        }
-
-        String river = artifact.getDataAsString("river");
-        String srid  = FLYSUtils.getRiverDGMSrid(river);
-        String srs   = "EPSG:" + srid;
-
-        Floodplain plain = Floodplain.getFloodplain(river);
-
-        SimpleFeatureType ft = GeometryUtils.buildFeatureType(
-            "talaue", srs, Polygon.class);
-
-        SimpleFeatureBuilder builder = new SimpleFeatureBuilder(ft);
-        builder.add(plain.getGeom());
-
-        FeatureCollection collection = FeatureCollections.newCollection();
-        collection.add(builder.buildFeature("0"));
-
-        File talaueShape = new File(dir, WSPLGEN_FLOODPLAIN);
-
-        boolean t = GeometryUtils.writeShapefile(
-            talaueShape,
-            GeometryUtils.buildFeatureType("talaue", srs, Polygon.class),
-            collection);
-
-        if (t) {
-            job.setArea(talaueShape.getAbsolutePath());
-        }
-    }
-
-
-    protected void setOutFile(FLYSArtifact artifact, WSPLGENJob job) {
-        job.setOutFile(WSPLGEN_OUTPUT_FILE);
-    }
-
-
-    protected WQKms getWQKms(FLYSArtifact flys, CallContext cc) {
-        String   wspString = flys.getDataAsString(WSP_ARTIFACT);
-        if (wspString == null) {
-            logger.debug("getWQKms(): wspString == null");
-            return null;
-        }
-        String[] parts = wspString.split(";");
-        String otherArtifact = parts[0];
-
-        int idx = -1;
-        try {
-            idx = Integer.parseInt(parts[2]);
-        }
-        catch (NumberFormatException nfe) { /* do nothing */ }
-
-        FLYSArtifact src = otherArtifact != null
-            ? FLYSUtils.getArtifact(otherArtifact, cc)
-            : flys;
-
-        logger.debug("Use waterlevel provided by Artifact: " + src.identifier());
-
-        CalculationResult rawData = (CalculationResult) src.compute(
-            cc,
-            null,
-            WINFO_WSP_STATE_ID,
-            ComputeType.ADVANCE,
-            false);
-
-        WQKms[] wqkms = (WQKms[]) rawData.getData();
-
-        return wqkms == null || idx == -1 || idx >= wqkms.length
-            ? null
-            : wqkms[idx];
-    }
-
-
-    protected void setWsp(
-        FLYSArtifact artifact,
-        CallContext  context,
-        File         dir,
-        WSPLGENJob   job)
-    {
-        logger.debug("FloodMapState.setWsp");
-
-        WQKms data = getWQKms(artifact, context);
-
-        if (data == null) {
-            logger.warn("No WST data found!");
-            return;
-        }
-
-        WstWriter writer = new WstWriter(1);
-
-        // TODO REMOVE job.setWspTag(...) This is only used until the user is
-        // able to select the WSP column himself!
-        boolean writeWspTag = true;
-
-        double[] buf = new double[4];
-        logger.debug("Add WST column: " + data.getName());
-        writer.addColumn(data.getName());
-
-        if (writeWspTag) {
-            job.setWspTag(data.getName());
-            writeWspTag = false;
-        }
-
-        for (int i = 0, num = data.size(); i < num; i++) {
-            data.get(i, buf);
-            writer.add(buf);
-        }
-
-        FileOutputStream fout = null;
-
-        try {
-            File wspFile = new File(dir, WSPLGEN_WSP_FILE);
-            fout         = new FileOutputStream(wspFile);
-
-            writer.write(fout);
-
-            job.setWsp(wspFile.getAbsolutePath());
-        }
-        catch (FileNotFoundException fnfe) {
-            logger.warn("Error while writing wsp file: " + fnfe.getMessage());
-        }
-        finally {
-            if (fout != null) {
-                try {
-                    fout.close();
-                }
-                catch (IOException ioe) { /* do nothing */ }
-            }
-        }
-    }
-
-
-
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/FloodplainChoice.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,87 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import org.w3c.dom.Element;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.artifacts.common.utils.XMLUtils;
-
-import de.intevation.artifactdatabase.ProtocolUtils;
-
-import de.intevation.flys.artifacts.resources.Resources;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class FloodplainChoice extends DefaultState {
-
-    public static final String OPTION   = "floodplain.option";
-    public static final String ACTIVE   = "floodplain.active";
-    public static final String INACTIVE = "floodplain.inactive";
-
-    private static final Logger logger =
-        Logger.getLogger(FloodplainChoice.class);
-
-
-    @Override
-    protected String getUIProvider() {
-        return "boolean_panel";
-    }
-
-
-    @Override
-    protected Element[] createItems(
-        XMLUtils.ElementCreator cr,
-        Artifact    artifact,
-        String      name,
-        CallContext context)
-    {
-        CallMeta meta = context.getMeta();
-
-        Element option = createItem(
-            cr,
-            new String[] { Resources.getMsg(meta, OPTION, OPTION), "true" });
-
-        return new Element[] { option };
-    }
-
-
-    @Override
-    protected String getLabelFor(
-        CallContext cc,
-        String      name,
-        String      value,
-        String      type
-    ) {
-        logger.debug("GET LABEL FOR '" + name + "' / '" + value + "'");
-        if (value != null && value.equals("true")) {
-            return Resources.getMsg(cc.getMeta(), ACTIVE, ACTIVE);
-        }
-        else {
-            return Resources.getMsg(cc.getMeta(), INACTIVE, INACTIVE);
-        }
-    }
-
-
-    protected Element createItem(XMLUtils.ElementCreator cr, Object obj) {
-        Element item  = ProtocolUtils.createArtNode(cr, "item", null, null);
-        Element label = ProtocolUtils.createArtNode(cr, "label", null, null);
-        Element value = ProtocolUtils.createArtNode(cr, "value", null, null);
-
-        String[] arr = (String[]) obj;
-
-        label.setTextContent(arr[0]);
-        value.setTextContent(arr[1]);
-
-        item.appendChild(label);
-        item.appendChild(value);
-
-        return item;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/FlowVelocityState.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,297 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.FacetActivity;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.access.FlowVelocityAccess;
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.DataFacet;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.FlowVelocityCalculation;
-import de.intevation.flys.artifacts.model.FlowVelocityData;
-import de.intevation.flys.artifacts.model.FlowVelocityFacet;
-import de.intevation.flys.artifacts.model.FlowVelocityFilterFacet;
-import de.intevation.flys.artifacts.resources.Resources;
-
-
-/* State in which flow velocities can/will be calculated. */
-public class FlowVelocityState extends DefaultState implements FacetTypes {
-
-    private static Logger logger = Logger.getLogger(FlowVelocityState.class);
-
-    public static final String I18N_MAINCHANNEL_FACET =
-        "facet.flow_velocity.mainchannel";
-
-    public static final String I18N_TOTALCHANNEL_FACET =
-        "facet.flow_velocity.totalchannel";
-
-    public static final String I18N_TAU_FACET =
-        "facet.flow_velocity.tauchannel";
-
-    public static final String I18N_MAINCHANNEL_FACET_RAW =
-        "facet.flow_velocity.mainchannel.raw";
-
-    public static final String I18N_TOTALCHANNEL_FACET_RAW =
-        "facet.flow_velocity.totalchannel.raw";
-
-    public static final String I18N_TAU_FACET_RAW =
-        "facet.flow_velocity.tauchannel.raw";
-
-    public static final String I18N_DISCHARGE_FACET =
-        "facet.flow_velocity.discharge";
-
-
-    @Override
-    public Object computeAdvance(
-        FLYSArtifact artifact,
-        String       hash,
-        CallContext  context,
-        List<Facet>  facets,
-        Object       old
-    ) {
-        logger.debug("FlowVelocityState.computeAdvance");
-
-        List<Facet> newFacets = new ArrayList<Facet>();
-
-        FlowVelocityAccess access = new FlowVelocityAccess(artifact, context);
-
-        CalculationResult res = old instanceof CalculationResult
-            ? (CalculationResult) old
-            : new FlowVelocityCalculation().calculate(access);
-
-        if (facets == null || res == null) {
-            return res;
-        }
-
-        FlowVelocityData[] data = (FlowVelocityData[]) res.getData();
-
-        logger.debug("Calculated " + data.length + " FlowVelocityData objects");
-
-        String id  = getID();
-        int    idx = 0;
-
-        for (FlowVelocityData d: data) {
-            if (d.getType().equals("main")) {
-                newFacets.add(new FlowVelocityFacet(
-                    idx,
-                    FLOW_VELOCITY_MAINCHANNEL,
-                    buildFacetName(artifact, context, d, I18N_MAINCHANNEL_FACET_RAW),
-                    ComputeType.ADVANCE,
-                    id,
-                    hash
-                ));
-
-                newFacets.add(new FlowVelocityFacet(
-                    idx,
-                    FLOW_VELOCITY_TAU,
-                    buildFacetName(artifact, context, d, I18N_TAU_FACET_RAW),
-                    ComputeType.ADVANCE,
-                    id,
-                    hash
-                ));
-                newFacets.add(new FlowVelocityFilterFacet(
-                    idx,
-                    FLOW_VELOCITY_MAINCHANNEL_FILTERED,
-                    buildMainChannelName(artifact, context, d),
-                    ComputeType.ADVANCE,
-                    id,
-                    hash
-                ));
-                newFacets.add(new FlowVelocityFilterFacet(
-                    idx,
-                    FLOW_VELOCITY_TAU_FILTERED,
-                    buildTauName(artifact, context, d),
-                    ComputeType.ADVANCE,
-                    id,
-                    hash
-                ));
-            }
-            else if (d.getType().equals("total")) {
-                newFacets.add(new FlowVelocityFacet(
-                    idx,
-                    FLOW_VELOCITY_TOTALCHANNEL,
-                    buildFacetName(artifact, context, d, I18N_TOTALCHANNEL_FACET_RAW),
-                    ComputeType.ADVANCE,
-                    id,
-                    hash
-                ));
-                newFacets.add(new FlowVelocityFilterFacet(
-                    idx,
-                    FLOW_VELOCITY_TOTALCHANNEL_FILTERED,
-                    buildTotalChannelName(artifact, context, d),
-                    ComputeType.ADVANCE,
-                    id,
-                    hash
-                ));
-
-            }
-            else if(d.getType().equals("main_total")) {
-                 newFacets.add(new FlowVelocityFacet(
-                    idx,
-                    FLOW_VELOCITY_MAINCHANNEL,
-                    buildFacetName(artifact, context, d, I18N_MAINCHANNEL_FACET_RAW),
-                    ComputeType.ADVANCE,
-                    id,
-                    hash
-                ));
-                newFacets.add(new FlowVelocityFacet(
-                    idx,
-                    FLOW_VELOCITY_TAU,
-                    buildFacetName(artifact, context, d, I18N_TAU_FACET_RAW),
-                    ComputeType.ADVANCE,
-                    id,
-                    hash
-                ));
-                newFacets.add(new FlowVelocityFacet(
-                    idx,
-                    FLOW_VELOCITY_TOTALCHANNEL,
-                    buildFacetName(artifact, context, d, I18N_TOTALCHANNEL_FACET_RAW),
-                    ComputeType.ADVANCE,
-                    id,
-                    hash
-                ));
-                newFacets.add(new FlowVelocityFilterFacet(
-                    idx,
-                    FLOW_VELOCITY_MAINCHANNEL_FILTERED,
-                    buildMainChannelName(artifact, context, d),
-                    ComputeType.ADVANCE,
-                    id,
-                    hash
-                ));
-                newFacets.add(new FlowVelocityFilterFacet(
-                    idx,
-                    FLOW_VELOCITY_TAU_FILTERED,
-                    buildTauName(artifact, context, d),
-                    ComputeType.ADVANCE,
-                    id,
-                    hash
-                ));
-                newFacets.add(new FlowVelocityFilterFacet(
-                    idx,
-                    FLOW_VELOCITY_TOTALCHANNEL_FILTERED,
-                    buildTotalChannelName(artifact, context, d),
-                    ComputeType.ADVANCE,
-                    id,
-                    hash
-                ));
-            }
-
-            newFacets.add(new FlowVelocityFacet(
-                idx,
-                FLOW_VELOCITY_DISCHARGE,
-                buildDischargeName(artifact, context, d),
-                ComputeType.ADVANCE,
-                id,
-                hash
-            ));
-
-            idx++;
-        }
-
-        Facet csv = new DataFacet(
-            CSV, "CSV data", ComputeType.ADVANCE, hash, id);
-
-        // TODO ADD PDF FACET
-
-        newFacets.add(csv);
-
-        logger.debug("Created " + newFacets.size() + " new Facets.");
-
-        facets.addAll(newFacets);
-
-        return res;
-    }
-
-
-    protected String buildFacetName(
-        FLYSArtifact     flys,
-        CallContext      cc,
-        FlowVelocityData data,
-        String           resourceId
-    ) {
-        Object[] args = new Object[] {
-            data.getZone()
-        };
-
-        return Resources.getMsg(
-            cc.getMeta(),
-            resourceId,
-            resourceId,
-            args);
-    }
-
-
-    protected String buildMainChannelName(
-        FLYSArtifact     flys,
-        CallContext      cc,
-        FlowVelocityData data
-    ) {
-        return buildFacetName(flys, cc, data, I18N_MAINCHANNEL_FACET);
-    }
-
-
-    protected String buildTotalChannelName(
-        FLYSArtifact     flys,
-        CallContext      cc,
-        FlowVelocityData data
-    ) {
-        return buildFacetName(flys, cc, data, I18N_TOTALCHANNEL_FACET);
-    }
-
-
-    protected String buildDischargeName(
-        FLYSArtifact     flys,
-        CallContext      cc,
-        FlowVelocityData data
-    ) {
-        return buildFacetName(flys, cc, data, I18N_DISCHARGE_FACET);
-    }
-
-    protected String buildTauName(
-        FLYSArtifact     flys,
-        CallContext      cc,
-        FlowVelocityData data
-    ) {
-        return buildFacetName(flys, cc, data, I18N_TAU_FACET);
-    }
-
-    static {
-        // Active/deactivate facets.
-        FacetActivity.Registry.getInstance().register(
-            "minfo",
-            new FacetActivity() {
-                @Override
-                public Boolean isInitialActive(
-                    Artifact artifact,
-                    Facet    facet,
-                    String   output
-                ) {
-                    String name = facet.getName();
-                    if (name.equals(FLOW_VELOCITY_MAINCHANNEL_FILTERED) ||
-                        name.equals(FLOW_VELOCITY_TAU_FILTERED) ||
-                        name.equals(FLOW_VELOCITY_DISCHARGE) ||
-                        name.equals(FLOW_VELOCITY_TOTALCHANNEL_FILTERED)) {
-                        return Boolean.TRUE;
-                    }
-                    else if (name.equals(FLOW_VELOCITY_MAINCHANNEL) ||
-                        name.equals(FLOW_VELOCITY_TAU) ||
-                        name.equals(FLOW_VELOCITY_TOTALCHANNEL)) {
-                        return Boolean.FALSE;
-                    }
-                    else {
-                        return null;
-                    }
-                }
-            });
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/GaugeDischargeState.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,161 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.artifactdatabase.state.Facet;
-
-import de.intevation.flys.artifacts.ChartArtifact;
-import de.intevation.flys.artifacts.GaugeDischargeArtifact;
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.flys.artifacts.model.GaugeDischargeFacet;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.WQKms;
-import de.intevation.flys.artifacts.model.ReportFacet;
-import de.intevation.flys.artifacts.model.EmptyFacet;
-import de.intevation.flys.artifacts.model.CalculationResult;
-
-import de.intevation.flys.artifacts.resources.Resources;
-
-import de.intevation.flys.model.Gauge;
-
-
-/**
- * The only state for an GaugeDischargeState (River and km known).
- */
-public class GaugeDischargeState
-extends      DefaultState
-implements   FacetTypes
-{
-    /** Developer-centric description of facet. */
-    public static final String I18N_DESCRIPTION = "facet.discharge.curve";
-
-    /** The logger that is used in this state. */
-    private static final Logger logger =
-        Logger.getLogger(GaugeDischargeState.class);
-
-
-    /**
-     * Create i18ned name for gaugedischargeFacet.
-     * @param artifact The artifact which has information about the gauge.
-     * @param meta used for i18n.
-     * @return localized name for gaugedischargefacet.
-     */
-    protected String createFacetName(GaugeDischargeArtifact artifact,
-        CallMeta meta) {
-
-        Gauge gauge = artifact.getGauge();
-        Object[] args = new Object[] {
-            gauge.getName(),
-            gauge.getStation()
-        };
-
-        String name = Resources.getMsg(
-            meta,
-            "chart.computed.discharge.curve.gauge",
-            "",
-            args);
-
-        return name;
-    }
-
-
-    /**
-     * Add an GaugeDischargeFacet to list of Facets.
-     *
-     * @param artifact Ignored.
-     * @param hash Ignored.
-     * @param context Ignored.
-     * @param meta CallMeta to be used for internationalization.
-     * @param facets List to add AnnotationFacet to.
-     *
-     * @return null.
-     */
-    @Override
-    public Object computeInit(
-        FLYSArtifact artifact,
-        String       hash,
-        Object       context,
-        CallMeta     meta,
-        List<Facet>  facets
-    ) {
-        logger.debug("GaugeDischargeState.computeInit()");
-
-        GaugeDischargeFacet facet = new GaugeDischargeFacet(
-            0,
-            DISCHARGE_CURVE,
-            createFacetName((GaugeDischargeArtifact) artifact, meta));
-
-        facets.add(facet);
-
-        return null;
-    }
-
-
-    /**
-     * 'Calculate' Discharge at Gauge.
-     */
-    @Override
-    public Object computeAdvance(
-        FLYSArtifact artifact,
-        String       hash,
-        CallContext  context,
-        List<Facet>  facets,
-        Object       old
-    ) {
-        if (artifact instanceof GaugeDischargeArtifact) {
-            logger.debug("GaugeDischargeState.computeAdvance()");
-            GaugeDischargeArtifact dischargeArtifact = (GaugeDischargeArtifact) artifact;
-
-            CalculationResult res;
-
-
-            if (old instanceof CalculationResult) {
-                res = (CalculationResult) old;
-            }
-            else {
-                res = dischargeArtifact.getDischargeCurveData();
-            }
-
-            WQKms[] wqkms = (WQKms[]) res.getData();
-
-            if (wqkms != null && facets != null) {
-                logger.debug("GaugeDischargeState.computeAdvance(): create facets");
-
-                GaugeDischargeFacet facet = new GaugeDischargeFacet(
-                    0,
-                    DISCHARGE_CURVE,
-                    createFacetName(dischargeArtifact, context.getMeta()));
-
-                facets.add(facet);
-
-                //facets.add(new DataFacet(CSV, "CSV data"));
-                //facets.add(new DataFacet(PDF, "PDF data"));
-
-                if (res.getReport().hasProblems()) {
-                    facets.add(new ReportFacet());
-                }
-            }
-            else {
-                if (wqkms == null)
-                    logger.debug("GaugeDischargeState.computeAdvance(): wqkms 0");
-                else
-                    logger.debug("GaugeDischargeState.computeAdvance(): facets 0");
-            }
-
-            return res;
-        }
-        else if (artifact instanceof ChartArtifact) {
-            ChartArtifact chart = (ChartArtifact)artifact;
-            facets.add(new EmptyFacet());
-            return null;
-        }
-        return null;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/GaugeTimerangeState.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,94 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import java.util.Calendar;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-import org.hibernate.SQLQuery;
-import org.hibernate.Session;
-import org.hibernate.type.StandardBasicTypes;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.backend.SessionHolder;
-import de.intevation.flys.model.Gauge;
-import de.intevation.flys.utils.FLYSUtils;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class GaugeTimerangeState extends IntRangeState {
-
-    /** Private logger. */
-    private static final Logger logger =
-            Logger.getLogger(GaugeTimerangeState.class);
-
-
-    /** Get 'min' and 'max'times of gauge time intervals. */
-    protected long[] getLowerUpper(FLYSArtifact flys) {
-        Gauge gauge = FLYSUtils.getReferenceGauge(flys);
-
-        if (gauge == null) {
-            logger.warn("No reference gauge specified!");
-            return new long[] { 0, 0 };
-        }
-
-        Session session = SessionHolder.HOLDER.get();
-
-        SQLQuery query = session.createSQLQuery(
-                "SELECT min(start_time) as min, max(stop_time) as max " +
-                        "FROM time_intervals WHERE id in " +
-                        "(SELECT time_interval_id FROM discharge_tables " +
-                "WHERE gauge_id =:gid)");
-
-        query.addScalar("min", StandardBasicTypes.CALENDAR);
-        query.addScalar("max", StandardBasicTypes.CALENDAR);
-
-        query.setInteger("gid", gauge.getId());
-
-        List<?> results = query.list();
-
-        if (results != null) {
-            Object[] res = (Object[]) results.get(0);
-
-            Calendar lo = (Calendar) res[0];
-            Calendar up = (Calendar) res[1];
-
-            if (lo != null && up != null) {
-                return new long[] { lo.getTimeInMillis(), up.getTimeInMillis() };
-            }
-        }
-
-        logger.warn("Could not determine time range for gauge: " + gauge);
-
-        return null;
-    }
-
-
-    @Override
-    protected Object getLower(FLYSArtifact flys) {
-        long[] lowerUpper = getLowerUpper(flys);
-
-        return lowerUpper != null ? lowerUpper[0] : 0;
-    }
-
-
-    @Override
-    protected Object getUpper(FLYSArtifact flys) {
-        long[] lowerUpper = getLowerUpper(flys);
-
-        return lowerUpper != null ? lowerUpper[1] : 0;
-    }
-
-
-    @Override
-    protected String getUIProvider() {
-        return "gaugetimerange";
-    }
-
-    @Override
-    protected String getType() {
-        return "longrange";
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/HWSBarriersState.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,359 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import org.apache.velocity.Template;
-
-import org.geotools.data.shapefile.ShapefileDataStore;
-
-import org.geotools.feature.FeatureCollection;
-import org.geotools.feature.FeatureCollections;
-
-import org.opengis.feature.simple.SimpleFeatureType;
-
-import org.opengis.feature.type.GeometryDescriptor;
-
-import org.w3c.dom.Element;
-
-import de.intevation.artifactdatabase.state.Facet;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.artifacts.common.utils.FileTools;
-
-import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.flys.artifacts.access.MapAccess;
-
-import de.intevation.flys.artifacts.model.LayerInfo;
-
-import de.intevation.flys.artifacts.model.map.HWS;
-import de.intevation.flys.artifacts.model.map.HWSContainer;
-import de.intevation.flys.artifacts.model.map.HWSFactory;
-
-import de.intevation.flys.utils.ArtifactMapfileGenerator;
-import de.intevation.flys.utils.FLYSUtils;
-import de.intevation.flys.utils.GeometryUtils;
-import de.intevation.flys.utils.MapfileGenerator;
-
-public class HWSBarriersState
-extends DefaultState
-{
-
-    /** The logger that is used in this class.*/
-    private static Logger logger = Logger.getLogger(HWSBarriersState.class);
-    private static final String HWS_SHAPEFILE_LINES = "hws-lines.shp";
-    private static final String HWS_SHAPEFILE_POINTS = "hws-points.shp";
-    private static final String USER_RGD_SHAPE     = "user-rgd.shp";
-    private static final String USER_RGD_ZIP       = "user-rgd.zip";
-    private static final String USER_RGD_FILENAME  = "user-rgd";
-    @Override
-    protected String getUIProvider() {
-        return "map_digitize";
-    }
-
-
-    @Override
-    protected Element createStaticData(
-        FLYSArtifact   flys,
-        ElementCreator creator,
-        CallContext    cc,
-        String         name,
-        String         value,
-        String         type
-    ) {
-        Element dataElement = creator.create("data");
-        creator.addAttr(dataElement, "name", name, true);
-        creator.addAttr(dataElement, "type", type, true);
-
-        Element itemElement = creator.create("item");
-        creator.addAttr(itemElement, "value", value, true);
-
-        creator.addAttr(itemElement, "label", "", true);
-        dataElement.appendChild(itemElement);
-
-        return dataElement;
-    }
-
-
-    @Override
-    public Object computeAdvance(
-        FLYSArtifact artifact,
-        String hash,
-        CallContext context,
-        List<Facet> facets,
-        Object old
-    ) {
-        File artifactDir = getDirectory(artifact);
-
-        if (artifactDir == null) {
-            logger.error("Could not create directory for HWS shapefile!");
-            return null;
-        }
-
-        MapAccess access = new MapAccess(artifact, context);
-        String river = access.getRiver();
-        HWSContainer hwsLines = HWSFactory.getHWSLines(river);
-        HWSContainer hwsPoints = HWSFactory.getHWSPoints(river);
-        List<String> selected = access.getHWS();
-
-        List<HWS> selectedLines = hwsLines.getHws(selected);
-        List<HWS> selectedPoints = hwsPoints.getHws(selected);
-
-        FeatureCollection collectionLines = FeatureCollections.newCollection();
-        SimpleFeatureType lineType = null;
-        for (HWS h : selectedLines) {
-            lineType = h.getFeatureType();
-            collectionLines.add(h.getFeature());
-        }
-        boolean successLines = false;
-        if (lineType != null && collectionLines.size() > 0) {
-            File shapeLines = new File(artifactDir, HWS_SHAPEFILE_LINES);
-            successLines = GeometryUtils.writeShapefile(
-                shapeLines, lineType, collectionLines);
-        }
-
-        FeatureCollection collectionPoints = FeatureCollections.newCollection();
-        SimpleFeatureType pointType = null;
-        for (HWS h : selectedPoints) {
-            pointType = h.getFeatureType();
-            collectionPoints.add(h.getFeature());
-        }
-        boolean successPoints = false;
-        if (pointType != null && collectionPoints.size() > 0) {
-            File shapePoints = new File(artifactDir, HWS_SHAPEFILE_POINTS);
-            successPoints =GeometryUtils.writeShapefile(
-                shapePoints, pointType, collectionPoints);
-        }
-
-        if (successLines) {
-            createMapfile(
-                artifact,
-                artifactDir,
-                MapfileGenerator.MS_LAYER_PREFIX + "hws-lines",
-                HWS_SHAPEFILE_LINES,
-                "LINE",
-                "31467",
-                "hws");
-        }
-        if (successPoints) {
-            createMapfile(
-                artifact,
-                artifactDir,
-                MapfileGenerator.MS_LAYER_PREFIX + "hws-points",
-                HWS_SHAPEFILE_POINTS,
-                "POINT",
-                "31467",
-                "hws");
-        }
-
-        String userRgd = artifact.getDataAsString("uesk.user-rgd");
-        if (!userRgd.equals("none")) {
-            if (extractUserShp(artifactDir)) {
-                try {
-                    ShapefileDataStore store = new ShapefileDataStore(
-                    new File(artifactDir.getCanonicalPath() +
-                        "/" + USER_RGD_SHAPE)
-                            .toURI().toURL());
-                    GeometryDescriptor desc =
-                        store.getSchema().getGeometryDescriptor();
-                    String type = desc.getType().getName().toString();
-                    String proj =
-                        desc.getCoordinateReferenceSystem().
-                            getCoordinateSystem().toString();
-                    int pos1 = proj.indexOf("EPSG\",\"");
-                    int pos2 = proj.indexOf("\"]]");
-                    String epsg = "";
-                    if (pos1 >= 0 && pos2 >= 0) {
-                        epsg =
-                            proj.substring(proj.indexOf("EPSG\",\"") + 7,
-                                proj.indexOf("\"]]"));
-                    }
-                    else {
-                        logger.warn("Could not read EPSG code from shapefile.");
-                        return null;
-                    }
-                    if (type.contains("Line")) {
-                        type = "LINE";
-                    }
-                    else if (type.contains("Poly")) {
-                        type = "POLYGON";
-                    }
-                    else {
-                        type = "POINT";
-                    }
-                    createMapfile(
-                        artifact,
-                        artifactDir,
-                        MapfileGenerator.MS_LAYER_PREFIX + USER_RGD_FILENAME,
-                        USER_RGD_SHAPE,
-                        type,
-                        epsg,
-                        "user-rgd");
-                }
-                catch (IOException e) {
-                    logger.warn("No mapfile for user-rgd created!");
-                }
-            }
-        }
-        return null;
-    }
-
-    private boolean extractUserShp(File dir) {
-        // TODO Auto-generated method stub
-        File archive = new File(dir, USER_RGD_ZIP);
-        boolean exists = archive.exists();
-        logger.debug("Zip file exists: " + exists);
-        if (exists) {
-            try {
-                File tmpDir = new File(dir, "usr_tmp");
-                FileTools.extractArchive(archive, tmpDir);
-                moveFiles(tmpDir, dir);
-                return true;
-            }
-            catch (IOException ioe) {
-                logger.warn("Zip archive " + dir + "/"
-                    + USER_RGD_ZIP + " could not be extracted.");
-                return false;
-            }
-        }
-        return false;
-    }
-
-    private void moveFiles(File source, final File target)
-    throws IOException
-    {
-        if (!source.exists()) {
-            return;
-        }
-        if (!target.exists()) {
-            target.mkdir();
-        }
-        FileTools.walkTree(source, new FileTools.FileVisitor() {
-            @Override
-            public boolean visit(File file) {
-                if (!file.isDirectory()) {
-                    String name = file.getName();
-                    String suffix = "";
-                    int pos = name.lastIndexOf('.');
-                    if (pos > 0 && pos < name.length() - 1) {
-                        suffix = name.substring(pos + 1);
-                    }
-                    else {
-                        return true;
-                    }
-                    try {
-                        FileTools.copyFile(file, new File(target, USER_RGD_FILENAME + "." + suffix));
-                    }
-                    catch (IOException ioe) {
-                        logger.warn ("Error while copying file " + file.getName());
-                        return true;
-                    }
-                }
-                return true;
-            }
-        });
-
-        FileTools.deleteRecursive(source);
-    }
-
-    private void createMapfile(
-        FLYSArtifact artifact,
-        File artifactDir,
-        String name,
-        String hwsShapefile,
-        String type,
-        String srid,
-        String group
-    ) {
-        LayerInfo info = new LayerInfo();
-        info.setName(name + artifact.identifier());
-        info.setType(type);
-        info.setDirectory(artifact.identifier());
-        info.setTitle(name);
-        info.setData(hwsShapefile);
-        info.setSrid(srid);
-        info.setGroupTitle(group);
-        MapfileGenerator generator = new ArtifactMapfileGenerator();
-        Template tpl = generator.getTemplateByName(MapfileGenerator.SHP_LAYER_TEMPLATE);
-        try {
-            File layer = new File(artifactDir.getCanonicalPath() + "/" + name);
-            generator.writeLayer(info, layer, tpl);
-            List<String> layers = new ArrayList<String>();
-            layers.add(layer.getAbsolutePath());
-            generator.generate();
-        }
-        catch(FileNotFoundException fnfe) {
-            logger.warn("Could not find mapfile for hws layer");
-        }
-        catch (Exception ioe) {
-            logger.warn("Could not create mapfile for hws layer");
-            logger.warn(Arrays.toString(ioe.getStackTrace()));
-        }
-    }
-
-
-    @Override
-    public void endOfLife(Artifact artifact, Object callContext) {
-        super.endOfLife(artifact, callContext);
-        logger.info("ScenarioSelect.endOfLife: " + artifact.identifier());
-
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-        removeDirectory(flys);
-    }
-
-
-    /**
-     * Removes the directory and all its content where the required data and the
-     * results of WSPLGEN are stored. Should be called in endOfLife().
-     */
-    // FIXME: I've seen this code somewhere else...
-    protected void removeDirectory(FLYSArtifact artifact) {
-        String shapePath = FLYSUtils.getXPathString(
-            FLYSUtils.XPATH_FLOODMAP_SHAPEFILE_DIR);
-
-        File artifactDir = new File(shapePath, artifact.identifier());
-
-        if (artifactDir.exists()) {
-            logger.debug("Delete directory: " + artifactDir.getAbsolutePath());
-            boolean success = FileTools.deleteRecursive(artifactDir);
-            if (!success) {
-                logger.warn("could not remove dir '" + artifactDir + "'");
-            }
-        }
-        else {
-            logger.debug("There is no directory to remove.");
-        }
-    }
-
-    /**
-     * Returns (and creates if not existing) the directory for storing WSPLEN
-     * data for the owner artifact.
-     *
-     * @param artifact The owner Artifact.
-     *
-     * @return the directory for WSPLEN data.
-     */
-    protected File getDirectory(FLYSArtifact artifact) {
-        String shapePath = FLYSUtils.getXPathString(
-            FLYSUtils.XPATH_FLOODMAP_SHAPEFILE_DIR);
-
-        File artifactDir = FileTools.getDirectory(
-            shapePath, artifact.identifier());
-
-        return artifactDir;
-    }
-
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/HWSDatacageState.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,90 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import org.apache.log4j.Logger;
-import org.w3c.dom.Element;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-
-public class HWSDatacageState
-extends DefaultState
-{
-
-    private static final Logger logger = Logger.getLogger(HWSDatacageState.class);
-
-    @Override
-    protected String getUIProvider() {
-        return "hws_datacage_panel";
-    }
-
-
-    @Override
-    protected Element createStaticData(
-        FLYSArtifact   flys,
-        ElementCreator creator,
-        CallContext    cc,
-        String         name,
-        String         value,
-        String         type
-    ) {
-        Element dataElement = creator.create("data");
-        creator.addAttr(dataElement, "name", name, true);
-        creator.addAttr(dataElement, "type", type, true);
-
-        Element itemElement = creator.create("item");
-        creator.addAttr(itemElement, "value", value, true);
-
-        creator.addAttr(itemElement, "label", getLabel(cc, value), true);
-        dataElement.appendChild(itemElement);
-
-        return dataElement;
-    }
-
-
-    public static String getLabel(CallContext cc, String value) {
-        logger.debug("Create label for value: " + value);
-
-        return value;
-    }
-
-
-    @Override
-    public boolean validate(Artifact artifact)
-    throws IllegalArgumentException
-    {
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-        String hws = flys.getDataAsString("uesk.hws");
-        logger.debug("hws: " + hws);
-        return true;
-    }
-
-
-    /**
-     * Returns the DGM specified in the parameters of <i>flys</i>.
-     *
-     * @param flys The FLYSArtifact that knows the ID of a DGM.
-     *
-     * @throws IllegalArgumentException If the FLYSArtifact doesn't know the ID
-     * of a DGM.
-     *
-     * @return the DGM specified by FLYSArtifact's parameters.
-     */
-    public static String getHWS(FLYSArtifact flys)
-    throws IllegalArgumentException
-    {
-        String hws= flys.getDataAsString("uesk.hws");
-        if (hws == null) {
-            return null;
-        }
-
-        logger.debug("Found selected hws: '" + hws + "'");
-
-        return hws;
-    }
-
-
-
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/HistoricalDischargeComputeState.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,207 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import java.text.NumberFormat;
-
-import java.util.List;
-
-import org.apache.log4j.Logger;
-import org.w3c.dom.Element;
-
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.WINFOArtifact;
-import de.intevation.flys.artifacts.access.HistoricalDischargeAccess;
-import de.intevation.flys.artifacts.access.HistoricalDischargeAccess.EvaluationMode;
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.DataFacet;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.HistoricalDischargeCurveFacet;
-import de.intevation.flys.artifacts.model.HistoricalDischargeData;
-import de.intevation.flys.artifacts.model.HistoricalDischargeDifferenceFacet;
-import de.intevation.flys.artifacts.model.HistoricalDischargeFacet;
-import de.intevation.flys.artifacts.model.HistoricalDischargeWQFacet;
-import de.intevation.flys.artifacts.model.HistoricalWQKms;
-import de.intevation.flys.artifacts.model.HistoricalWQTimerange;
-import de.intevation.flys.artifacts.model.ReportFacet;
-import de.intevation.flys.artifacts.model.WQKms;
-import de.intevation.flys.artifacts.model.WQTimerange;
-import de.intevation.flys.artifacts.resources.Resources;
-import de.intevation.flys.model.TimeInterval;
-
-
-/**
- * State to calculate historical discharge curves.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class HistoricalDischargeComputeState
-extends    DefaultState
-implements FacetTypes {
-
-    private static final Logger logger = Logger
-        .getLogger(HistoricalDischargeComputeState.class);
-
-    public static final String DEFAULT_UNIT = "cm";
-    public static final String I18N_WQ_CURVE_FACET_RANGE = "historical_discharge.wq.curve_range";
-    public static final String I18N_WQ_CURVE_FACET_SINCE = "historical_discharge.wq.curve_since";
-
-    @Override
-    protected void appendItems(Artifact artifact, ElementCreator creator,
-        String name, CallContext context, Element select) {
-        // TODO IMPLEMENT ME
-    }
-
-    @Override
-    public Object computeAdvance(FLYSArtifact artifact, String hash,
-        CallContext context, List<Facet> facets, Object old) {
-        logger.debug("HistoricalDischargeComputeState.computeAdvance");
-
-        WINFOArtifact winfo = (WINFOArtifact) artifact;
-
-        CalculationResult res = old instanceof CalculationResult ? (CalculationResult) old
-            : winfo.getHistoricalDischargeData();
-
-        if (facets == null) {
-            return res;
-        }
-
-        if (res.getReport().hasProblems()) {
-            facets.add(new ReportFacet(ComputeType.ADVANCE, hash, id));
-        }
-
-        HistoricalDischargeData data = (HistoricalDischargeData) res.getData();
-        HistoricalDischargeAccess access = new HistoricalDischargeAccess(
-            artifact);
-
-        WQTimerange[] wqts = (WQTimerange[]) data.getWQTimeranges();
-        if (wqts != null && wqts.length > 0) {
-            facets.add(new DataFacet(CSV, "CSV data", ComputeType.ADVANCE,
-                hash, id));
-
-            facets.add(new DataFacet(PDF, "PDF data", ComputeType.ADVANCE,
-                hash, id));
-
-            prepareFacets(facets, wqts, access);
-        }
-
-        WQKms[] wqs = (WQKms[]) data.getWQs();
-        if (wqs != null && wqs.length > 0) {
-            prepareWQFacets(context, facets, wqs, access, hash);
-        }
-
-        return res;
-    }
-
-    protected void prepareFacets(List<Facet> facets, WQTimerange[] wqts,
-        HistoricalDischargeAccess access) {
-        int i = 0;
-
-        for (WQTimerange wqt : wqts) {
-            logger.debug("Prepare facet for: " + wqt.getName());
-
-            EvaluationMode evalMode = access.getEvaluationMode();
-            if (evalMode == EvaluationMode.W) {
-                facets.add(new HistoricalDischargeFacet(i,
-                    HISTORICAL_DISCHARGE_Q, createFacetTitle(wqt)));
-
-                if (wqt instanceof HistoricalWQTimerange) {
-                    logger
-                        .debug("Create another facet for historical differences.");
-
-                    // TODO CREATE BETTER TITLE FOR FACETS (issue1180)
-                    facets.add(new HistoricalDischargeDifferenceFacet(i,
-                        HISTORICAL_DISCHARGE_Q_DIFF, "DIFF: " + wqt.getName()));
-                }
-            }
-            else {
-                facets.add(new HistoricalDischargeFacet(i,
-                    HISTORICAL_DISCHARGE_W, createFacetTitle(wqt)));
-
-                if (wqt instanceof HistoricalWQTimerange) {
-                    logger
-                        .debug("Create another facet for historical differences.");
-
-                    // TODO CREATE BETTER TITLE FOR FACETS
-                    facets.add(new HistoricalDischargeDifferenceFacet(i,
-                        HISTORICAL_DISCHARGE_W_DIFF, "DIFF: " + wqt.getName()));
-                }
-            }
-
-            i++;
-        }
-    }
-
-    protected void prepareWQFacets(CallContext cc, List<Facet> facets,
-        WQKms[] wqs, HistoricalDischargeAccess access, String hash) {
-        double[] ws = access.getWs();
-        double[] qs = access.getQs();
-
-        NumberFormat format = NumberFormat.getInstance(
-                        Resources.getLocale(cc.getMeta()));
-        for (int k = 0; k < ws.length; k++) {
-            facets.add(new HistoricalDischargeWQFacet(k,
-                HISTORICAL_DISCHARGE_WQ_W, "W=" + format.format(ws[k]), ComputeType.ADVANCE,
-                hash, getID(), ws[k]));
-        }
-
-        for (int k = 0; k < qs.length; k++) {
-            facets.add(new HistoricalDischargeWQFacet(k,
-                HISTORICAL_DISCHARGE_WQ_Q, "Q=" + format.format(qs[k]), ComputeType.ADVANCE,
-                hash, getID(), qs[k]));
-        }
-
-        int idx = 0;
-        for (WQKms wqkms : wqs) {
-            String title = null;
-            if (wqkms instanceof HistoricalWQKms) {
-                title = createDischargeCurveTitle(cc, (HistoricalWQKms) wqkms);
-            }
-            else {
-                title = wqkms.getName();
-            }
-
-            facets.add(new HistoricalDischargeCurveFacet(idx,
-                HISTORICAL_DISCHARGE_WQ_CURVE, title, ComputeType.ADVANCE,
-                getID(), hash));
-            idx++;
-        }
-    }
-
-    /** Create title for a Discharge-curve, including date. */
-    protected String createDischargeCurveTitle(CallContext cc,
-        HistoricalWQKms wqkms) {
-        TimeInterval timeInterval = wqkms.getTimeInterval();
-
-        if (timeInterval.getStopTime() != null) {
-            return Resources.format(cc.getMeta(), I18N_WQ_CURVE_FACET_RANGE,
-                I18N_WQ_CURVE_FACET_RANGE, timeInterval.getStartTime(),
-                timeInterval.getStopTime());
-        }
-        else {
-            return Resources.format(cc.getMeta(), I18N_WQ_CURVE_FACET_SINCE,
-                I18N_WQ_CURVE_FACET_SINCE, timeInterval.getStartTime());
-        }
-    }
-
-
-    /** Create string for facets name/description. */
-    protected String createFacetTitle(WQTimerange wqt) {
-        String name = wqt.getName();
-
-        return name != null && name.indexOf("W") >= 0 ? createFacetTitleW(wqt)
-            : createFacetTitleQ(wqt);
-    }
-
-    protected String createFacetTitleW(WQTimerange wqt) {
-        String name = wqt.getName();
-        return name + " " + DEFAULT_UNIT;
-    }
-
-    protected String createFacetTitleQ(WQTimerange wqt) {
-        return wqt.getName();
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/HistoricalDischargeState.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,153 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Element;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.ArtifactNamespaceContext;
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
-
-import de.intevation.flys.artifacts.resources.Resources;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class HistoricalDischargeState extends DefaultState {
-
-    private static final Logger logger =
-        Logger.getLogger(HistoricalDischargeState.class);
-
-    public static final String I18N_MODE_W = "historical.mode.w";
-    public static final String I18N_MODE_Q = "historical.mode.q";
-
-    public static final String DATA_MODE   = "historical_mode";
-    public static final String DATA_VALUES = "historical_values";
-    public static final int    DATA_MODE_W = 0;
-    public static final int    DATA_MODE_Q = 1;
-
-
-    @Override
-    protected String getUIProvider() {
-        return "wq_simple_array";
-    }
-
-    @Override
-    protected void appendItems(
-        Artifact       artifact,
-        ElementCreator creator,
-        String         name,
-        CallContext    context,
-        Element        select
-    ) {
-        if (name != null && name.equals(DATA_VALUES)) {
-            select.setAttributeNS(
-                ArtifactNamespaceContext.NAMESPACE_URI,
-                "art:type",
-                "doublearray");
-        }
-        else if (name != null && name.equals(DATA_MODE)) {
-            select.setAttributeNS(
-                ArtifactNamespaceContext.NAMESPACE_URI,
-                "art:type",
-                "intoptions");
-        }
-
-        super.appendItems(artifact, creator, name, context, select);
-    }
-
-
-    @Override
-    protected Element[] createItems(
-        ElementCreator creator,
-        Artifact       artifact,
-        String         name,
-        CallContext    context
-    ) {
-        logger.debug("createItems()");
-
-        if (name != null && name.equals(DATA_MODE)) {
-            return createModeItem(creator, artifact, name, context);
-        }
-        else if (name != null && name.equals(DATA_VALUES)) {
-            return createValuesItem(creator, artifact, name, context);
-        }
-
-        logger.warn("Tried to create item for invalid data: " + name);
-
-        return new Element[0];
-    }
-
-
-    /** Get label for display in client, depending on chosen W or Q input. */
-    @Override
-    protected String getLabelFor(
-        CallContext cc,
-        String      name,
-        String      value,
-        String      type
-    ) {
-        CallMeta meta = cc.getMeta();
-
-        if (name.equals(DATA_MODE)) {
-            if (value.equals(String.valueOf(DATA_MODE_W))) {
-                return Resources.getMsg(meta, I18N_MODE_W, I18N_MODE_W);
-            }
-            else {
-                return Resources.getMsg(meta, I18N_MODE_Q, I18N_MODE_Q);
-            }
-        }
-        else {
-            return value;
-        }
-    }
-
-
-    protected Element[] createModeItem(
-        ElementCreator creator,
-        Artifact       artifact,
-        String         name,
-        CallContext    context
-    ) {
-        logger.debug("createModeItem()");
-
-        CallMeta meta = context.getMeta();
-
-        Element modeW = createItem(
-            creator,
-            new String[] {
-                Resources.getMsg(meta, I18N_MODE_W, I18N_MODE_W),
-                String.valueOf(DATA_MODE_W) } );
-
-        Element modeQ = createItem(
-            creator,
-            new String[] {
-                Resources.getMsg(meta, I18N_MODE_Q, I18N_MODE_Q),
-                String.valueOf(DATA_MODE_Q) } );
-
-        return new Element[] { modeW, modeQ };
-    }
-
-
-    protected Element[] createValuesItem(
-        ElementCreator creator,
-        Artifact       artifact,
-        String         name,
-        CallContext    context
-    ) {
-        logger.debug("createValuesItem()");
-
-        Element valuesW = createItem(
-            creator, new String[] { "ws", "" } );
-
-        Element valuesQ = createItem(
-            creator, new String[] { "qs", "" } );
-
-        return new Element[] { valuesW, valuesQ };
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/InputDoubleState.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-
-/**
- * State to keep a double value and validate it against a range.
- */
-public class InputDoubleState extends MinMaxState {
-
-    private static final Logger logger = Logger.getLogger(InputDoubleState.class);
-
-
-    @Override
-    protected String getUIProvider() {
-        return "location_panel";
-    }
-
-
-    @Override
-    protected Object getLower(FLYSArtifact flys) {
-        return 0;
-    }
-
-
-    @Override
-    protected Object getUpper(FLYSArtifact flys) {
-        return 0;
-    }
-
-
-    @Override
-    protected String getType() {
-        return "double";
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/IntRangeState.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class IntRangeState extends MinMaxState {
-
-    private static final Logger logger = Logger.getLogger(IntRangeState.class);
-
-
-    @Override
-    protected String getUIProvider() {
-        return "timerange";
-    }
-
-
-    @Override
-    protected Object getLower(FLYSArtifact flys) {
-        return 0;
-    }
-
-
-    @Override
-    protected Object getUpper(FLYSArtifact flys) {
-        return 0;
-    }
-
-
-    @Override
-    protected String getType() {
-        return "intrange";
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/LocationDistanceSelect.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,152 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import org.apache.log4j.Logger;
-
-import gnu.trove.TDoubleArrayList;
-
-import de.intevation.artifacts.Artifact;
-
-import de.intevation.artifactdatabase.data.StateData;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.WINFOArtifact;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class LocationDistanceSelect
-extends      ComputationRangeState
-{
-
-    /** The logger used in this class. */
-    private static Logger logger = Logger.getLogger(LocationDistanceSelect.class);
-
-    /** The name of the 'mode' field. */
-    public static final String MODE = "ld_mode";
-
-    /** The name of the 'locations' field. */
-    public static final String LOCATIONS = "ld_locations";
-
-
-    /**
-     * The default constructor that initializes an empty State object.
-     */
-    public LocationDistanceSelect() {
-    }
-
-
-    @Override
-    protected String getUIProvider() {
-        return "location_distance_panel";
-    }
-
-
-    /** Validates the range (or location). */
-    @Override
-    public boolean validate(Artifact artifact)
-    throws IllegalArgumentException
-    {
-        logger.debug("LocationDistanceSelect.validate");
-
-        FLYSArtifact flys = (FLYSArtifact)artifact;
-        StateData mode = getData(flys, MODE);
-        String mValue = mode != null ? (String)mode.getValue() : null;
-        if (mValue != null) {
-            if (mValue.equals("distance")) {
-                return super.validate(flys);
-            }
-            else {
-                return validateLocations(flys);
-            }
-        }
-        return false;
-    }
-
-
-    /** Validate selected locations. */
-    protected boolean validateLocations(FLYSArtifact flys)
-    throws    IllegalArgumentException
-    {
-        StateData dValues = getData(flys, LOCATIONS);
-        String    values  = dValues != null ? (String)dValues.getValue() : null;
-
-        if (values == null || values.length() == 0) {
-            throw new IllegalArgumentException("error_empty_state");
-        }
-
-        double[] absMinMax = getMinMax(flys);
-        double[] relMinMax = getMinMaxFromString(values);
-
-        if (relMinMax[0] < absMinMax[0] || relMinMax[0] > absMinMax[1]) {
-            throw new IllegalArgumentException("error_feed_from_out_of_range");
-        }
-
-        if (relMinMax[1] > absMinMax[1] || relMinMax[1] < absMinMax[0]) {
-            throw new IllegalArgumentException("error_feed_to_out_of_range");
-        }
-
-        return true;
-    }
-
-
-    /**
-     * Extracts the min/max values from String <i>s</i>. An
-     * IllegalArgumentException is thrown if there is a value that throws a
-     * NumberFormatException.
-     *
-     * @param s String that contains whitespace separated double values.
-     *
-     * @return a 2dmin array [min,max].
-     */
-    public static double[] getMinMaxFromString(String s)
-    throws IllegalArgumentException
-    {
-        String[] values = s.split(" ");
-
-        double[] minmax = new double[] {
-            Double.MAX_VALUE,
-            -Double.MAX_VALUE };
-
-        for (String v: values) {
-            try {
-                double value = Double.valueOf(v);
-
-                minmax[0] = minmax[0] < value ? minmax[0] : value;
-                minmax[1] = minmax[1] > value ? minmax[1] : value;
-            }
-            catch (NumberFormatException nfe) {
-                throw new IllegalArgumentException(
-                    "error_invalid_double_value");
-            }
-        }
-
-        return minmax;
-    }
-
-
-    public static double[] getLocations(WINFOArtifact flys) {
-        StateData data  = flys.getData(LOCATIONS);
-        String    value = data != null ? (String) data.getValue() : null;
-
-        if (value == null || value.length() == 0) {
-            logger.warn("No location data given.");
-            return null;
-        }
-
-        String[]         splitted = value.split(" ");
-        TDoubleArrayList values   = new TDoubleArrayList();
-
-        for (String split: splitted) {
-            try {
-                values.add(Double.valueOf(split));
-            }
-            catch (NumberFormatException nfe) {
-                logger.warn(nfe, nfe);
-            }
-        }
-
-        return values.toNativeArray();
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/LocationSelect.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,135 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import gnu.trove.TDoubleArrayList;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Element;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.artifacts.common.utils.XMLUtils;
-
-import de.intevation.artifactdatabase.data.StateData;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-
-/**
- * This state is used to realize the input of multiple locations as string.
- *
- * The string should be a whitespace separated list of double values where each
- * double value represents a location.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class LocationSelect extends LocationDistanceSelect {
-
-    /** The logger used in this class.*/
-    private static Logger logger = Logger.getLogger(LocationSelect.class);
-
-
-    public LocationSelect() {
-    }
-
-
-    /** UI Provider (which input method should the client provide to user. */
-    @Override
-    protected String getUIProvider() {
-        return "location_panel";
-    }
-
-
-    @Override
-    protected Element[] createItems(
-        XMLUtils.ElementCreator cr,
-        Artifact    artifact,
-        String      name,
-        CallContext context)
-    {
-        double[] minmax = getMinMax(artifact);
-
-        double minVal = Double.MIN_VALUE;
-        double maxVal = Double.MAX_VALUE;
-
-        if (minmax != null) {
-            minVal = minmax[0];
-            maxVal = minmax[1];
-        }
-        else {
-            logger.warn("Could not read min/max distance values!");
-        }
-
-        if (name.equals(LOCATIONS)) {
-            Element min = createItem(
-                cr,
-                new String[] {"min", new Double(minVal).toString()});
-
-            Element max = createItem(
-                cr,
-                new String[] {"max", new Double(maxVal).toString()});
-
-            return new Element[] { min, max };
-        }
-
-        return null;
-    }
-
-
-    /** Validates data from artifact. */
-    @Override
-    public boolean validate(Artifact artifact)
-    throws IllegalArgumentException
-    {
-        logger.debug("LocationSelect.validate");
-
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-        StateData    data = getData(flys, LOCATIONS);
-
-        String locationStr = data != null
-            ? (String) data.getValue()
-            : null;
-
-        if (locationStr == null || locationStr.length() == 0) {
-            logger.error("No locations given.");
-            throw new IllegalArgumentException("error_empty_state");
-        }
-
-        double[] minmax = getMinMax(artifact);
-        double[] mm     = extractLocations(locationStr);
-
-        logger.debug("Inserted min location: " + mm[0]);
-        logger.debug("Inserted max location: " + mm[mm.length-1]);
-
-        return validateBounds(minmax[0], minmax[1], mm[0], mm[mm.length-1], 0d);
-    }
-
-
-    /**
-     * This method takes a string that consist of whitespace separated double
-     * values and returns the double values as array.
-     *
-     * @param locationStr The locations inserted in this state.
-     *
-     * @return the locations as array.
-     */
-    protected double[] extractLocations(String locationStr) {
-        String[] tmp               = locationStr.split(" ");
-        TDoubleArrayList locations = new TDoubleArrayList();
-
-        for (String l: tmp) {
-            try {
-                locations.add(Double.parseDouble(l));
-            }
-            catch (NumberFormatException nfe) {
-                logger.warn(nfe, nfe);
-            }
-        }
-
-        locations.sort();
-
-        return locations.toNativeArray();
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/ManualPointsSingleState.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,141 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import java.util.List;
-
-import org.apache.log4j.Logger;
-import org.json.JSONArray;
-import org.json.JSONException;
-
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.CallMeta;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.ManualPointsArtifact;
-import de.intevation.flys.artifacts.model.CrossSectionWaterLineFacet;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.ManualPointsFacet;
-import de.intevation.flys.artifacts.resources.Resources;
-
-/**
- * The only state for an ManualPointArtifact.
- */
-public class ManualPointsSingleState
-extends      DefaultState
-implements   FacetTypes
-{
-    /** Developer-centric description of facet. */
-    public static final String I18N_DESCRIPTION
-        = "facet.longitudinal_section.manualpoint";
-
-    /** Part of data key. */
-    protected static final String DOT_DATA
-        = ".data";
-
-    /** Part of data key. */
-    protected static final String DOT_LINES
-        = ".lines";
-
-    /** The logger that is used in this state. */
-    private static final Logger logger =
-        Logger.getLogger(ManualPointsSingleState.class);
-
-
-    /**
-     * Add ManualPointsFacets to list of Facets.
-     *
-     * @param artifact Ignored.
-     * @param hash Ignored.
-     * @param meta CallMeta to be used for internationalization.
-     * @param facets List to add ManualPointsFacet to.
-     *
-     * @return null.
-     */
-    public Object compute(
-        FLYSArtifact artifact,
-        String       hash,
-        CallMeta     meta,
-        List<Facet>  facets
-    ) {
-        logger.debug("ManualPointsSingleState.compute()");
-        ManualPointsArtifact points = (ManualPointsArtifact) artifact;
-
-        // Add Facet per Diagram type if data given.
-        for (ChartType ct: ChartType.values()) {
-            // Handle points.
-            String pointData = points.getDataAsString(ct + "." + MANUALPOINTS +
-                DOT_DATA);
-            if (pointData != null && pointData.length() != 0
-                && !pointData.equals("[]")) {
-                String fName = ct + "." + MANUALPOINTS;
-                ManualPointsFacet facet = new ManualPointsFacet(
-                    0,
-                    fName,
-                    Resources.getMsg(meta, MANUALPOINTS, "Manual Points"));
-                facets.add(facet);
-                logger.debug("compute(): ManualPointsFacet for " + ct + " created");
-            }
-            else {
-                logger.debug("compute(): No points for " + ct);
-            }
-
-            // Handle lines.
-            String linesData = points.getDataAsString(ct + "." + MANUALPOINTS +
-                DOT_LINES);
-            if (linesData != null && linesData.length() != 0
-                && !linesData.equals("[]")) {
-                try {
-                    JSONArray lines = new JSONArray(linesData);
-                    for (int i = 0, P = lines.length(); i < P; i++) {
-                        JSONArray array = lines.getJSONArray(i);
-                        double y    = array.getDouble(0);
-                        String name = array.getString(1);
-                        String fName = ct + "." + MANUALLINE;
-                        logger.debug("have facet: " + y + " / " + name + " -> " + fName);
-                        CrossSectionWaterLineFacet facet = new CrossSectionWaterLineFacet(
-                            i,
-                            fName,
-                            name);
-
-                        facets.add(facet);
-                    }
-                }
-                catch(JSONException e){
-                    logger.error("Could not decode json.");
-                }
-
-            }
-            else {
-                //logger.debug("No points for " + ct);
-            }
-        }
-
-        return null;
-    }
-
-
-    /** Call compute. */
-    @Override
-    public Object computeInit(
-        FLYSArtifact artifact,
-        String       hash,
-        Object       context,
-        CallMeta     meta,
-        List<Facet>  facets
-     ) {
-        return compute(artifact, hash, meta, facets);
-    }
-
-
-    /** Call compute. */
-    @Override
-    public Object computeFeed(
-        FLYSArtifact artifact,
-        String hash,
-        CallContext context,
-        List<Facet> facets,
-        Object old
-    ) {
-        return compute(artifact, hash, context.getMeta(), facets);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/MiddleBedHeight.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,97 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.artifactdatabase.state.Facet;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.access.BedHeightAccess;
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.DataFacet;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.MiddleBedHeightData;
-import de.intevation.flys.artifacts.model.MiddleBedHeightFacet;
-import de.intevation.flys.artifacts.model.MiddleBedHeightCalculation;
-
-
-/** State in which Middle Bed Heights are generated. */
-public class MiddleBedHeight extends DefaultState implements FacetTypes {
-
-    /** Private logger. */
-    private static final Logger logger = Logger.getLogger(MiddleBedHeight.class);
-
-
-    @Override
-    public Object computeAdvance(
-        FLYSArtifact artifact,
-        String       hash,
-        CallContext  context,
-        List<Facet>  facets,
-        Object       old
-    ) {
-        logger.debug("MiddleBedHeight.computeAdvance");
-
-        List<Facet> newFacets = new ArrayList<Facet>();
-
-        BedHeightAccess access = new BedHeightAccess(artifact, context);
-
-        CalculationResult res = old instanceof CalculationResult
-            ? (CalculationResult) old
-            : new MiddleBedHeightCalculation().calculate(access);
-
-        if (facets == null || res == null) {
-            return res;
-        }
-
-        MiddleBedHeightData[] data = (MiddleBedHeightData[]) res.getData();
-
-        logger.debug("Calculated " + data.length + " MiddleBedHeightData objects");
-
-        String id  = getID();
-        int    idx = 0;
-
-        for (MiddleBedHeightData d: data) {
-            if (d.getStartYear() == d.getEndYear()) {
-                newFacets.add(new MiddleBedHeightFacet(
-                    idx,
-                    MIDDLE_BED_HEIGHT_SINGLE,
-                    d.getSoundingName(context),
-                    ComputeType.ADVANCE,
-                    id,
-                    hash
-                ));
-            }
-            else {
-                newFacets.add(new MiddleBedHeightFacet(
-                    idx,
-                    MIDDLE_BED_HEIGHT_EPOCH,
-                    d.getSoundingName(context),
-                    ComputeType.ADVANCE,
-                    id,
-                    hash
-                ));
-            }
-
-            idx++;
-        }
-
-        Facet csv = new DataFacet(
-            CSV, "CSV data", ComputeType.ADVANCE, hash, id);
-
-        // TODO ADD PDF FACET
-
-        newFacets.add(csv);
-
-        logger.debug("Created " + newFacets.size() + " new Facets.");
-
-        facets.addAll(newFacets);
-
-        return res;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/MinMaxState.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,173 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import org.w3c.dom.Element;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.artifacts.common.ArtifactNamespaceContext;
-import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
-
-import de.intevation.artifactdatabase.ProtocolUtils;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-
-/**
- * State that holds minimun and maximum (for validation).
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public abstract class MinMaxState extends DefaultState {
-
-    private static final Logger logger = Logger.getLogger(MinMaxState.class);
-
-    @Override
-    protected void appendItems(
-        Artifact       artifact,
-        ElementCreator creator,
-        String         name,
-        CallContext    context,
-        Element        select
-    ) {
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-
-        select.setAttributeNS(
-            ArtifactNamespaceContext.NAMESPACE_URI,
-            "art:type",
-            getType());
-
-        String[] defMinMax = getDefaults(artifact, name);
-
-        Element min = ProtocolUtils.createArtNode(
-            creator,
-            "min",
-            new String[] { "value", "default" },
-            new String[] { String.valueOf(getLower(flys)), defMinMax[0] });
-
-        Element max = ProtocolUtils.createArtNode(
-            creator,
-            "max",
-            new String[] { "value", "default" },
-            new String[] { String.valueOf(getUpper(flys)), defMinMax[1] });
-
-        select.appendChild(min);
-        select.appendChild(max);
-    }
-
-
-    /**
-     * @param cc
-     * @param name
-     * @param value
-     * @param type
-     *
-     * @return
-     */
-    @Override
-    protected String getLabelFor(
-        CallContext cc,
-        String      name,
-        String      value,
-        String      type
-    ) {
-        if (type.indexOf("range") > 0) {
-            String[] minmax = extractRangeAsString(value);
-
-            if (minmax != null) {
-                return minmax[0] + " - " + minmax[1];
-            }
-        }
-
-        return super.getLabelFor(cc, name, value, type);
-    }
-
-
-    /**
-     * Returns a string array with [min,max] from <i>rawValue</i>.
-     * <i>rawValue</i> should be a string like "1999;2001".
-     *
-     * @param rawValue A string with min and max separated by a ';'.
-     *
-     * @return the min and max as string array ([min,max]).
-     */
-    protected String[] extractRangeAsString(String rawValue) {
-        return rawValue.split(";");
-    }
-
-
-    /**
-     * This method returns the default values for min and max. If the static
-     * field DefaultState.USE_DEFAULTS is set, the minimum and maximum inserted
-     * by the user is returned as string. Otherwise, the absolute minimum and
-     * maximum are returned.
-     *
-     * @param artifact The FLYSArtifact.
-     * @param name The name of the parameter.
-     *
-     * @return a string array [min,max] that contains the minimum and maximum
-     * values for the parameter <i>name</i>.
-     */
-    protected String[] getDefaults(Artifact artifact, String name) {
-        if (DefaultState.USE_DEFAULTS) {
-            String[] tmp = getMinMaxByParameter(artifact, name);
-
-            return tmp != null ? tmp : getMinMaxDefaults(artifact, name);
-        }
-        else {
-            return getMinMaxDefaults(artifact, name);
-        }
-    }
-
-
-    /**
-     * Returns a string array with minimum and maximum inserted by the user as
-     * [min,max].
-     *
-     * @param artifact The FLYSArtifact that stores the parameter.
-     * @param name The name of the parameter for raw min/max value string.
-     *
-     * @return a string array [min,max].
-     */
-    protected String[] getMinMaxByParameter(Artifact artifact, String name) {
-        FLYSArtifact flys     = (FLYSArtifact) artifact;
-        String       rawValue = flys.getDataAsString(name);
-
-        if (rawValue == null) {
-            logger.debug("No value for '" + rawValue + "' existing.");
-            return null;
-        }
-
-        logger.debug("Raw value for '" + name + "' = " + rawValue);
-
-        return extractRangeAsString(rawValue);
-    }
-
-
-    /**
-     * Returns a string array with absolute minimum and maximum as [min,max].
-     *
-     * @param artifact The FLYSArtifact (not used in this implementation).
-     * @param name The parameter name (not used in this implementation).
-     *
-     * @return a string array [min,max].
-     */
-    protected String[] getMinMaxDefaults(Artifact artifact, String name) {
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-
-        Object lower = getLower(flys);
-        Object upper = getUpper(flys);
-
-        return new String[] { String.valueOf(lower), String.valueOf(upper) };
-    }
-
-
-    protected abstract Object getLower(FLYSArtifact flys);
-
-    protected abstract Object getUpper(FLYSArtifact flys);
-
-    protected abstract String getType();
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/MultiIntArrayState.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,101 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import org.w3c.dom.Element;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
-import de.intevation.artifacts.common.model.KVP;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.resources.Resources;
-import de.intevation.flys.utils.FLYSUtils;
-
-
-/**
- * State that holds minimun and maximum (for validation).
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public abstract class MultiIntArrayState extends DefaultState {
-
-    private static final Logger logger =
-        Logger.getLogger(MultiIntArrayState.class);
-
-
-    @Override
-    protected void appendItems(
-        Artifact       artifact,
-        ElementCreator creator,
-        String         name,
-        CallContext    context,
-        Element        select
-    ) {
-        try {
-            creator.addAttr(select, "type", "intoptions", true);
-
-            for (KVP kvp: getOptions(artifact, name)) {
-                Element item = creator.create("item");
-                creator.addAttr(item, "label", kvp.getValue().toString(), true);
-                creator.addAttr(item, "value", kvp.getKey().toString(), true);
-
-                select.appendChild(item);
-            }
-        }
-        catch (IllegalArgumentException iae) {
-            logger.warn("Illegal argument", iae);
-        }
-    }
-
-
-    @Override
-    protected Element createStaticData(
-        FLYSArtifact   flys,
-        ElementCreator creator,
-        CallContext    cc,
-        String         name,
-        String         value,
-        String         type
-    ) {
-        Element data = creator.create("data");
-        creator.addAttr(data, "name",  name, true);
-        creator.addAttr(data, "type",  type, true);
-        creator.addAttr(data, "label",
-            Resources.getMsg(cc.getMeta(), name, name), true);
-
-        int[] values = FLYSUtils.intArrayFromString(value);
-
-        for (int val: values) {
-            try {
-                Element item = creator.create("item");
-                creator.addAttr(item, "value", String.valueOf(val), true);
-                creator.addAttr(item, "label", getLabelFor(cc, name, val), true);
-
-                data.appendChild(item);
-            }
-            catch (IllegalArgumentException iae) {
-                logger.warn("Cannot append item: " + val, iae);
-            }
-        }
-
-        return data;
-    }
-
-
-    protected abstract KVP<Integer, String>[] getOptions(
-        Artifact artifact,
-        String   parameterName
-    )
-    throws IllegalArgumentException;
-
-
-    protected abstract String getLabelFor(
-        CallContext cc,
-        String      parameterName,
-        int         value)
-    throws IllegalArgumentException;
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/MultiStringArrayState.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,96 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import org.w3c.dom.Element;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
-import de.intevation.artifacts.common.model.KVP;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.resources.Resources;
-
-
-/**
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public abstract class MultiStringArrayState extends DefaultState {
-
-    private static final Logger logger =
-        Logger.getLogger(MultiStringArrayState.class);
-
-
-    @Override
-    protected void appendItems(
-        Artifact       artifact,
-        ElementCreator creator,
-        String         name,
-        CallContext    context,
-        Element        select
-    ) {
-        try {
-            creator.addAttr(select, "type", "options", true);
-
-            for (KVP kvp: getOptions(artifact, name, context)) {
-                Element item = creator.create("item");
-                creator.addAttr(item, "label", kvp.getValue().toString(), true);
-                creator.addAttr(item, "value", kvp.getKey().toString(), true);
-
-                select.appendChild(item);
-            }
-        }
-        catch (IllegalArgumentException iae) {
-            logger.warn("Illegal argument", iae);
-        }
-    }
-
-
-    @Override
-    protected Element createStaticData(
-        FLYSArtifact   flys,
-        ElementCreator creator,
-        CallContext    cc,
-        String         name,
-        String         value,
-        String         type
-    ) {
-        Element data = creator.create("data");
-        creator.addAttr(data, "name",  name, true);
-        creator.addAttr(data, "type",  type, true);
-        creator.addAttr(data, "label",
-            Resources.getMsg(cc.getMeta(), name, name), true);
-
-        String[] values = value.split(";");
-
-        for (String val: values) {
-            Element item = creator.create("item");
-            creator.addAttr(item, "value", val, true);
-            creator.addAttr(item, "label", getLabelFor(cc, name, val), true);
-
-            data.appendChild(item);
-        }
-
-        return data;
-    }
-
-
-    protected abstract KVP<String, String>[] getOptions(
-        Artifact artifact,
-        String   parameterName,
-        CallContext context
-    )
-    throws IllegalArgumentException;
-
-
-    protected abstract String getLabelFor(
-        CallContext cc,
-        String      parameterName,
-        String      value
-    )
-    throws IllegalArgumentException;
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/OutliersInput.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifactdatabase.data.StateData;
-import de.intevation.artifacts.CallContext;
-
-/**
- * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
- */
-public class OutliersInput extends DefaultState {
-
-    /** The logger used in this class. */
-    private static Logger logger = Logger.getLogger(OutliersInput.class);
-
-    public static final String PARAMETER_NAME = "outliers";
-
-
-    /**
-     * The default constructor that initializes an empty State object.
-     */
-    public OutliersInput() {
-    }
-
-
-    /** Tell UI how to allow for input. */
-    @Override
-    protected String getUIProvider() {
-        return "outliers_input";
-    }
-
-
-    @Override
-    protected String[] getDefaultsFor(CallContext context, StateData data) {
-        if (data != null && data.getName().equals(PARAMETER_NAME)) {
-            return new String[] {"3", "3"};
-        }
-
-        return null;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/OutputState.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.model.FacetTypes;
-
-
-public class OutputState extends DefaultState implements FacetTypes {
-
-    @Override
-    public Element describeStatic(
-        Artifact    artifact,
-        Document    document,
-        Node        root,
-        CallContext context,
-        String      uuid)
-    {
-        return null;
-    }
-
-
-    @Override
-    public Element describe(
-        Artifact    artifact,
-        Document    document,
-        Node        root,
-        CallContext context,
-        String      uuid)
-    {
-        return null;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/PeriodSelect.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,21 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-public class PeriodSelect extends DefaultState {
-
-    public static final String UI_PROVIDER = "period_select";
-
-    private static final long serialVersionUID = 1L;
-
-    /**
-     * The default constructor that initializes an empty State object.
-     */
-    public PeriodSelect() {
-    }
-
-    @Override
-    protected String getUIProvider() {
-        return UI_PROVIDER;
-    }
-
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/PeriodsSelect.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,26 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import org.apache.log4j.Logger;
-
-/**
- * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
- */
-public class PeriodsSelect extends DefaultState {
-
-    /** The logger used in this class. */
-    private static Logger logger = Logger.getLogger(PeriodsSelect.class);
-
-
-    /**
-     * The default constructor that initializes an empty State object.
-     */
-    public PeriodsSelect() {
-    }
-
-    @Override
-    protected String getUIProvider() {
-        return "periods_select";
-    }
-
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/ProfileDistanceSelect.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,13 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class ProfileDistanceSelect extends DefaultState {
-
-    @Override
-    protected String getUIProvider() {
-        return "auto_integer";
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/QSectorSingleState.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,91 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.artifactdatabase.state.Facet;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.QSectorArtifact;
-
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.QSectorFacet;
-
-import de.intevation.flys.artifacts.resources.Resources;
-
-/**
- * The only state for an QSectorArtifact.
- */
-public class QSectorSingleState
-extends      DefaultState
-implements   FacetTypes
-{
-    /** Developer-centric description of facet. */
-    public static final String I18N_DESCRIPTION
-        = "facet.qsector";
-
-    /** The logger that is used in this state. */
-    private static final Logger logger =
-        Logger.getLogger(QSectorSingleState.class);
-
-
-    /**
-     * Add QSectorFacets to list of Facets.
-     *
-     * @param artifact Ignored.
-     * @param hash Ignored.
-     * @param meta CallMeta to be used for internationalization.
-     * @param facets List to add QSectorFacet to.
-     *
-     * @return null.
-     */
-    public Object compute(
-        FLYSArtifact artifact,
-        String       hash,
-        CallMeta     meta,
-        List<Facet>  facets
-    ) {
-        logger.debug("QSectorSingleState.compute()");
-        QSectorArtifact points = (QSectorArtifact) artifact;
-
-        QSectorFacet qfacet = new QSectorFacet(
-            0,
-            QSECTOR,
-            Resources.getMsg(meta, "qsectors", "Q Sectors"));
-
-        facets.add(qfacet);
-
-        return null;
-    }
-
-
-    /** Call compute. */
-    @Override
-    public Object computeInit(
-        FLYSArtifact artifact,
-        String       hash,
-        Object       context,
-        CallMeta     meta,
-        List<Facet>  facets
-     ) {
-        return compute(artifact, hash, meta, facets);
-    }
-
-
-    /** Call compute. */
-    @Override
-    public Object computeFeed(
-        FLYSArtifact artifact,
-        String hash,
-        CallContext context,
-        List<Facet> facets,
-        Object old
-    ) {
-        return compute(artifact, hash, context.getMeta(), facets);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/RangeState.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,95 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import de.intevation.artifacts.Artifact;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.flys.artifacts.access.RangeAccess;
-
-import org.apache.log4j.Logger;
-
-
-/**
- * State in which km range is set.
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public abstract class RangeState extends DefaultState {
-
-    /** The logger that is used in this class. */
-    private Logger logger = Logger.getLogger(RangeState.class);
-
-
-    public RangeState() {
-    }
-
-    protected abstract double[] getMinMax(Artifact artifact);
-
-
-    protected boolean validateBounds(
-        double fromValid, double toValid,
-        double from,      double to)
-    throws IllegalArgumentException
-    {
-        if (from < fromValid) {
-            logger.error(
-                "Invalid 'from'. " + from + " is smaller than " + fromValid);
-            throw new IllegalArgumentException("error_feed_from_out_of_range");
-        }
-        else if (to > toValid) {
-            logger.error(
-                "Invalid 'to'. " + to + " is bigger than " + toValid);
-            throw new IllegalArgumentException("error_feed_to_out_of_range");
-        }
-
-        return true;
-    }
-
-
-    /**
-     * Validates a given range with a given valid range.
-     *
-     * @param fromValid Valid lower value of the range.
-     * @param toValid Valid upper value of the range.
-     * @param from The lower value.
-     * @param to The upper value.
-     * @param step The step width.
-     *
-     * @return true, if everything was fine, otherwise an exception is thrown.
-     */
-    protected boolean validateBounds(
-        double fromValid, double toValid,
-        double from,      double to,      double step)
-    throws IllegalArgumentException
-    {
-        logger.debug("RangeState.validateRange");
-
-        // XXX The step width is not validated at the moment!
-        return validateBounds(fromValid, toValid, from, to);
-    }
-
-
-    @Override
-    public boolean validate(Artifact artifact)
-    throws IllegalArgumentException
-    {
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-
-        try {
-            RangeAccess rangeAccess = new RangeAccess(flys, null);
-            double from = rangeAccess.getFrom();
-            double to   = rangeAccess.getTo();
-            double step = rangeAccess.getStep();
-
-            double[] minmax = getMinMax(flys);
-
-            return validateBounds(minmax[0], minmax[1], from, to, step);
-        }
-        catch (NumberFormatException nfe) {
-            throw new IllegalArgumentException("error_invalid_double_value");
-        }
-        catch (NullPointerException npe) {
-            throw new IllegalArgumentException("error_empty_state");
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/ReferenceCurveState.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,87 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import de.intevation.artifactdatabase.state.Facet;
-
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.WINFOArtifact;
-
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.DataFacet;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.ReferenceCurveFacet;
-import de.intevation.flys.artifacts.model.ReportFacet;
-import de.intevation.flys.artifacts.model.WWQQ;
-
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-
-/** State of WINFO in which reference curves can be produced. */
-public class ReferenceCurveState
-extends      DefaultState
-implements   FacetTypes
-{
-    private static Logger logger = Logger.getLogger(ReferenceCurveState.class);
-
-
-    public ReferenceCurveState() {
-    }
-
-
-    @Override
-    public Object computeAdvance(
-        FLYSArtifact artifact,
-        String       hash,
-        CallContext  context,
-        List<Facet>  facets,
-        Object       old
-    ) {
-        if (!(artifact instanceof WINFOArtifact)) {
-            return null;
-        }
-
-        String id = getID();
-
-        WINFOArtifact winfo = (WINFOArtifact)artifact;
-
-        CalculationResult res = old instanceof CalculationResult
-            ? (CalculationResult)old
-            : winfo.getReferenceCurveData(context);
-
-        if (facets == null) {
-            return res;
-        }
-
-        WWQQ [] wws = (WWQQ [])res.getData();
-
-        for (int i = 0; i < wws.length; ++i) {
-            String wwsName = wws[i].getName();
-            facets.add(new ReferenceCurveFacet(i,
-                REFERENCE_CURVE,
-                wwsName
-                ));
-            facets.add(new ReferenceCurveFacet(i,
-                REFERENCE_CURVE_NORMALIZED,
-                wwsName
-                ));
-        }
-
-        if (wws.length > 0) {
-            logger.debug("Adding CSV and PDF data facet.");
-            Facet csv = new DataFacet (CSV, "CSV data", ComputeType.ADVANCE, hash, id);
-            Facet pdf = new DataFacet (PDF, "PDF data", ComputeType.ADVANCE, hash, id);
-            facets.add(csv);
-            facets.add(pdf);
-        }
-
-        if (res.getReport().hasProblems()) {
-            facets.add(new ReportFacet(ComputeType.ADVANCE, hash, id));
-        }
-
-        return res;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/ReferenceGaugeState.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,81 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import java.util.Collections;
-import java.util.List;
-
-import org.w3c.dom.Element;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.artifacts.common.utils.XMLUtils;
-
-import de.intevation.flys.model.Gauge;
-import de.intevation.flys.model.River;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.utils.FLYSUtils;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class ReferenceGaugeState extends DefaultState {
-
-    public static final String DATA_NAME = "reference_gauge";
-
-
-    @Override
-    protected Element[] createItems(
-        XMLUtils.ElementCreator cr,
-        Artifact    artifact,
-        String      name,
-        CallContext context)
-    {
-        River       river   = FLYSUtils.getRiver((FLYSArtifact) artifact);
-        List<Gauge> gauges  = river.getGauges();
-        Collections.sort(gauges);
-
-        int num = gauges != null ? gauges.size() : 0;
-
-        Element[] opts = new Element[num];
-
-        for (int i = 0; i < num; i++ ) {
-            Gauge g = gauges.get(i);
-
-            String gaugeName      = g.getName();
-            long   officialNumber = g.getOfficialNumber();
-
-            opts[i] = createItem(
-                cr, new String[] { gaugeName, String.valueOf(officialNumber) });
-        }
-
-        return opts;
-    }
-
-
-    @Override
-    protected String getLabelFor(
-        CallContext cc,
-        String      name,
-        String      value,
-        String      type
-    ) {
-        if (name.equals(DATA_NAME)) {
-            try {
-                long  number = Long.valueOf(value);
-                Gauge gauge  = Gauge.getGaugeByOfficialNumber(number);
-
-                if (gauge != null) {
-                    return gauge.getName();
-                }
-            }
-            catch (NumberFormatException nfe) {
-                // do nothing
-            }
-        }
-
-        return super.getLabelFor(cc, name, value, type);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/RiverSelect.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,166 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.artifacts.common.utils.XMLUtils;
-
-import de.intevation.artifactdatabase.ProtocolUtils;
-import de.intevation.artifactdatabase.data.StateData;
-
-import de.intevation.flys.model.River;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.model.RiverFactory;
-import de.intevation.flys.artifacts.resources.Resources;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class RiverSelect extends DefaultState {
-
-    /** The logger used in this class. */
-    private static Logger logger = Logger.getLogger(RiverSelect.class);
-
-    /** Error message that is thrown if no river was found based on a given
-     * name.*/
-    public static final String ERROR_NO_SUCH_RIVER =
-        "error_feed_no_such_river";
-
-    /** Error message that is thrown if no river was found based on a given
-     * name.*/
-    public static final String ERROR_NO_RIVER_SELECTED =
-        "error_feed_no_river_selected";
-
-
-    /**
-     * The default constructor that initializes an empty State object.
-     */
-    public RiverSelect() {
-    }
-
-
-    /**
-     * Initialize the state based on the state node in the configuration.
-     *
-     * @param config The state configuration node.
-     */
-    public void setup(Node config) {
-        super.setup(config);
-    }
-
-
-    protected Element createData(
-        XMLUtils.ElementCreator cr,
-        Artifact    artifact,
-        StateData   data,
-        CallContext context)
-    {
-        Element select = ProtocolUtils.createArtNode(
-            cr, "select",
-            new String[] { "uiprovider" },
-            new String[] { "select_with_map" });
-        cr.addAttr(select, "name", data.getName(), true);
-
-        Element label = ProtocolUtils.createArtNode(
-            cr, "label", null, null);
-
-
-        // XXX: DEAD CODE
-        /*
-        Element choices = ProtocolUtils.createArtNode(
-            cr, "choices", null, null);
-        */
-
-        select.appendChild(label);
-
-        label.setTextContent(Resources.getMsg(
-            context.getMeta(),
-            getID(),
-            getID()));
-
-        return select;
-    }
-
-
-    @Override
-    protected Element[] createItems(
-        XMLUtils.ElementCreator cr,
-        Artifact    artifact,
-        String      name,
-        CallContext context)
-    {
-        List<River> rivers = RiverFactory.getRivers();
-        Element[] items    = new Element[rivers.size()];
-
-        int idx = 0;
-        for (River river: rivers) {
-            items[idx++] = createRiverItem(cr, river);
-        }
-
-        return items;
-    }
-
-
-    /**
-     * This method creates a node that represents a river item. This node
-     * contains the label and the value that describe the river.
-     *
-     * @param cr The ElementCreator.
-     * @param river The river.
-     *
-     * @return the element that contains the information about the river.
-     */
-    protected Element createRiverItem(XMLUtils.ElementCreator cr, River river) {
-        Element item  = ProtocolUtils.createArtNode(cr, "item", null, null);
-        Element label = ProtocolUtils.createArtNode(cr, "label", null, null);
-        Element value = ProtocolUtils.createArtNode(cr, "value", null, null);
-
-        label.setTextContent(river.getName());
-        value.setTextContent(river.getName());
-
-        item.appendChild(label);
-        item.appendChild(value);
-
-        return item;
-    }
-
-
-    @Override
-    public boolean validate(Artifact artifact)
-    throws IllegalArgumentException
-    {
-        logger.debug("RiverSelect.validate");
-
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-
-        StateData dRiver = getData(flys, "river");
-
-        if (dRiver == null || dRiver.getValue() == null) {
-            throw new IllegalArgumentException(ERROR_NO_RIVER_SELECTED);
-        }
-
-        River river = RiverFactory.getRiver((String) dRiver.getValue());
-
-        if (river == null) {
-            throw new IllegalArgumentException(ERROR_NO_SUCH_RIVER);
-        }
-
-        return true;
-    }
-
-
-    @Override
-    protected String getUIProvider() {
-        return "river_panel";
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/SQRelation.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,295 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.FacetActivity;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.artifacts.common.utils.StringUtils;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.flys.artifacts.access.SQRelationAccess;
-
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.DataFacet;
-import de.intevation.flys.artifacts.model.FacetTypes;
-
-import de.intevation.flys.artifacts.model.sq.SQCurveFacet;
-import de.intevation.flys.artifacts.model.sq.SQFractionResult;
-import de.intevation.flys.artifacts.model.sq.SQMeasurementFacet;
-import de.intevation.flys.artifacts.model.sq.SQOutlierCurveFacet;
-import de.intevation.flys.artifacts.model.sq.SQOutlierFacet;
-import de.intevation.flys.artifacts.model.sq.SQOutlierMeasurementFacet;
-import de.intevation.flys.artifacts.model.sq.SQOverviewFacet;
-import de.intevation.flys.artifacts.model.sq.SQRelationCalculation;
-import de.intevation.flys.artifacts.model.sq.SQResult;
-
-import de.intevation.flys.artifacts.resources.Resources;
-
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-/**
- * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
- */
-public class SQRelation extends DefaultState implements FacetTypes {
-
-    private static Logger log = Logger.getLogger(SQRelation.class);
-
-
-    public static final String I18N_FACET_CURVE =
-        "facet.sq_relation.curve";
-
-    public static final String I18N_FACET_MEASUREMENTS =
-        "facet.sq_relation.measurements";
-
-    public static final String I18N_FACET_OUTLIERS =
-        "facet.sq_relation.outliers";
-
-    public static final String I18N_FACET_OUTLIER_CURVE =
-        "facet.sq_relation.outlier.curve";
-
-    public static final String I18N_FACET_OUTLIER_MEASUREMENT =
-        "facet.sq_relation.outlier.measurement";
-
-    public static final int CURVE_INDEX               = 0;
-    public static final int MEASURREMENT_INDEX        = 1;
-    public static final int OUTLIER_INDEX             = 2;
-    public static final int OUTLIER_CURVE_INDEX       = 3;
-    public static final int OUTLIER_MEASUREMENT_INDEX = 4;
-
-    public static final String [][] FACET_NAMES = {
-        { SQ_A_CURVE, SQ_B_CURVE, SQ_C_CURVE,
-          SQ_D_CURVE, SQ_E_CURVE, SQ_F_CURVE
-        },
-        { SQ_A_MEASUREMENT, SQ_B_MEASUREMENT, SQ_C_MEASUREMENT,
-          SQ_D_MEASUREMENT, SQ_E_MEASUREMENT, SQ_F_MEASUREMENT
-        },
-        { SQ_A_OUTLIER, SQ_B_OUTLIER, SQ_C_OUTLIER,
-          SQ_D_OUTLIER, SQ_E_OUTLIER, SQ_F_OUTLIER
-        },
-        { SQ_A_OUTLIER_CURVE, SQ_B_OUTLIER_CURVE, SQ_C_OUTLIER_CURVE,
-          SQ_D_OUTLIER_CURVE, SQ_E_OUTLIER_CURVE, SQ_F_OUTLIER_CURVE
-        },
-        { SQ_A_OUTLIER_MEASUREMENT, SQ_B_OUTLIER_MEASUREMENT,
-          SQ_C_OUTLIER_MEASUREMENT, SQ_D_OUTLIER_MEASUREMENT,
-          SQ_E_OUTLIER_MEASUREMENT, SQ_F_OUTLIER_MEASUREMENT
-        }
-    };
-
-
-    static {
-        // Active/deactivate facets.
-        FacetActivity.Registry.getInstance().register(
-            "minfo",
-            new FacetActivity() {
-                @Override
-                public Boolean isInitialActive(
-                    Artifact artifact,
-                    Facet    facet,
-                    String   output
-                ) {
-                    String name = facet.getName();
-
-                    if (StringUtils.contains(
-                        name, FACET_NAMES[CURVE_INDEX])
-                    ||  StringUtils.contains(
-                        name, FACET_NAMES[OUTLIER_INDEX])
-                    ||  StringUtils.contains(
-                        name, FACET_NAMES[MEASURREMENT_INDEX])
-                    ) {
-                        // TODO: Only the last should be active.
-                        return Boolean.TRUE;
-                    }
-
-                    if (StringUtils.contains(
-                        name, FACET_NAMES[OUTLIER_CURVE_INDEX])
-                     || StringUtils.contains(
-                        name, FACET_NAMES[OUTLIER_MEASUREMENT_INDEX])
-                    ) {
-                        return Boolean.FALSE;
-                    }
-
-                    return null;
-                }
-            });
-    }
-
-
-    public SQRelation() {
-    }
-
-
-    @Override
-    public Object computeAdvance(
-        FLYSArtifact artifact,
-        String       hash,
-        CallContext  context,
-        List<Facet>  facets,
-        Object       old
-    ) {
-        log.debug("SQRelation.computeAdvance");
-
-        CalculationResult res = old instanceof CalculationResult
-            ? (CalculationResult)old
-            : new SQRelationCalculation(
-                new SQRelationAccess(artifact)).calculate();
-
-        if (facets == null) {
-            return res;
-        }
-
-        SQResult [] sqr = (SQResult [])res.getData();
-        if (sqr == null) {
-            return res;
-        }
-
-        createFacets(context, facets, sqr, hash);
-
-        Facet csv = new DataFacet(
-            CSV, "CSV data", ComputeType.ADVANCE, hash, id);
-
-        Facet pdf = new DataFacet(
-            PDF, "PDF data", ComputeType.ADVANCE, hash, id);
-
-        facets.add(csv);
-        facets.add(pdf);
-
-        return res;
-    }
-
-
-    protected void createFacets(
-        CallContext context,
-        List<Facet> container,
-        SQResult[]  sqr,
-        String      hash
-    ) {
-        boolean debug = log.isDebugEnabled();
-
-        CallMeta meta    = context.getMeta();
-        String   stateId = getID();
-        for (int i = 0; i < 6; i++) {
-            container.add(new SQOverviewFacet(
-                i,
-                i,
-                "sq_chart_overview",
-                Resources.getMsg(
-                    meta,
-                    I18N_FACET_CURVE,
-                    I18N_FACET_CURVE
-                ),
-                hash,
-                getID()
-            ));
-        }
-        for (int res = 0, n = sqr.length; res < n; res++) {
-
-            for (int i = 0; i < SQResult.NUMBER_FRACTIONS; i++) {
-                SQFractionResult result = sqr[res].getFraction(i);
-
-                if (result == null) {
-                    log.warn("Fraction at index " + i + " is empty!");
-                    continue;
-                }
-
-                container.add(new SQCurveFacet(
-                    res,
-                    i,
-                    getFractionFacetname(CURVE_INDEX, i),
-                    Resources.getMsg(
-                        meta,
-                        I18N_FACET_CURVE,
-                        I18N_FACET_CURVE
-                    ),
-                    hash,
-                    stateId
-                ));
-
-                for (int j = 0, C = result.numIterations()-1; j < C; j++) {
-
-                    Object [] round = new Object [] { j + 1 };
-
-                    int index = res;
-                    index     = index << 16;
-                    index     = index + j;
-
-                    if (debug) {
-                        log.debug("new outliers facet (index=" +index+ ")");
-                        log.debug("   result index = " + res);
-                        log.debug("   fraction idx = " + i);
-                        log.debug("   iteration    = " + j);
-                    }
-
-                    container.add(new SQOutlierFacet(
-                        index,
-                        i,
-                        getFractionFacetname(OUTLIER_INDEX, i),
-                        Resources.getMsg(
-                            meta,
-                            I18N_FACET_OUTLIERS,
-                            I18N_FACET_OUTLIERS,
-                            round
-                        ),
-                        hash,
-                        stateId
-                    ));
-
-                    container.add(new SQOutlierCurveFacet(
-                        index,
-                        i,
-                        getFractionFacetname(OUTLIER_CURVE_INDEX, i),
-                        Resources.getMsg(
-                            meta,
-                            I18N_FACET_OUTLIER_CURVE,
-                            I18N_FACET_OUTLIER_CURVE,
-                            round
-                        ),
-                        hash,
-                        stateId
-                    ));
-
-                    container.add(new SQOutlierMeasurementFacet(
-                        index,
-                        i,
-                        getFractionFacetname(OUTLIER_MEASUREMENT_INDEX, i),
-                        Resources.getMsg(
-                            meta,
-                            I18N_FACET_OUTLIER_MEASUREMENT,
-                            I18N_FACET_OUTLIER_MEASUREMENT,
-                            round
-                        ),
-                        hash,
-                        stateId
-                    ));
-                } // for all outliers
-
-                container.add(new SQMeasurementFacet(
-                    res,
-                    i,
-                    getFractionFacetname(MEASURREMENT_INDEX, i),
-                    Resources.getMsg(
-                        meta,
-                        I18N_FACET_MEASUREMENTS,
-                        I18N_FACET_MEASUREMENTS
-                    ),
-                    hash,
-                    stateId
-                ));
-            } // for all fractions
-        } // for all results
-    }
-
-    protected static String getFractionFacetname(int type, int idx) {
-        if (log.isDebugEnabled()) {
-            log.debug("getFractionFacetname(): " + type + " | " + idx);
-        }
-        type %= FACET_NAMES.length;
-        return FACET_NAMES[type][idx % FACET_NAMES[type].length];
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/ScenarioSelect.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,138 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import de.intevation.artifactdatabase.ProtocolUtils;
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.common.utils.FileTools;
-import de.intevation.artifacts.common.utils.XMLUtils;
-import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.resources.Resources;
-import de.intevation.flys.utils.FLYSUtils;
-
-import java.io.File;
-
-import org.apache.log4j.Logger;
-import org.w3c.dom.Element;
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class ScenarioSelect extends DefaultState {
-
-    /** The logger that is used in this class.*/
-    private static Logger logger = Logger.getLogger(ScenarioSelect.class);
-
-
-    public static final String FIELD_MODE     = "scenario";
-
-    public static final String SCENARIO_CURRENT   = "scenario.current";
-    public static final String SCENARIO_POTENTIEL = "scenario.potentiel";
-    public static final String SCENARIO_SCENRAIO  = "scenario.scenario";
-
-    public static final String[] SCENARIOS = {
-        SCENARIO_CURRENT,
-        SCENARIO_POTENTIEL,
-        SCENARIO_SCENRAIO };
-
-
-    @Override
-    protected String getUIProvider() {
-        return "";
-    }
-
-    @Override
-    protected Element[] createItems(
-        XMLUtils.ElementCreator cr,
-        Artifact    artifact,
-        String      name,
-        CallContext context)
-    {
-        CallMeta meta = context.getMeta();
-
-        if (name.equals(FIELD_MODE)) {
-            Element[] scenarios = new Element[SCENARIOS.length];
-
-            int i = 0;
-
-            for (String scenario: SCENARIOS) {
-                scenarios[i++] = createItem(
-                    cr, new String[] {
-                        Resources.getMsg(meta, scenario, scenario),
-                        scenario
-                    });
-            }
-
-            return scenarios;
-        }
-        else {
-            FLYSArtifact flys = (FLYSArtifact) artifact;
-            String       data = flys.getDataAsString(name);
-
-            return new Element[] { createItem(
-                cr,
-                new String[] {
-                    Resources.getMsg(meta, name, name),
-                    data
-                }
-            )};
-        }
-    }
-
-
-    @Override
-    protected Element createItem(XMLUtils.ElementCreator cr, Object obj) {
-        Element item  = ProtocolUtils.createArtNode(cr, "item", null, null);
-        Element label = ProtocolUtils.createArtNode(cr, "label", null, null);
-        Element value = ProtocolUtils.createArtNode(cr, "value", null, null);
-
-        String[] arr = (String[]) obj;
-
-        label.setTextContent(arr[0]);
-        value.setTextContent(arr[1]);
-
-        item.appendChild(label);
-        item.appendChild(value);
-
-        return item;
-    }
-
-
-
-    @Override
-    public void endOfLife(Artifact artifact, Object callContext) {
-        super.endOfLife(artifact, callContext);
-        logger.info("ScenarioSelect.endOfLife: " + artifact.identifier());
-
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-        removeDirectory(flys);
-    }
-
-
-    /**
-     * Removes the directory and all its content where the required data and the
-     * results of WSPLGEN are stored. Should be called in endOfLife().
-     */
-    // FIXME: I've seen this code somewhere else...
-    protected void removeDirectory(FLYSArtifact artifact) {
-        String shapePath = FLYSUtils.getXPathString(
-            FLYSUtils.XPATH_FLOODMAP_SHAPEFILE_DIR);
-
-        File artifactDir = new File(shapePath, artifact.identifier());
-
-        if (artifactDir.exists()) {
-            logger.debug("Delete directory: " + artifactDir.getAbsolutePath());
-            boolean success = FileTools.deleteRecursive(artifactDir);
-            if (!success) {
-                logger.warn("could not remove dir '" + artifactDir + "'");
-            }
-        }
-        else {
-            logger.debug("There is no directory to remove.");
-        }
-    }
-
-
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/SoundingsSelect.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,208 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.artifacts.common.model.KVP;
-
-import de.intevation.flys.model.BedHeightEpoch;
-import de.intevation.flys.model.BedHeightSingle;
-import de.intevation.flys.model.River;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.utils.FLYSUtils;
-
-
-public class SoundingsSelect extends MultiStringArrayState {
-
-    public static final String SOUNDINGS = "soundings";
-
-    public static final String PREFIX_SINGLE = "single-";
-
-    public static final String PREFIX_EPOCH = "epoch-";
-
-    /** Private logger. */
-    private static final Logger logger = Logger.getLogger(SoundingsSelect.class);
-
-
-    @Override
-    public String getUIProvider() {
-        return "parameter-matrix";
-    }
-
-
-    @Override
-    protected KVP<String, String>[] getOptions(
-        Artifact artifact,
-        String   parameterName,
-        CallContext context
-    )
-    throws IllegalArgumentException
-    {
-        logger.debug("Get options for parameter: '" + parameterName + "'");
-
-        if (!testParameterName(parameterName)) {
-            throw new IllegalArgumentException(
-                "Invalid parameter for state: '" + parameterName + "'");
-        }
-
-        River river = FLYSUtils.getRiver((FLYSArtifact) artifact);
-        double lo = ((FLYSArtifact) artifact).getDataAsDouble("ld_from");
-        double hi = ((FLYSArtifact) artifact).getDataAsDouble("ld_to");
-
-        double kmLo = Math.min(lo, hi);
-        double kmHi = Math.max(lo, hi);
-
-        List<KVP<String, String>> kvp = new ArrayList<KVP<String, String>>();
-
-        appendSingles(river, kmLo, kmHi, kvp);
-        appendEpochs(river, kmLo, kmHi, kvp);
-
-        return kvp.toArray(new KVP[kvp.size()]);
-    }
-
-
-    protected void appendSingles(
-        River river,
-        double kmLo,
-        double kmHi,
-        List<KVP<String, String>> kvp
-    ) {
-        List<BedHeightSingle> singles =
-            BedHeightSingle.getBedHeightSingles(river, kmLo, kmHi);
-
-        if (singles != null) {
-            int size = singles.size();
-
-            logger.debug("Found " + size + " singles.");
-
-            for (int i = 0; i < size; i++) {
-                BedHeightSingle s = singles.get(i);
-
-                String id    = PREFIX_SINGLE + s.getId();
-                String value = s.getDescription();
-
-                kvp.add(new KVP(id, value));
-            }
-        }
-    }
-
-
-    protected void appendEpochs(
-        River river,
-        double kmLo,
-        double kmHi,
-        List<KVP<String, String>> kvp
-    ) {
-        List<BedHeightEpoch> epochs =
-            BedHeightEpoch.getBedHeightEpochs(river, kmLo, kmHi);
-
-        if (epochs != null) {
-            int size = epochs.size();
-
-            logger.debug("Found " + size + " epochs.");
-
-            for (int i = 0; i < size; i++) {
-                BedHeightEpoch e = epochs.get(i);
-
-                String id    = PREFIX_EPOCH + e.getId();
-                String value = e.getDescription();
-
-                kvp.add(new KVP(id, value));
-            }
-        }
-    }
-
-
-    @Override
-    protected String getLabelFor(
-        CallContext cc,
-        String      parameterName,
-        String      value
-    ) throws IllegalArgumentException
-    {
-        if (!testParameterName(parameterName)) {
-            throw new IllegalArgumentException(
-                "Invalid parameter for state: '" + parameterName + "'");
-        }
-
-        if (value.indexOf(PREFIX_SINGLE) >= 0) {
-            return getLabelForSingle(cc, value);
-        }
-        else if (value.indexOf(PREFIX_EPOCH) >= 0) {
-            return getLabelForEpoch(cc, value);
-        }
-
-        return value;
-    }
-
-
-    protected String getLabelForSingle(CallContext cc, String value) {
-        String id = value.replace(PREFIX_SINGLE, "");
-        try {
-            BedHeightSingle s = BedHeightSingle.getBedHeightSingleById(
-                Integer.parseInt(id));
-
-            if (s != null) {
-                return s.getDescription();
-            }
-            else {
-                return "no value for '" + id + "'";
-            }
-        }
-        catch (NumberFormatException nfe) {
-            logger.warn("Could not parse id from string '" + id + "'", nfe);
-        }
-
-        return "n.A.";
-    }
-
-
-    protected String getLabelForEpoch(CallContext cc, String value) {
-        String id = value.replace(PREFIX_EPOCH, "");
-        try {
-            BedHeightEpoch e = BedHeightEpoch.getBedHeightEpochById(
-                Integer.parseInt(id));
-
-            if (e != null) {
-                return e.getDescription();
-            }
-            else {
-                return "no value for '" + id + "'";
-            }
-        }
-        catch (NumberFormatException nfe) {
-            logger.warn("Could not parse id from string '" + id + "'", nfe);
-        }
-
-        return "n.A.";
-    }
-
-
-    /**
-     * This method might be used to test, if a parameter name is handled by this
-     * state.
-     *
-     * @param parameterName The name of a parameter.
-     *
-     * @return true, if parameterName is one of <i>MAIN_CHANNEL</i> or
-     * <i>TOTAL_CHANNEL</i>. Otherwise false.
-     */
-    protected boolean testParameterName(String parameterName) {
-        if (parameterName == null || parameterName.length() == 0) {
-            return false;
-        }
-        else if (parameterName.equals(SOUNDINGS)) {
-            return true;
-        }
-        else {
-            return false;
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/StateFactory.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,122 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import javax.xml.xpath.XPathConstants;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-
-import de.intevation.artifactdatabase.data.DefaultStateData;
-import de.intevation.artifactdatabase.state.State;
-
-import de.intevation.artifacts.common.utils.XMLUtils;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class StateFactory {
-
-    /** The logger used in this class */
-    private static Logger logger = Logger.getLogger(StateFactory.class);
-
-    /** The XPath to the classname of the state */
-    public static final String XPATH_STATE = "@state";
-
-    /** The XPath to the data items of the state relative to the state node. */
-    public static final String XPATH_DATA = "data";
-
-    /** The XPath to the data name relative to the data node.*/
-    public static final String XPATH_DATA_NAME = "@name";
-
-    /** The XPath to the data type relative to the data node.*/
-    public static final String XPATH_DATA_TYPE = "@type";
-
-    /** The XPath to the data description relative to the data node.*/
-    public static final String XPATH_DATA_DESCRIPTION = "@description";
-
-
-    /**
-     * Creates a new State based on the configured class provided by
-     * <code>stateConf</code>.
-     *
-     * @param stateConf The configuration of the state.
-     *
-     * @return a State.
-     */
-    public static State createState(Node stateConf) {
-        String clazz = (String) XMLUtils.xpath(
-            stateConf, XPATH_STATE, XPathConstants.STRING);
-
-        State state = null;
-
-        try {
-            logger.debug("Create a new State for class: " + clazz);
-            state = (State) Class.forName(clazz).newInstance();
-            state.setup(stateConf);
-
-            initializeStateData(state, stateConf);
-        }
-        catch (InstantiationException ie) {
-            logger.error(ie, ie);
-        }
-        catch (IllegalAccessException iae) {
-            logger.error(iae, iae);
-        }
-        catch (ClassNotFoundException cnfe) {
-            logger.error(cnfe, cnfe);
-        }
-
-        return state;
-    }
-
-
-    /**
-     * This method extracts the configured input data of a state and adds new
-     * StateData objects to the State.
-     *
-     * @param state The state.
-     * @param stateConf The state configuration node.
-     */
-    protected static void initializeStateData(State state, Node stateConf) {
-        NodeList dataList = (NodeList) XMLUtils.xpath(
-            stateConf, XPATH_DATA, XPathConstants.NODESET);
-
-        if (dataList == null || dataList.getLength() == 0) {
-            logger.debug("The state has no input data configured.");
-
-            return;
-        }
-
-        int items = dataList.getLength();
-
-        logger.debug("The state has " + items + " data items configured.");
-
-        for (int i = 0; i < items; i++) {
-            Node data = dataList.item(i);
-
-            String name = (String) XMLUtils.xpath(
-                data, XPATH_DATA_NAME, XPathConstants.STRING);
-            String type = (String) XMLUtils.xpath(
-                data, XPATH_DATA_TYPE, XPathConstants.STRING);
-            String desc = (String) XMLUtils.xpath(
-                data, XPATH_DATA_DESCRIPTION, XPathConstants.STRING);
-
-            if (name == null || name.length() == 0) {
-                logger.warn("No name for data item at pos " + i + " found.");
-                continue;
-            }
-
-            if (type == null || type.length() == 0) {
-                logger.warn("No type for data item at pos " + i + " found.");
-                logger.warn("Default type 'string' used.");
-                type = "string";
-            }
-
-            logger.debug("add StateData '" + name + "' (type '" + type + "')");
-            state.addData(name, new DefaultStateData(name, desc, type));
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/StaticHYKState.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,110 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.artifactdatabase.state.Facet;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.HYKArtifact;
-
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.HYKFacet;
-import de.intevation.flys.artifacts.model.HYKFactory;
-
-/**
- * Only state of a HYKArtifact.
- */
-public class StaticHYKState
-extends      DefaultState
-implements   FacetTypes
-{
-    /** The logger that is used in this state. */
-    transient private static final Logger logger = Logger.getLogger(StaticHYKState.class);
-
-
-    /**
-     * From this state can not be continued.
-     */
-    @Override
-    protected String getUIProvider() {
-        return "noinput";
-    }
-
-
-    /**
-     * Compute, create Facets, do the same stuff as all the other states do.
-     */
-    protected Object compute(
-        HYKArtifact   hyk,
-        CallMeta      metaLocale,
-        String        hash,
-        List<Facet>   facets,
-        Object        old
-    ) {
-        logger.debug("StaticHYKState.compute");
-        String id = getID();
-
-        // Prepare comparison against cached result.
-        List<HYKFactory.Zone> resZones = old instanceof List
-            ? (List<HYKFactory.Zone>)old
-            : null;
-
-        // TODO Compare against cached object.
-
-        // Get Zones from HYKFactory
-        List<HYKFactory.Zone> zones = (List<HYKFactory.Zone>)
-            HYKFactory.getHYKs(hyk.getHykId(), hyk.getKm());
-
-        if (facets == null) {
-            logger.debug("StaticHYKState.compute no facets");
-            return zones;
-        }
-
-        // Spawn Facets.
-        Facet facet = new HYKFacet(0, HYKFactory.getHykName(hyk.getHykId()));
-        facets.add(facet);
-
-        return zones;
-    }
-
-
-    /**
-     * Get data, create the facets.
-     *
-     * @param context Ignored.
-     */
-    @Override
-    public Object computeFeed(
-        FLYSArtifact artifact,
-        String       hash,
-        CallContext  context,
-        List<Facet>  facets,
-        Object       old
-    ) {
-        return compute((HYKArtifact) artifact, context.getMeta(),
-            hash, facets, old);
-    }
-
-
-    /**
-     * Create the facets.
-     * @param context Ignored.
-     */
-    @Override
-    public Object computeInit(
-        FLYSArtifact artifact,
-        String       hash,
-        Object       context,
-        CallMeta     meta,
-        List<Facet>  facets
-    ) {
-        return compute((HYKArtifact) artifact, meta, hash, facets,
-            null);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/StaticState.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,134 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.flys.artifacts.model.FacetTypes;
-
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.DefaultOutput;
-import de.intevation.artifactdatabase.state.Output;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.CallMeta;
-
-/**
- * Yet, a non-abstract DefaultState.
- */
-public class StaticState
-extends      DefaultState
-implements   FacetTypes
-{
-    /** The logger that is used in this state. */
-    private static final Logger logger = Logger.getLogger(StaticState.class);
-
-    private static String uiprovider;
-
-
-    public StaticState() {
-        super();
-    }
-
-    /**
-     * Trivial constructor, sets id and description.
-     * @param id String used for both id and description.
-     */
-    public StaticState(String id) {
-        this(id, id);
-    }
-
-
-    public StaticState(String id, String description) {
-        super();
-        setID(id);
-        setDescription(description);
-    }
-
-    public void addDefaultChartOutput(String nameDesc, List<Facet> facets) {
-        DefaultOutput output = new DefaultOutput(nameDesc,
-            nameDesc, "image/png", facets, "chart");
-        getOutputs().add(output);
-    }
-
-    public static void addDefaultChartOutput(
-        DefaultState state,
-        String nameDesc,
-        List<Facet> facets
-    ) {
-        DefaultOutput output = new DefaultOutput(nameDesc,
-            nameDesc, "image/png", facets, "chart");
-        state.getOutputs().add(output);
-    }
-
-
-    /**
-     * Do nothing (override to include your logic).
-     * @param facets List of facets (to add to).
-     */
-    public Object staticCompute(List<Facet> facets, FLYSArtifact artifact) {
-        return staticCompute(facets);
-    }
-
-    public Object staticCompute(List<Facet> facets) {
-        return null;
-    }
-
-
-    /** Call staticCompute to allow easy adjustments. */
-    @Override
-    public Object computeAdvance(
-        FLYSArtifact artifact,
-        String       hash,
-        CallContext  context,
-        List<Facet>  facets,
-        Object       old
-    ) {
-        return staticCompute(facets, artifact);
-    }
-
-
-    /** Call staticCompute to allow easy adjustments. */
-    @Override
-    public Object computeFeed(
-        FLYSArtifact artifact,
-        String       hash,
-        CallContext  context,
-        List<Facet>  facets,
-        Object       old
-    ) {
-        return staticCompute(facets, artifact);
-    }
-
-
-    /** Call staticCompute to allow easy adjustments. */
-    @Override
-    public Object computeInit(
-        FLYSArtifact artifact,
-        String       hash,
-        Object       context,
-        CallMeta     meta,
-        List<Facet>  facets
-    ) {
-        return staticCompute(facets, artifact);
-    }
-
-    public void addOutput(Output out) {
-        super.addOutput(out);
-    }
-
-    @Override
-    protected String getUIProvider() {
-        return this.uiprovider;
-    }
-
-    /**
-     * Allow to set the uiprovider for displaying the static data
-     */
-    public void setUIProvider(String uiprovider) {
-        this.uiprovider = uiprovider;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/StaticWQKmsState.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,145 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.artifactdatabase.state.Facet;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.StaticWQKmsArtifact;
-
-import de.intevation.flys.artifacts.model.CrossSectionWaterLineFacet;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.RelativePointFacet;
-import de.intevation.flys.artifacts.model.WQKms;
-import de.intevation.flys.artifacts.model.WQKmsFacet;
-
-/**
- * Only state of WQKmsArtifact.
- */
-public class StaticWQKmsState
-extends      DefaultState
-implements   FacetTypes
-{
-    /** The logger that is used in this state. */
-    private static Logger logger = Logger.getLogger(StaticWQKmsState.class);
-
-
-    /**
-     * From this state can not be continued.
-     */
-    @Override
-    protected String getUIProvider() {
-        return "noinput";
-    }
-
-
-    /**
-     * Compute, create Facets, do the same stuff as all the other states do.
-     */
-    protected Object compute(
-        StaticWQKmsArtifact winfo,
-        CallMeta      metaLocale,
-        String        hash,
-        List<Facet>   facets,
-        Object        old
-    ) {
-        String id = getID();
-
-        WQKms res = old instanceof WQKms
-            ? (WQKms)old
-            : winfo.getWQKms();
-
-        WQKms wqkms = res;
-
-        if (facets == null) {
-            return res;
-        }
-
-        /*
-         * TODO: re-enable HEIGHTMARKS_POINTS-thing
-
-           String name;
-           if (parts[0].equals(HEIGHTMARKS_POINTS)) {
-               name = HEIGHTMARKS_POINTS;
-           }
-           else {
-               name = STATIC_WQKMS;
-           }
-        */
-
-        String wkmsName = wqkms.getName();
-        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);
-
-        wkmsName = "W (" + wkmsName + ")";
-
-        Facet csFacet = new CrossSectionWaterLineFacet(0,
-             wkmsName);
-        facets.add(csFacet);
-
-        Facet wfacet = new WQKmsFacet(
-            STATIC_WQKMS_W,
-            wkmsName
-            /*
-            // TODO re-enable translations.
-            Resources.getMsg(
-                metaLocale,
-                wkmsName,
-                wkmsName)*/);
-        facets.add(wfacet);
-
-        return res;
-    }
-
-
-    /**
-     * Get data, create the facets.
-     *
-     * @param context Ignored.
-     */
-    @Override
-    public Object computeFeed(
-        FLYSArtifact artifact,
-        String       hash,
-        CallContext  context,
-        List<Facet>  facets,
-        Object       old
-    ) {
-        return compute((StaticWQKmsArtifact) artifact, context.getMeta(),
-            hash, facets, old);
-    }
-
-
-    /**
-     * Create the facets.
-     * @param context Ignored.
-     */
-    @Override
-    public Object computeInit(
-        FLYSArtifact artifact,
-        String       hash,
-        Object       context,
-        CallMeta     meta,
-        List<Facet>  facets
-    ) {
-        return compute((StaticWQKmsArtifact) artifact, meta, hash, facets,
-            null);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/UserRGDState.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import org.w3c.dom.Element;
-
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-
-public class UserRGDState
-extends DefaultState
-{
-   @Override
-    protected String getUIProvider() {
-        return "user_rgd_panel";
-    }
-
-    @Override
-    protected Element createStaticData(
-        FLYSArtifact   flys,
-        ElementCreator creator,
-        CallContext    cc,
-        String         name,
-        String         value,
-        String         type
-    ) {
-        Element dataElement = creator.create("data");
-        creator.addAttr(dataElement, "name", name, true);
-        creator.addAttr(dataElement, "type", type, true);
-
-        Element itemElement = creator.create("item");
-        creator.addAttr(itemElement, "value", value, true);
-
-        creator.addAttr(itemElement, "label", getLabel(cc, value), true);
-        dataElement.appendChild(itemElement);
-
-        return dataElement;
-    }
-
-    public static String getLabel(CallContext cc, String value) {
-
-        return value;
-    }
-
-
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/WDifferencesState.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,191 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.data.StateData;
-
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.Artifact;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.StaticWKmsArtifact;
-import de.intevation.flys.artifacts.WINFOArtifact;
-import de.intevation.flys.artifacts.ChartArtifact;
-
-import de.intevation.flys.artifacts.math.WKmsOperation;
-
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.DataFacet;
-import de.intevation.flys.artifacts.model.DifferenceCurveFacet;
-import de.intevation.flys.artifacts.model.EmptyFacet;
-
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.WKms;
-import de.intevation.flys.artifacts.model.WQKms;
-
-import de.intevation.flys.utils.FLYSUtils;
-import de.intevation.flys.utils.StringUtil;
-
-
-public class WDifferencesState
-extends      DefaultState
-implements   FacetTypes
-{
-    /** The logger that is used in this state. */
-    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() {
-        return "noinput";
-    }
-
-
-    @Override
-    public boolean validate(Artifact artifact)
-    throws IllegalArgumentException
-    {
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-
-        StateData data = flys.getData("diffids");
-
-        if (data == null) {
-            throw new IllegalArgumentException("diffids is empty");
-        }
-
-        // TODO: Also validate format.
-
-        return true;
-    }
-
-
-    /**
-     * Access the data (wkms).
-     */
-    protected WKms getWKms(String mingle, CallContext context) {
-        String[] def  = mingle.split(";");
-        String   uuid = def[0];
-        String   name = def[1];
-        int      idx  = Integer.parseInt(def[2]);
-
-        if (name.startsWith("staticwkms")) {
-            StaticWKmsArtifact staticWKms =
-                (StaticWKmsArtifact) FLYSUtils.getArtifact(
-                    uuid,
-                    context);
-            logger.debug("WDifferencesState obtain data from StaticWKms");
-            WKms wkms = staticWKms.getWKms(idx);
-            if (wkms == null)
-                logger.error("No WKms from artifact.");
-            return wkms;
-        }
-
-        WINFOArtifact flys = (WINFOArtifact) FLYSUtils.getArtifact(
-            uuid,
-            context);
-
-        if (flys == null) {
-            logger.warn("One of the artifacts (1) for diff calculation could not be loaded");
-            return null;
-        }
-        else{
-            WQKms[] wqkms = (WQKms[]) flys.getWaterlevelData().
-                                              getData();
-            if (wqkms == null)
-            logger.warn("not  waterlevels in artifact");
-            else if (wqkms.length < idx)
-            logger.warn("not enough waterlevels in artifact");
-            return wqkms[idx];
-        }
-    }
-
-
-    /**
-     * Return CalculationResult with Array of WKms that are difference of
-     * Waterlevels. Add respective facets (DifferencesCurveFacet, DataFacet).
-     */
-    @Override
-    public Object computeAdvance(
-        FLYSArtifact artifact,
-        String       hash,
-        CallContext  context,
-        List<Facet>  facets,
-        Object       old
-    ) {
-        if (artifact instanceof ChartArtifact) {
-            ChartArtifact chart = (ChartArtifact)artifact;
-            facets.add(new EmptyFacet());
-            return null;
-        }
-        WINFOArtifact winfo = (WINFOArtifact) artifact;
-        String id = getID();
-
-        // Load the Artifacts/facets that we want to subtract and display.
-        // Expected format is:
-        // [42537f1e-3522-42ef-8968-635b03d8e9c6;longitudinal_section.w;0]#[1231f2-....]
-        String diffids = winfo.getDataAsString("diffids");
-        logger.debug("WDifferencesState has: " + diffids);
-        String datas[] = diffids.split("#");
-
-        // Validate the Data-Strings.
-        for (String s: datas) {
-            if (!WaterlevelSelectState.isValueValid(s)) {
-                // TODO: escalate.
-            }
-        }
-
-        if (datas.length < 2) {
-            // TODO crash with style
-        }
-
-        List<WKms> wkmss = new ArrayList<WKms>();
-
-        for(int i = 0; i < datas.length; i+=2) {
-            // e.g.:
-            // 42537f1e-3522-42ef-8968-635b03d8e9c6;longitudinal_section.w;1
-            WKms minuendWKms = getWKms(StringUtil.unbracket(datas[i+0]),
-                context);
-            WKms subtrahendWKms = getWKms(StringUtil.unbracket(datas[i+1]),
-                context);
-
-            String facetName = "diff ()";
-
-            if (minuendWKms != null && subtrahendWKms != null) {
-                facetName = StringUtil.wWrap(minuendWKms.getName())
-                    + " - " + StringUtil.wWrap(subtrahendWKms.getName());
-                WKms wkms = WKmsOperation.SUBTRACTION.operate(minuendWKms,
-                     subtrahendWKms);
-                wkms.setName(facetName);
-                wkmss.add(wkms);
-                logger.debug("WKMSSubtraction happened");
-            }
-            if (facets != null) {
-                facets.add(new DifferenceCurveFacet(i/2, W_DIFFERENCES, facetName,
-                    ComputeType.ADVANCE, id, hash));
-            }
-        }
-
-        if (facets != null) {
-            facets.add(new DataFacet(CSV, "CSV data"));
-            facets.add(new DataFacet(PDF, "PDF data"));
-            logger.debug("Adding facets in WDifferencesState.");
-        }
-        else {
-            logger.debug("Not adding facets in WDifferencesState.");
-        }
-
-        // TODO Evaluate whether null is okay as reports.
-        WKms[] diffs = wkmss.toArray(new WKms[wkmss.size()]);
-        CalculationResult result = new CalculationResult(diffs, null);
-        return result;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/WMSBackgroundState.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,156 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.xml.xpath.XPathConstants;
-
-import org.apache.log4j.Logger;
-import org.w3c.dom.Document;
-import org.w3c.dom.Node;
-
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.common.utils.Config;
-import de.intevation.artifacts.common.utils.XMLUtils;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.model.map.WMSLayerFacet;
-import de.intevation.flys.artifacts.resources.Resources;
-
-
-public class WMSBackgroundState extends OutputState {
-
-    public static final String I18N_DESCRIPTION = "floodmap.wmsbackground";
-
-    public static final String XPATH_SRID =
-        "/artifact-database/floodmap/river[@name=$name]/srid/@value";
-
-    public static final String XPATH_WMS_URL =
-        "/artifact-database/floodmap/river[@name=$name]/background-wms/@url";
-
-    public static final String XPATH_WMS_LAYER =
-        "/artifact-database/floodmap/river[@name=$name]/background-wms/@layers";
-
-
-    protected String url;
-    protected String layer;
-    protected String srid;
-
-    protected Document cfg;
-
-    protected Map<String, String> variables;
-
-
-    private static final Logger logger = Logger.getLogger(WMSBackgroundState.class);
-
-
-    @Override
-    public void setup(Node config) {
-        super.setup(config);
-
-        logger.debug("WMSBackgroundState.setup()");
-    }
-
-
-    @Override
-    public Object computeInit(
-        FLYSArtifact artifact,
-        String       hash,
-        Object       context,
-        CallMeta     meta,
-        List<Facet>  facets
-    ) {
-        logger.debug("WMSBackgroundState.computeInit()");
-
-        initVariables(artifact);
-
-        if (url == null || layer == null) {
-            // XXX I don't remember why 'srid', 'url' and 'layer' are member
-            // variables. I think the reason was buffering those values.
-            srid  = getSrid();
-            url   = getUrl();
-            layer = getLayer();
-        }
-
-        if (url == null || layer == null) {
-            logger.warn("No background layers currently configured:");
-            logger.warn("... add config for WMS url: " + XPATH_WMS_URL);
-            logger.warn("... add config for WMS layer: " + XPATH_WMS_LAYER);
-            return null;
-        }
-
-        WMSLayerFacet facet = new WMSLayerFacet(
-            0,
-            getFacetType(),
-            getTitle(meta),
-            ComputeType.INIT,
-            getID(), hash,
-            url);
-
-        facet.addLayer(layer);
-        facet.setSrid(srid);
-
-        facets.add(facet);
-
-        return null;
-    }
-
-
-    protected Document getConfig() {
-        if (cfg == null) {
-            cfg = Config.getConfig();
-        }
-
-        return cfg;
-    }
-
-
-    protected void initVariables(FLYSArtifact artifact) {
-        String river = artifact.getDataAsString("river");
-
-        variables = new HashMap<String, String>();
-        variables.put("name", river);
-    }
-
-
-    protected String getFacetType() {
-        return FLOODMAP_WMSBACKGROUND;
-    }
-
-
-    protected String getSrid() {
-        return (String) XMLUtils.xpath(
-            getConfig(),
-            XPATH_SRID,
-            XPathConstants.STRING,
-            null,
-            variables);
-    }
-
-
-    protected String getUrl() {
-        return (String) XMLUtils.xpath(
-            getConfig(),
-            XPATH_WMS_URL,
-            XPathConstants.STRING,
-            null,
-            variables);
-    }
-
-
-    protected String getLayer() {
-        return (String) XMLUtils.xpath(
-            getConfig(),
-            XPATH_WMS_LAYER,
-            XPathConstants.STRING,
-            null,
-            variables);
-    }
-
-
-    protected String getTitle(CallMeta meta) {
-        return Resources.getMsg(meta, I18N_DESCRIPTION, I18N_DESCRIPTION);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/WQAdapted.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,476 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Comparator;
-import java.util.Collections;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Element;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.artifactdatabase.ProtocolUtils;
-import de.intevation.artifactdatabase.data.StateData;
-
-import de.intevation.artifacts.common.utils.XMLUtils;
-
-import de.intevation.flys.artifacts.access.RangeAccess;
-
-import de.intevation.flys.model.Gauge;
-import de.intevation.flys.model.Range;
-import de.intevation.flys.model.River;
-import de.intevation.flys.model.Wst;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.flys.artifacts.model.RangeWithValues;
-import de.intevation.flys.artifacts.model.WstFactory;
-import de.intevation.flys.utils.FLYSUtils;
-
-
-/**
- * State to input W/Q data.
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class WQAdapted extends DefaultState {
-
-    /** The logger used in this state.*/
-    private static Logger logger = Logger.getLogger(WQAdapted.class);
-
-    public static final String FIELD_WQ_MODE = "wq_isq";
-
-    public static final String FIELD_WQ_VALUES = "wq_values";
-
-    public static final class GaugeOrder implements Comparator<Gauge> {
-        private int order;
-
-        public GaugeOrder(boolean up) {
-            order = up ? 1 : -1;
-        }
-
-        public int compare(Gauge a, Gauge b) {
-            return order * a.getRange().getA().compareTo(b.getRange().getA());
-        }
-    } // class GaugeOrder
-
-    public static final GaugeOrder GAUGE_UP   = new GaugeOrder(true);
-    public static final GaugeOrder GAUGE_DOWN = new GaugeOrder(false);
-
-
-    /** Trivial, empty constructor. */
-    public WQAdapted() {
-    }
-
-
-    /**
-     * This method creates one element for each gauge of the selected river that
-     * is intersected by the given kilometer range. Each element is a tuple of
-     * (from;to) where <i>from</i> is the lower bounds of the gauge or the lower
-     * kilometer range. <i>to</i> is the upper bounds of the gauge or the upper
-     * kilometer range.
-     *
-     * @param cr The ElementCreator.
-     * @param artifact The FLYS artifact.
-     * @param name The name of the data item.
-     * @param context The CallContext.
-     *
-     * @return a list of elements that consist of tuples of the intersected
-     * gauges of the selected river.
-     */
-    @Override
-    protected Element[] createItems(
-        XMLUtils.ElementCreator cr,
-        Artifact    artifact,
-        String      name,
-        CallContext context)
-    {
-        logger.debug("WQAdapted.createItems");
-
-        if (name != null && name.equals(FIELD_WQ_MODE)) {
-            return createModeItems(cr, artifact, name, context);
-        }
-        else if (name != null && name.equals(FIELD_WQ_VALUES)) {
-            return createValueItems(cr, artifact, name, context);
-        }
-        else {
-            logger.warn("Unknown data object: " + name);
-            return null;
-        }
-    }
-
-
-    /** Creates "Q" and "W" items. */
-    protected Element[] createModeItems(
-        XMLUtils.ElementCreator cr,
-        Artifact    artifact,
-        String      name,
-        CallContext context)
-    {
-        logger.debug("WQAdapted.createModeItems");
-
-        Element w = createItem(cr, new String[] { "w", "W" });
-        Element q = createItem(cr, new String[] { "q", "Q" });
-
-        return new Element[] { w, q };
-    }
-
-
-    /** Create the items for input to the ranges per mode. */
-    protected Element[] createValueItems(
-        XMLUtils.ElementCreator cr,
-        Artifact    artifact,
-        String      name,
-        CallContext context)
-    {
-        logger.debug("WQAdapted.createValueItems");
-
-        FLYSArtifact flysArtifact = (FLYSArtifact) artifact;
-
-        RangeAccess rangeAccess = new RangeAccess(flysArtifact, context);
-        double[]    dist   = rangeAccess.getKmRange();
-        // TODO use Access to get River and gauges.
-        River       river  = FLYSUtils.getRiver(flysArtifact);
-        Wst         wst    = WstFactory.getWst(river);
-        List<Gauge> gauges = FLYSUtils.getGauges(flysArtifact);
-
-        int num = gauges != null ? gauges.size() : 0;
-
-        if (num == 0) {
-            logger.warn("Selected distance matches no gauges.");
-            return null;
-        }
-
-        List<Element> elements = new ArrayList<Element>();
-
-        double rangeFrom = dist[0];
-        double rangeTo   = dist[1];
-
-        if (rangeFrom < rangeTo) {
-            Collections.sort(gauges, GAUGE_UP);
-            for (Gauge gauge: gauges) {
-                Range range = gauge.getRange();
-                double lower = range.getA().doubleValue();
-                double upper = range.getB().doubleValue();
-
-                // If gauge out of range, skip it.
-                if (upper <= rangeFrom || lower >= rangeTo) {
-                    continue;
-                }
-
-                double from = lower < rangeFrom ? rangeFrom : lower;
-                double to   = upper > rangeTo   ? rangeTo   : upper;
-
-                double[] mmQ = determineMinMaxQ(gauge, wst);
-                double[] mmW = gauge.determineMinMaxW();
-
-                elements.add(createItem(
-                    cr, new String[] { from + ";" + to, gauge.getName()}, mmQ, mmW));
-            }
-        }
-        else {
-            Collections.sort(gauges, GAUGE_DOWN);
-            rangeFrom = dist[1];
-            rangeTo   = dist[0];
-            for (Gauge gauge: gauges) {
-                Range range = gauge.getRange();
-                double lower = range.getA().doubleValue();
-                double upper = range.getB().doubleValue();
-
-                double from = lower < rangeFrom ? rangeFrom : lower;
-                double to   = upper > rangeTo   ? rangeTo   : upper;
-
-                // TODO probably need to continue out if oof range (see above).
-
-                double[] mmQ = determineMinMaxQ(gauge, wst);
-                double[] mmW = gauge.determineMinMaxW();
-
-                elements.add(createItem(
-                    cr, new String[] { to + ";" + from, gauge.getName()}, mmQ, mmW));
-            }
-        }
-
-        Element[] els = new Element[elements.size()];
-        return elements.toArray(els);
-    }
-
-
-    protected Element createItem(XMLUtils.ElementCreator cr, Object obj) {
-        return createItem(cr, obj, null, null);
-    }
-
-
-    /** In obj: 0 is label, 1 is value. */
-    protected Element createItem(
-        XMLUtils.ElementCreator cr,
-        Object   obj,
-        double[] q,
-        double[] w)
-    {
-        Element item  = ProtocolUtils.createArtNode(cr, "item", null, null);
-        Element label = ProtocolUtils.createArtNode(cr, "label", null, null);
-        Element value = ProtocolUtils.createArtNode(cr, "value", null, null);
-
-        String[] arr = (String[]) obj;
-
-        label.setTextContent(arr[0]);
-        value.setTextContent(arr[1]);
-
-        item.appendChild(label);
-        item.appendChild(value);
-
-        if (q != null) {
-            Element qRange = createRangeElement(cr, q, "Q");
-            item.appendChild(qRange);
-        }
-
-        if (w != null) {
-            Element wRange = createRangeElement(cr, w, "W");
-            item.appendChild(wRange);
-        }
-
-        return item;
-    }
-
-
-    protected Element createRangeElement(
-        XMLUtils.ElementCreator cr,
-        double[] mm,
-        String   type)
-    {
-        Element range = ProtocolUtils.createArtNode(
-            cr, "range",
-            new String[] {"type"},
-            new String[] {type});
-
-        Element min = ProtocolUtils.createArtNode(cr, "min", null, null);
-        min.setTextContent(String.valueOf(mm[0]));
-
-        Element max = ProtocolUtils.createArtNode(cr, "max", null, null);
-        max.setTextContent(String.valueOf(mm[1]));
-
-        range.appendChild(min);
-        range.appendChild(max);
-
-        return range;
-    }
-
-
-    /**
-     * Determines the min and max Q value for the given gauge. If no min and
-     * max values could be determined, this method will return
-     * [Double.MIN_VALUE, Double.MAX_VALUE].
-     *
-     * @param gauge
-     * @param wst
-     *
-     * @return the min and max Q values for the given gauge.
-     */
-    protected double[] determineMinMaxQ(Gauge gauge, Wst wst) {
-        logger.debug("WQAdapted.determineMinMaxQ");
-
-        double[] minmaxQ = gauge != null
-            ? wst.determineMinMaxQ(gauge.getRange())
-            : null;
-
-        double minQ = minmaxQ != null ? minmaxQ[0] : Double.MIN_VALUE;
-        double maxQ = minmaxQ != null ? minmaxQ[1] : Double.MAX_VALUE;
-
-        return new double[] { minQ, maxQ };
-    }
-
-
-    /** Indicate client which input elements to use. */
-    @Override
-    protected String getUIProvider() {
-        return "wq_panel_adapted";
-    }
-
-
-    @Override
-    public boolean validate(Artifact artifact)
-    throws IllegalArgumentException
-    {
-        logger.debug("WQAdapted.validate");
-
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-        StateData    data = getData(flys, FIELD_WQ_MODE);
-
-        String mode = data != null ? (String) data.getValue() : null;
-        boolean isQ = mode != null
-            ? Boolean.valueOf(mode)
-            : false;
-
-        if (!isQ) {
-            return validateW(artifact);
-        }
-        else if (isQ) {
-            return validateQ(artifact);
-        }
-        else {
-            throw new IllegalArgumentException("error_feed_no_wq_mode_selected");
-        }
-    }
-
-
-    protected boolean validateW(Artifact artifact)
-    throws IllegalArgumentException
-    {
-        logger.debug("WQAdapted.validateW");
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-
-        RangeWithValues[] rwvs = extractInput(getData(flys, "wq_values"));
-
-        if (rwvs == null) {
-            throw new IllegalArgumentException("error_missing_wq_data");
-        }
-
-        List<Gauge> gauges = FLYSUtils.getGauges((FLYSArtifact) artifact);
-
-        for (Gauge gauge: gauges) {
-            Range range  = gauge.getRange();
-            double lower = range.getA().doubleValue();
-            double upper = range.getB().doubleValue();
-
-            for (RangeWithValues rwv: rwvs) {
-                if (lower <= rwv.getStart() && upper >= rwv.getEnd()) {
-                    compareWsWithGauge(gauge, rwv.getValues());
-                }
-            }
-        }
-
-        return true;
-    }
-
-
-    protected boolean validateQ(Artifact artifact)
-    throws IllegalArgumentException
-    {
-        logger.debug("WQAdapted.validateQ");
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-
-        RangeWithValues[] rwvs = extractInput(getData(flys, "wq_values"));
-
-        if (rwvs == null) {
-            throw new IllegalArgumentException("error_missing_wq_data");
-        }
-
-        List<Gauge> gauges = FLYSUtils.getGauges(flys);
-        River        river = FLYSUtils.getRiver(flys);
-        Wst            wst = WstFactory.getWst(river);
-
-        for (Gauge gauge: gauges) {
-            Range range  = gauge.getRange();
-            double lower = range.getA().doubleValue();
-            double upper = range.getB().doubleValue();
-
-            for (RangeWithValues rwv: rwvs) {
-                if (lower <= rwv.getStart() && upper >= rwv.getEnd()) {
-                    compareQsWithGauge(wst, gauge, rwv.getValues());
-                }
-            }
-        }
-
-        return true;
-    }
-
-
-    protected boolean compareQsWithGauge(Wst wst, Gauge gauge, double[] qs)
-    throws IllegalArgumentException
-    {
-        double[] minmax = gauge != null
-            ? wst.determineMinMaxQ(gauge.getRange())
-            : null;
-
-        if (minmax == null) {
-            logger.warn("Could not determine min/max Q of gauge.");
-            return true;
-        }
-
-        if (logger.isDebugEnabled()) {
-            logger.debug("Validate Qs with:");
-            logger.debug("-- Gauge: " + gauge.getName());
-            logger.debug("-- Gauge min: " + minmax[0]);
-            logger.debug("-- Gauge max: " + minmax[1]);
-        }
-
-        for (double q: qs) {
-            if (q < minmax[0] || q > minmax[1]) {
-                throw new IllegalArgumentException(
-                    "error_feed_q_values_invalid");
-            }
-        }
-
-        return true;
-    }
-
-
-    protected boolean compareWsWithGauge(Gauge gauge, double[] ws)
-    throws IllegalArgumentException
-    {
-        double[] minmax = gauge != null
-            ? gauge.determineMinMaxW()
-            : null;
-
-        if (minmax == null) {
-            logger.warn("Could not determine min/max W of gauge.");
-            return true;
-        }
-
-        if (logger.isDebugEnabled()) {
-            logger.debug("Validate Ws with:");
-            logger.debug("-- Gauge: " + gauge.getName());
-            logger.debug("-- Gauge min: " + minmax[0]);
-            logger.debug("-- Gauge max: " + minmax[1]);
-        }
-
-        for (double w: ws) {
-            if (w < minmax[0] || w > minmax[1]) {
-                throw new IllegalArgumentException(
-                    "error_feed_w_values_invalid");
-            }
-        }
-
-        return true;
-    }
-
-
-    protected RangeWithValues[] extractInput(StateData data) {
-        if (data == null) {
-            return null;
-        }
-
-        String dataString = (String) data.getValue();
-        String[]   ranges = dataString.split(":");
-
-        List<RangeWithValues> rwv = new ArrayList<RangeWithValues>();
-
-        for (String range: ranges) {
-            String[] parts = range.split(";");
-
-            double lower = Double.parseDouble(parts[0]);
-            double upper = Double.parseDouble(parts[1]);
-
-            String[] values = parts[3].split(",");
-
-            int      num = values.length;
-            double[] res = new double[num];
-
-            for (int i = 0; i < num; i++) {
-                try {
-                    res[i] = Double.parseDouble(values[i]);
-                }
-                catch (NumberFormatException nfe) {
-                    logger.warn(nfe, nfe);
-                }
-            }
-
-            rwv.add(new RangeWithValues(lower, upper, res));
-        }
-
-        return rwv.toArray(new RangeWithValues[rwv.size()]);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/WQSelect.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,767 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import java.text.NumberFormat;
-
-import gnu.trove.TDoubleArrayList;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Element;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.artifacts.common.utils.XMLUtils;
-import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
-
-import de.intevation.artifactdatabase.ProtocolUtils;
-import de.intevation.artifactdatabase.data.StateData;
-
-import de.intevation.flys.model.Gauge;
-import de.intevation.flys.model.River;
-import de.intevation.flys.model.Wst;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.WINFOArtifact;
-
-import de.intevation.flys.artifacts.model.WstFactory;
-import de.intevation.flys.artifacts.model.WstValueTable;
-import de.intevation.flys.artifacts.resources.Resources;
-
-import de.intevation.flys.utils.FLYSUtils;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class WQSelect extends DefaultState {
-
-    /** The logger used in this class. */
-    private static Logger logger = Logger.getLogger(WQSelect.class);
-
-    /** The default step width for Qs. */
-    public static final String DEFAULT_STEP_Q = "50";
-
-    /** The default step width for Qs. */
-    public static final String DEFAULT_STEP_W = "30";
-
-    /** The max number of steps for Qs and Ws. */
-    public static final int MAX_STEPS = 30;
-
-    /** The name of the 'mode' field. */
-    public static final String WQ_MODE = "wq_isq";
-
-    /** Them name fo the 'free' field. */
-    public static final String WQ_FREE = "wq_isfree";
-
-    /** The name of the 'selection' field. */
-    public static final String WQ_SELECTION = "wq_isrange";
-
-    /** The name of the 'from' field. */
-    public static final String WQ_FROM = "wq_from";
-
-    /** The name of the 'to' field. */
-    public static final String WQ_TO = "wq_to";
-
-    /** The name of the 'step' field. */
-    public static final String WQ_STEP = "wq_step";
-
-    /** The name of the 'single' field. */
-    public static final String WQ_SINGLE = "wq_single";
-
-
-    /**
-     * The default constructor that initializes an empty State object.
-     */
-    public WQSelect() {
-    }
-
-
-    @Override
-    protected Element createStaticData(
-        FLYSArtifact   flys,
-        ElementCreator creator,
-        CallContext    cc,
-        String         name,
-        String         value,
-        String         type
-    ) {
-        if (!name.equals(WQ_SINGLE)) {
-            return super.createStaticData(flys, creator, cc, name, value, type);
-        }
-
-        Boolean isQ = flys.getDataAsBoolean(WQ_MODE);
-        Boolean isFree = flys.getDataAsBoolean(WQ_FREE);
-
-        WINFOArtifact winfo = (WINFOArtifact) flys;
-
-        Element dataElement = creator.create("data");
-        creator.addAttr(dataElement, "name", name, true);
-        creator.addAttr(dataElement, "type", type, true);
-
-        Element itemElement = creator.create("item");
-        creator.addAttr(itemElement, "value", value, true);
-
-        String label;
-
-        if (!isQ || isFree) {
-            label = getLabel(winfo, cc, value);
-        }
-        else {
-            label = getSpecialLabel(winfo, cc, value);
-        }
-
-        creator.addAttr(itemElement, "label", label, true);
-
-        dataElement.appendChild(itemElement);
-
-        return dataElement;
-    }
-
-
-    protected static String getLabel(
-        WINFOArtifact winfo,
-        CallContext   cc,
-        String        raw
-    ) {
-        String[] values = raw.split(" ");
-
-        if (values.length < 1) {
-            return null;
-        }
-
-        StringBuilder label = new StringBuilder();
-
-        NumberFormat nf = NumberFormat.getInstance(
-            Resources.getLocale(cc.getMeta()));
-
-        for (String value: values) {
-            try {
-                double v = Double.parseDouble(value.trim());
-
-                String formatted = nf.format(v);
-
-                if (label.length() > 0) {
-                    label.append(';');
-                }
-                label.append(formatted);
-            }
-            catch (NumberFormatException nfe) {
-                // do nothing here
-            }
-        }
-
-        return label.toString();
-    }
-
-
-    protected static String getSpecialLabel(
-        WINFOArtifact winfo,
-        CallContext   cc,
-        String        raw
-    ) {
-        String[] values = raw.split(" ");
-
-        if (values.length < 1) {
-            return null;
-        }
-
-        NumberFormat nf = NumberFormat.getInstance(
-            Resources.getLocale(cc.getMeta()));
-
-        Gauge gauge = winfo.getGauge();
-
-        boolean debug = logger.isDebugEnabled();
-
-        StringBuilder label = new StringBuilder();
-
-        for (String value: values) {
-            try {
-                double v = Double.parseDouble(value.trim());
-
-                String tmp = nf.format(v);
-                String mv  = FLYSUtils.getNamedMainValue(gauge, v);
-
-                if (mv != null && mv.length() > 0) {
-                    tmp = mv + ": " + tmp;
-                    if (debug) {
-                        logger.debug("Add main value: '" + mv + "'");
-                    }
-                }
-                if (label.length() > 0) {
-                    label.append(';');
-                }
-                label.append(tmp);
-            }
-            catch (NumberFormatException nfe) {
-                // do nothing here
-            }
-        }
-
-        return label.toString();
-    }
-
-
-    @Override
-    protected Element createData(
-        XMLUtils.ElementCreator cr,
-        Artifact    artifact,
-        StateData   data,
-        CallContext context)
-    {
-        Element select = ProtocolUtils.createArtNode(
-            cr, "select", null, null);
-
-        cr.addAttr(select, "name", data.getName(), true);
-
-        Element label = ProtocolUtils.createArtNode(
-            cr, "label", null, null);
-
-        // XXX: DEAD CODE
-        /*
-        Element choices = ProtocolUtils.createArtNode(
-            cr, "choices", null, null);
-        */
-
-        label.setTextContent(Resources.getMsg(
-            context.getMeta(),
-            data.getName(),
-            data.getName()));
-
-        select.appendChild(label);
-
-        return select;
-    }
-
-
-    @Override
-    protected Element[] createItems(
-        XMLUtils.ElementCreator cr,
-        Artifact    artifact,
-        String      name,
-        CallContext context)
-    {
-        double[] minmaxW     = determineMinMaxW(artifact);
-        double[] minmaxWFree = determineMinMaxWFree(artifact);
-        double[] minmaxQ     = determineMinMaxQAtGauge(artifact);
-        double[] minmaxQFree = determineMinMaxQ(artifact);
-
-        if (name.equals("wq_from")) {
-            Element minW = createItem(cr, new String[] {
-                "minW",
-                String.valueOf(minmaxW[0])});
-
-            Element minQ = createItem(cr, new String[] {
-                "minQ",
-                String.valueOf(minmaxQ[0])});
-
-            Element minQFree = createItem(cr, new String[] {
-                "minQFree",
-                String.valueOf(minmaxQFree[0])});
-
-            Element minWFree = createItem(cr, new String[] {
-                "minWFree",
-                String.valueOf(minmaxWFree[0])});
-
-            return new Element[] { minW, minQ, minQFree, minWFree };
-        }
-        else if (name.equals("wq_to")) {
-            Element maxW = createItem(cr, new String[] {
-                "maxW",
-                String.valueOf(minmaxW[1])});
-
-            Element maxQ = createItem(cr, new String[] {
-                "maxQ",
-                String.valueOf(minmaxQ[1])});
-
-            Element maxQFree = createItem(cr, new String[] {
-                "maxQFree",
-                String.valueOf(minmaxQFree[1])});
-
-            Element maxWFree = createItem(cr, new String[] {
-                "maxWFree",
-                String.valueOf(minmaxWFree[1])});
-
-            return new Element[] { maxW, maxQ, maxQFree, maxWFree };
-        }
-        else {
-            Element stepW = createItem(
-                cr, new String[] {
-                    "stepW",
-                    String.valueOf(getStepsW(minmaxW[0], minmaxW[1]))});
-            Element stepQ = createItem(
-                cr, new String[] {
-                    "stepQ",
-                    String.valueOf(getStepsQ(minmaxQ[0], minmaxQ[1]))});
-            Element stepQFree = createItem(
-                cr, new String[] {
-                    "stepQFree",
-                    String.valueOf(getStepsQ(minmaxQFree[0], minmaxQFree[1]))});
-            Element stepWFree = createItem(
-                cr, new String[] {
-                    "stepWFree",
-                    String.valueOf(getStepsW(minmaxWFree[0], minmaxWFree[1]))});
-
-            return new Element[] { stepW, stepQ, stepQFree, stepWFree };
-        }
-    }
-
-
-    protected static double getStepsW(double min, double max) {
-        double diff = min < max ? max - min : min - max;
-        double step = diff / MAX_STEPS;
-
-        if (step < 10) {
-            return getSteps(step, 1);
-        }
-        else if (step < 100) {
-            return getSteps(step, 10);
-        }
-        else if (step < 1000) {
-            return getSteps(step, 100);
-        }
-        else {
-            return step;
-        }
-    }
-
-
-    protected static double getStepsQ(double min, double max) {
-        double diff = min < max ? max - min : min - max;
-        double step = diff / MAX_STEPS;
-
-        if (step < 10) {
-            return getSteps(step, 1);
-        }
-        else if (step < 100) {
-            return getSteps(step, 10);
-        }
-        else if (step < 1000) {
-            return getSteps(step, 100);
-        }
-        else {
-            return step;
-        }
-    }
-
-
-    protected static double getSteps(double steps, double factor) {
-        int    fac  = (int) (steps / factor);
-        double diff = steps - fac * factor;
-
-        if (diff == 0) {
-            return steps;
-        }
-
-        return factor * (fac + 1);
-    }
-
-
-    protected Element createItem(XMLUtils.ElementCreator cr, Object obj) {
-        Element item  = ProtocolUtils.createArtNode(cr, "item", null, null);
-        Element label = ProtocolUtils.createArtNode(cr, "label", null, null);
-        Element value = ProtocolUtils.createArtNode(cr, "value", null, null);
-
-        String[] arr = (String[]) obj;
-
-        label.setTextContent(arr[0]);
-        value.setTextContent(arr[1]);
-
-        item.appendChild(label);
-        item.appendChild(value);
-
-        return item;
-    }
-
-
-    @Override
-    protected String getUIProvider() {
-        return "wq_panel";
-    }
-
-
-    /**
-     * Determines the min and max W value for the current gauge. If no min and
-     * max values could be determined, this method will return
-     * [Double.MIN_VALUE, Double.MAX_VALUE].
-     *
-     * @param artifact The FLYSArtifact.
-     *
-     * @return the min and max W values for the current gauge.
-     */
-    protected double[] determineMinMaxW(Artifact artifact) {
-        logger.debug("WQSelect.determineCurrentGauge");
-
-        Gauge    gauge   = ((WINFOArtifact) artifact).getGauge();
-        double[] minmaxW = gauge != null ? gauge.determineMinMaxW() : null;
-
-        double minW = minmaxW != null ? minmaxW[0] : Double.MIN_VALUE;
-        double maxW = minmaxW != null ? minmaxW[1] : Double.MAX_VALUE;
-
-        return new double[] { minW, maxW };
-    }
-
-
-    /**
-     * Determines the min and max W value. If no min and
-     * max values could be determined, this method will return
-     * [Double.MIN_VALUE, Double.MAX_VALUE].
-     *
-     * @param artifact The FLYSArtifact.
-     *
-     * @return the min and max W values.
-     */
-    protected double[] determineMinMaxWFree(Artifact artifact) {
-        logger.debug("WQSelect.determineMinMaxWFree");
-
-        WINFOArtifact winfo = (WINFOArtifact) artifact;
-        WstValueTable valueTable = winfo.getWstValueTable();
-
-        double[] minmaxW = null;
-        if(valueTable != null) {
-            double[] km = null;
-            if(winfo.isRange()) {
-                km = winfo.getFromToStep();
-                // Use the start km to determine the min max values.
-                minmaxW = valueTable.getMinMaxW(km[0]);
-            }
-            else {
-                km = winfo.getKms();
-                minmaxW = valueTable.getMinMaxW(km[0]);
-            }
-        }
-        return minmaxW != null
-            ? minmaxW
-            : new double[] { Double.MIN_VALUE, Double.MAX_VALUE };
-    }
-
-
-    /**
-     * Determines the min and max Q value for the current gauge. If no min and
-     * max values could be determined, this method will return
-     * [Double.MIN_VALUE, Double.MAX_VALUE].
-     *
-     * @param artifact The FLYSArtifact.
-     *
-     * @return the min and max Q values for the current gauge.
-     */
-    protected double[] determineMinMaxQAtGauge(Artifact artifact) {
-        logger.debug("WQSelect.determineMinMaxQAtGauge");
-
-        WINFOArtifact flysArtifact = (WINFOArtifact) artifact;
-
-        River river = FLYSUtils.getRiver(flysArtifact);
-        Gauge gauge = flysArtifact.getGauge();
-        Wst   wst   = WstFactory.getWst(river);
-
-        double[] minmaxQ = gauge != null
-            ? wst.determineMinMaxQ(gauge.getRange())
-            : null;
-
-        double minQ = minmaxQ != null ? minmaxQ[0] : Double.MIN_VALUE;
-        double maxQ = minmaxQ != null ? minmaxQ[1] : Double.MAX_VALUE;
-
-        return new double[] { minQ, maxQ };
-    }
-
-
-    /**
-     * Determines the min and max Q value for the current kilometer range. If no
-     * min and max values could be determined, this method will return
-     *
-     * @param artifact The FLYSArtifact.
-     *
-     * @return the min and max Q values for the current kilometer range.
-     */
-    protected double[] determineMinMaxQ(Artifact artifact) {
-        logger.debug("WQSelect.determineMinMaxQ");
-
-        WINFOArtifact winfo = (WINFOArtifact) artifact;
-        WstValueTable valueTable = winfo.getWstValueTable();
-
-        double[] minmaxQ = null;
-        if(valueTable != null) {
-            double[] km = null;
-            if(winfo.isRange()) {
-                km = winfo.getFromToStep();
-                minmaxQ = valueTable.getMinMaxQ(km[0], km[1], km[2]);
-            }
-            else {
-                km = winfo.getKms();
-                minmaxQ = valueTable.getMinMaxQ(km[0]);
-                for (int i = 1; i < km.length; i++) {
-                    double[] tmp = valueTable.getMinMaxQ(km[i]);
-                    if(tmp[0] < minmaxQ[0]) {
-                        minmaxQ[0] = tmp[0];
-                    }
-                    if(tmp[1] > minmaxQ[1]) {
-                        minmaxQ[1] = tmp[1];
-                    }
-                }
-            }
-        }
-        return minmaxQ != null
-            ? minmaxQ
-            : new double[] { Double.MIN_VALUE, Double.MAX_VALUE };
-    }
-
-
-    @Override
-    public boolean validate(Artifact artifact)
-    throws IllegalArgumentException
-    {
-        logger.debug("WQSelect.validate");
-
-        WINFOArtifact flys = (WINFOArtifact) artifact;
-
-        StateData data       = getData(flys, WQ_SELECTION);
-        boolean isRange = data != null
-            ? Boolean.valueOf((String) data.getValue())
-            : false;
-
-
-
-        if (!isRange) {
-            return validateSingle(artifact);
-        }
-        else {
-            return validateRange(artifact);
-        }
-    }
-
-
-    protected boolean validateBounds(
-        double fromValid, double toValid,
-        double from,      double to,      double step)
-    throws IllegalArgumentException
-    {
-        logger.debug("RangeState.validateRange");
-
-        if (from < fromValid) {
-            logger.error(
-                "Invalid 'from'. " + from + " is smaller than " + fromValid);
-            throw new IllegalArgumentException("error_feed_from_out_of_range");
-        }
-        else if (to > toValid) {
-            logger.error(
-                "Invalid 'to'. " + to + " is bigger than " + toValid);
-            throw new IllegalArgumentException("error_feed_to_out_of_range");
-        }
-
-        return true;
-    }
-
-
-    protected boolean validateSingle(Artifact artifact)
-    throws    IllegalArgumentException
-    {
-        logger.debug("WQSelect.validateSingle");
-
-        WINFOArtifact flys = (WINFOArtifact) artifact;
-        StateData    data = getData(flys, WQ_SINGLE);
-
-        String tmp = data != null ? (String) data.getValue() : null;
-
-        if (tmp == null || tmp.length() == 0) {
-            throw new IllegalArgumentException("error_empty_state");
-        }
-
-        String[] strValues = tmp.split(" ");
-        TDoubleArrayList all = new TDoubleArrayList();
-
-        for (String strValue: strValues) {
-            try {
-                all.add(Double.parseDouble(strValue));
-            }
-            catch (NumberFormatException nfe) {
-                logger.warn(nfe, nfe);
-            }
-        }
-
-        all.sort();
-
-        FLYSUtils.WQ_MODE mode = FLYSUtils.getWQMode(flys);
-
-        logger.debug("WQ Mode: " + mode);
-
-        double[] minmax = null;
-
-        if (mode == FLYSUtils.WQ_MODE.WGAUGE) {
-            minmax = determineMinMaxW(artifact);
-        }
-        else if (mode == FLYSUtils.WQ_MODE.QGAUGE) {
-            minmax = determineMinMaxQAtGauge(artifact);
-        }
-        else if (mode == FLYSUtils.WQ_MODE.QFREE) {
-            minmax = determineMinMaxQ(artifact);
-        }
-        else {
-            minmax = determineMinMaxWFree(artifact);
-        }
-
-        double min = all.get(0);
-        double max = all.get(all.size()-1);
-
-        logger.debug("Inserted min value = " + min);
-        logger.debug("Inserted max value = " + max);
-
-        return validateBounds(minmax[0], minmax[1], min, max, 0d);
-    }
-
-
-    protected boolean validateRange(Artifact artifact)
-    throws    IllegalArgumentException
-    {
-        logger.debug("WQSelect.validateRange");
-
-        WINFOArtifact     flys = (WINFOArtifact) artifact;
-        FLYSUtils.WQ_MODE mode = FLYSUtils.getWQMode(flys);
-
-        if (mode == null) {
-            throw new IllegalArgumentException("error_feed_invalid_wq_mode");
-        }
-
-        StateData dFrom = flys.getData(WQ_FROM);
-        StateData dTo   = flys.getData(WQ_TO);
-        StateData dStep = flys.getData(WQ_STEP);
-
-        String fromStr = dFrom != null ? (String) dFrom.getValue() : null;
-        String toStr   = dTo != null ? (String) dTo.getValue() : null;
-        String stepStr = dStep != null ? (String) dStep.getValue() : null;
-
-        if (fromStr == null || toStr == null || stepStr == null) {
-            throw new IllegalArgumentException("error_empty_state");
-        }
-
-        try {
-            double from = Double.parseDouble(fromStr);
-            double to   = Double.parseDouble(toStr);
-            double step = Double.parseDouble(stepStr);
-
-            if (mode == FLYSUtils.WQ_MODE.WGAUGE) {
-                return validateGaugeW(artifact, from, to, step);
-            }
-            else if (mode == FLYSUtils.WQ_MODE.QGAUGE) {
-                return validateGaugeQ(artifact, from, to, step);
-            }
-            else if (mode == FLYSUtils.WQ_MODE.QFREE) {
-                return validateFreeQ(artifact, from, to, step);
-            }
-            else if (mode == FLYSUtils.WQ_MODE.WFREE) {
-                return validateFreeW(artifact, from, to, step);
-            }
-            else {
-                throw new IllegalArgumentException(
-                    "error_feed_invalid_wq_mode");
-            }
-        }
-        catch (NumberFormatException nfe) {
-            throw new IllegalArgumentException("error_feed_number_format");
-        }
-    }
-
-
-    /**
-     * Validates the inserted W values.
-     *
-     * @param artifact The owner artifact.
-     * @param from The lower value of the W range.
-     * @param to The upper value of the W range.
-     * @param step The step width.
-     *
-     * @return true, if everything was fine, otherwise an exception is thrown.
-     */
-    protected boolean validateGaugeW(
-        Artifact    artifact,
-        double from,
-        double to,
-        double step)
-    throws    IllegalArgumentException
-    {
-        logger.debug("WQSelect.validateGaugeW");
-
-        double[] minmaxW = determineMinMaxW(artifact);
-
-        return validateBounds(minmaxW[0], minmaxW[1], from, to, step);
-    }
-
-
-    /**
-     * Validates the inserted Q values based on the Q range for the current
-     * gauge.
-     *
-     * @param artifact The owner artifact.
-     * @param from The lower value of the Q range.
-     * @param to The upper value of the Q range.
-     * @param step The step width.
-     *
-     * @return true, if everything was fine, otherwise an exception is thrown.
-     */
-    protected boolean validateGaugeQ(
-        Artifact artifact,
-        double   from,
-        double   to,
-        double   step)
-    throws IllegalArgumentException
-    {
-        logger.debug("WQSelect.validateGaugeQ");
-
-        double[] minmaxQ = determineMinMaxQAtGauge(artifact);
-
-        return validateBounds(minmaxQ[0], minmaxQ[1], from, to, step);
-    }
-
-
-    /**
-     * Validates the inserted Q values based on the Q range for the current
-     * kilometer range.
-     *
-     * @param artifact The owner artifact.
-     * @param from The lower value of the Q range.
-     * @param to The upper value of the Q range.
-     * @param step The step width.
-     *
-     * @return true, if everything was fine, otherwise an exception is thrown.
-     */
-    protected boolean validateFreeQ(
-        Artifact artifact,
-        double   from,
-        double   to,
-        double   step)
-    throws IllegalArgumentException
-    {
-        logger.debug("WQSelect.validateFreeQ");
-
-        double[] minmaxQ = determineMinMaxQ(artifact);
-
-        return validateBounds(minmaxQ[0], minmaxQ[1], from, to, step);
-    }
-
-
-    /**
-     * Validates the inserted W values based on the W range for the current
-     * kilometer range.
-     *
-     * @param artifact The owner artifact.
-     * @param from The lower value of the W range.
-     * @param to The upper value of the W range.
-     * @param step The step width.
-     *
-     * @return true, if everything was fine, otherwise an exception is thrown.
-     */
-    protected boolean validateFreeW(
-        Artifact artifact,
-        double   from,
-        double   to,
-        double   step)
-    throws IllegalArgumentException
-    {
-        logger.debug("WQSelect.validateFreeW");
-
-        double[] minmaxW = determineMinMaxWFree(artifact);
-
-        return validateBounds(minmaxW[0], minmaxW[1], from, to, step);
-    }
-
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/WaterlevelGroundDifferences.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,157 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import org.w3c.dom.Element;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.artifacts.common.utils.XMLUtils;
-
-import de.intevation.artifactdatabase.data.StateData;
-import de.intevation.artifactdatabase.ProtocolUtils;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class WaterlevelGroundDifferences extends RangeState {
-
-    public static final String LOWER_FIELD  = "diff_from";
-    public static final String UPPER_FIELD  = "diff_to";
-    public static final String DIFF_FIELD   = "diff_diff";
-
-    public static final double DEFAULT_STEP = 0d;
-
-
-    private static Logger logger =
-        Logger.getLogger(WaterlevelGroundDifferences.class);
-
-
-    protected String getLowerField() {
-        return LOWER_FIELD;
-    }
-
-
-    protected String getUpperField() {
-        return UPPER_FIELD;
-    }
-
-
-    protected String getStepField() {
-        return DIFF_FIELD;
-    }
-
-
-    @Override
-    protected double[] getMinMax(Artifact artifact) {
-        return new double[] { -Double.MAX_VALUE, Double.MAX_VALUE };
-    }
-
-
-    @Override
-    protected String getUIProvider() {
-        return "waterlevel_ground_panel";
-    }
-
-
-    protected double getDefaultStep() {
-        return DEFAULT_STEP;
-    }
-
-
-    @Override
-    protected Element[] createItems(
-        XMLUtils.ElementCreator cr,
-        Artifact    artifact,
-        String      name,
-        CallContext context)
-    {
-        double[] minmax = getMinMax(artifact);
-
-        double minVal = Double.MIN_VALUE;
-        double maxVal = Double.MAX_VALUE;
-
-        if (minmax != null) {
-            minVal = minmax[0];
-            maxVal = minmax[1];
-        }
-        else {
-            logger.warn("Could not read min/max distance values!");
-        }
-
-        if (name.equals(LOWER_FIELD)) {
-            Element min = createItem(
-                cr,
-                new String[] {"min", new Double(minVal).toString()});
-
-            return new Element[] { min };
-        }
-        else if (name.equals(UPPER_FIELD)) {
-            Element max = createItem(
-                cr,
-                new String[] {"max", new Double(maxVal).toString()});
-
-            return new Element[] { max };
-        }
-        else {
-            Element step = createItem(
-                cr,
-                new String[] {"step", String.valueOf(getDefaultStep())});
-            return new Element[] { step };
-        }
-    }
-
-
-    protected Element createItem(XMLUtils.ElementCreator cr, Object obj) {
-        Element item  = ProtocolUtils.createArtNode(cr, "item", null, null);
-        Element label = ProtocolUtils.createArtNode(cr, "label", null, null);
-        Element value = ProtocolUtils.createArtNode(cr, "value", null, null);
-
-        String[] arr = (String[]) obj;
-
-        label.setTextContent(arr[0]);
-        value.setTextContent(arr[1]);
-
-        item.appendChild(label);
-        item.appendChild(value);
-
-        return item;
-    }
-
-    @Override
-    public boolean validate(Artifact artifact)
-    throws IllegalArgumentException
-    {
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-
-        StateData dFrom = getData(flys, getLowerField());
-        StateData dTo   = getData(flys, getUpperField());
-        StateData dStep = getData(flys, getStepField());
-
-        String fromStr = dFrom != null ? (String) dFrom.getValue() : null;
-        String toStr   = dTo   != null ? (String) dTo.getValue()   : null;
-        String stepStr = dStep != null ? (String) dStep.getValue() : null;
-
-        if (fromStr == null || toStr == null || stepStr == null) {
-            throw new IllegalArgumentException("error_empty_state");
-        }
-
-        try {
-            double from = Double.parseDouble(fromStr);
-            double to   = Double.parseDouble(toStr);
-            double step = Double.parseDouble(stepStr);
-
-            double[] minmax = getMinMax(flys);
-
-            return validateBounds(minmax[0], minmax[1], from, to, step);
-        }
-        catch (NumberFormatException nfe) {
-            throw new IllegalArgumentException("error_invalid_double_value");
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/WaterlevelInfoState.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,145 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.artifactdatabase.state.Facet;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.WINFOArtifact;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.ReportFacet;
-import de.intevation.flys.artifacts.model.WaterlevelFacet;
-import de.intevation.flys.artifacts.model.WQKms;
-
-import de.intevation.flys.artifacts.model.DataFacet;
-import de.intevation.flys.artifacts.model.CrossSectionWaterLineFacet;
-import de.intevation.flys.artifacts.model.CalculationResult;
-
-
-public class WaterlevelInfoState
-extends      DefaultState
-implements   FacetTypes
-{
-    /** The logger that is used in this state. */
-    private static Logger logger = Logger.getLogger(WaterlevelInfoState.class);
-
-
-    @Override
-    protected String getUIProvider() {
-        return "noinput";
-    }
-
-
-    @Override
-    public Object computeInit(
-        FLYSArtifact artifact,
-        String       hash,
-        Object       context,
-        CallMeta     meta,
-        List<Facet>  facets
-    ) {
-        return compute((WINFOArtifact) artifact, hash, facets, null);
-    }
-
-
-    protected Object compute(
-        WINFOArtifact winfo,
-        String        hash,
-        List<Facet>   facets,
-        Object        old
-    ) {
-        logger.debug("WaterlevelInfoState.compute");
-        String id = getID();
-
-        CalculationResult res = old instanceof CalculationResult
-            ? (CalculationResult)old
-            : winfo.getWaterlevelData();
-
-        if (facets == null) {
-            return res;
-        }
-
-        WQKms [] wqkms = (WQKms [])res.getData();
-
-        for (int i = 0; i < wqkms.length; i++) {
-            String nameW = null;
-            String nameQ = null;
-
-            if (winfo.isQ()) {
-                nameQ = wqkms[i].getName();
-                nameW = "W(" + nameQ + ")";
-            }
-            else {
-                nameW = wqkms[i].getName();
-                nameQ = "Q(" + nameQ + ")";
-            }
-
-            logger.debug("WaterlevelInfoState Create facet: " + nameW);
-            logger.debug("WaterlevelInfoState Create facet: " + nameQ);
-
-            Facet w = new WaterlevelFacet(
-                i, LONGITUDINAL_W, nameW, ComputeType.ADVANCE, id, hash);
-            Facet q = new WaterlevelFacet(
-                i, LONGITUDINAL_Q, nameQ, ComputeType.ADVANCE, id, hash);
-
-            facets.add(w);
-            facets.add(q);
-        }
-
-        if (wqkms.length > 0) {
-            Facet wst = new DataFacet(
-                WST, "WST data", ComputeType.ADVANCE, hash, id);
-            Facet csv = new DataFacet(
-                CSV, "CSV data", ComputeType.ADVANCE, hash, id);
-
-            facets.add(wst);
-            facets.add(csv);
-        }
-
-        if (res.getReport().hasProblems()) {
-            facets.add(new ReportFacet(ComputeType.ADVANCE, hash, id));
-        }
-
-        // TODO Adjust to WaterlevelState - implementation.
-        facets.add(new CrossSectionWaterLineFacet(0, "Q=" + winfo.getDataAsString("wq_single")));
-
-        // Assume to be in wq_single mode.
-        return res;
-    }
-
-
-    /**
-     * @param context Ignored.
-     */
-    @Override
-    public Object computeFeed(
-        FLYSArtifact artifact,
-        String       hash,
-        CallContext  context,
-        List<Facet>  facets,
-        Object       old
-    ) {
-        return compute((WINFOArtifact) artifact, hash, facets, old);
-    }
-
-
-    /**
-     * @param context Ignored.
-     */
-    @Override
-    public Object computeAdvance(
-        FLYSArtifact artifact,
-        String       hash,
-        CallContext  context,
-        List<Facet>  facets,
-        Object       old
-    ) {
-        return compute((WINFOArtifact) artifact, hash, facets, old);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/WaterlevelPairSelectState.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,162 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Element;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.artifactdatabase.ProtocolUtils;
-import de.intevation.artifactdatabase.state.Facet;
-
-import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.resources.Resources;
-
-import de.intevation.flys.utils.StringUtil;
-
-/**
- * State in which the user selects 1 to n pairs of Waterlevels and alikes.
- */
-public class WaterlevelPairSelectState
-extends      DefaultState
-implements   FacetTypes
-{
-    /** The logger that is used in this state. */
-    private static Logger logger = Logger.getLogger(
-         WaterlevelPairSelectState.class);
-
-
-    /** Trivial constructor. */
-    public WaterlevelPairSelectState() {
-    }
-
-
-    /** Specify to display a datacage_twin_panel. */
-    @Override
-    protected String getUIProvider() {
-        return "datacage_twin_panel";
-    }
-
-
-    /**
-     * Overridden to do nothing.
-     */
-    @Override
-    public Object computeAdvance(
-        FLYSArtifact artifact,
-        String       hash,
-        CallContext  context,
-        List<Facet>  facets,
-        Object       old
-    ) {
-        //Get data and do stuff, do not calculate
-        return "";
-    }
-
-
-    /**
-     * Create elements for document (prepopulated with data, if any).
-     * @param artifact FLYSArtifact to get data from.
-     * @param name DataName, expceted to be "diffids".
-     */
-    @Override
-    protected Element[] createItems(
-        ElementCreator cr,
-        Artifact    artifact,
-        String      name,
-        CallContext context)
-    {
-        logger.debug("createItems: " + name);
-        if (name.equals("diffids")) {
-            Element item  = ProtocolUtils.createArtNode(cr, "item", null, null);
-            Element label = ProtocolUtils.createArtNode(cr, "label", null, null);
-            Element value = ProtocolUtils.createArtNode(cr, "value", null, null);
-            FLYSArtifact flys = (FLYSArtifact) artifact;
-            String s = flys.getDataAsString("diffids");
-            value.setTextContent(s);
-            item.appendChild(label);
-            item.appendChild(value);
-            return new Element[] { item };
-        }
-        return new Element[] {};
-    }
-
-
-    /**
-     * Creats the data element used for the static part of DESCRIBE document.
-     */
-    @Override
-    protected Element createStaticData(
-        FLYSArtifact   flys,
-        ElementCreator creator,
-        CallContext    cc,
-        String         name,
-        String         value,
-        String         type
-    ) {
-        Element dataElement = creator.create("data");
-        creator.addAttr(dataElement, "name", name, true);
-        creator.addAttr(dataElement, "type", type, true);
-
-        Element itemElement = creator.create("item");
-        creator.addAttr(itemElement, "value", value, true);
-
-        String[] labels = getLabels(cc, value);
-        Object[] obj    = new Object[] { labels[0] };
-
-        // TODO own i18n
-        String attrValue = Resources.getMsg(
-            cc.getMeta(), "wsp.selected.string", "wsp.selected.string", obj);
-        //I18N_STATIC_KEY, I18N_STATIC_KEY, obj);
-
-        creator.addAttr(itemElement, "label", attrValue, true);
-        dataElement.appendChild(itemElement);
-
-        return dataElement;
-    }
-
-
-    /**
-     * Get name to display for selected watelerlevels (for example "Q=123")
-     * from the CalculationResult.
-     */
-    public static String[] getLabels(CallContext cc, String value) {
-        String[] recommendations = value.split("#");
-        String displayString = "";
-
-        // Walk over all selected recommendations and create label
-        // like "W (Q=1) - W (Q=2)".
-        for (int i = 0; i < recommendations.length; i+=2) {
-            String[] minuendParts = StringUtil
-                .unbracket(recommendations[i+0])
-                .split(";");
-            if(minuendParts.length >= 4) {
-                displayString += "(" + minuendParts[3];
-            }
-            else {
-                displayString += "([error]";
-            }
-
-            displayString += " - ";
-
-            String[] subtrahendParts = StringUtil
-                .unbracket(recommendations[i+1])
-                .split(";");
-            if(subtrahendParts.length >= 4) {
-                displayString += subtrahendParts[3] + ") ";
-            }
-            else {
-                displayString += "[error])";
-            }
-        }
-
-        return new String[] { displayString };
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/WaterlevelSelectState.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,192 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Element;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.artifactdatabase.data.DefaultStateData;
-import de.intevation.artifactdatabase.data.StateData;
-
-import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.WQKms;
-import de.intevation.flys.artifacts.model.extreme.ExtremeResult;
-import de.intevation.flys.artifacts.resources.Resources;
-import de.intevation.flys.utils.FLYSUtils;
-import de.intevation.flys.utils.StringUtil;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class WaterlevelSelectState extends DefaultState {
-
-    private static final Logger logger =
-        Logger.getLogger(WaterlevelSelectState.class);
-
-    public static final String SPLIT_CHAR = ";";
-
-    public static final String WINFO_WSP_STATE_ID = "state.winfo.waterlevel";
-
-    public static final String I18N_STATIC_KEY = "wsp.selected.string";
-
-
-    @Override
-    protected String getUIProvider() {
-        return "wsp_datacage_panel";
-    }
-
-
-    /**
-     * @param flys ignored
-     * @param cc   ignrored
-     */
-    @Override
-    public StateData transform(
-        FLYSArtifact flys,
-        CallContext  cc,
-        StateData    stateData,
-        String       name,
-        String       val
-    ) {
-        if (!isValueValid(val)) {
-            logger.error("The given input string is not valid: '" + val + "'");
-            return null;
-        }
-
-        return new DefaultStateData(name, null, null, StringUtil.unbracket(val));
-    }
-
-
-    @Override
-    public boolean validate(Artifact artifact)
-    throws IllegalArgumentException
-    {
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-
-        StateData data = flys.getData("wsp");
-
-        if (data == null) {
-            throw new IllegalArgumentException("WSP is empty");
-        }
-
-        return true;
-    }
-
-
-    @Override
-    protected Element createStaticData(
-        FLYSArtifact   flys,
-        ElementCreator creator,
-        CallContext    cc,
-        String         name,
-        String         value,
-        String         type
-    ) {
-        Element dataElement = creator.create("data");
-        creator.addAttr(dataElement, "name", name, true);
-        creator.addAttr(dataElement, "type", type, true);
-
-        Element itemElement = creator.create("item");
-        creator.addAttr(itemElement, "value", value, true);
-
-        String[] labels = getLabels(cc, value);
-        Object[] obj    = new Object[] { labels[0] };
-
-        String attrValue = Resources.getMsg(
-            cc.getMeta(), I18N_STATIC_KEY, I18N_STATIC_KEY, obj);
-
-        creator.addAttr(itemElement, "label", attrValue, true);
-        dataElement.appendChild(itemElement);
-
-        return dataElement;
-    }
-
-
-    /**
-     * Get name to display for selected watelerlevel (for example "Q=123")
-     * from the CalculationResult.
-     */
-    public static String[] getLabels(CallContext cc, String value) {
-        String[] parts = value.split(SPLIT_CHAR);
-
-        FLYSArtifact artifact = FLYSUtils.getArtifact(parts[0], cc);
-
-        CalculationResult rawData = (CalculationResult) artifact.compute(
-            cc,
-            null,
-            WINFO_WSP_STATE_ID,
-            ComputeType.ADVANCE,
-            false);
-
-        WQKms[] wqkms;
-
-        if (rawData.getData() instanceof ExtremeResult) {
-            wqkms = ((ExtremeResult) rawData.getData()).getWQKms();
-        }
-        else {
-            wqkms = (WQKms[]) rawData.getData();
-        }
-
-        int idx = -1;
-        try {
-            idx = Integer.parseInt(parts[2]);
-        }
-        catch (NumberFormatException nfe) { /* do nothing */ }
-
-        String name = wqkms[idx].getName();
-
-        return new String[] { StringUtil.wWrap(name) };
-    }
-
-
-    /**
-     * Validates the given String. A valid string for this state requires the
-     * format: "UUID;FACETNAME;FACETINDEX".
-     *
-     * @param value The string value requires validation.
-     *
-     * @return true, if the string applies the specified format, otherwise
-     * false.
-     */
-    public static boolean isValueValid(String value) {
-        logger.debug("Validate string: '" + value + "'");
-
-        value = StringUtil.unbracket(value);
-
-        logger.debug("Validate substring: '" + value + "'");
-
-        if (value == null || value.length() == 0) {
-            return false;
-        }
-
-        String[] parts = value.split(SPLIT_CHAR);
-
-        if (parts == null || parts.length < 3) {
-            return false;
-        }
-
-        if (parts[0] == null || parts[0].length() == 0) {
-            return false;
-        }
-
-        if (parts[1] == null || parts[1].length() == 0) {
-            return false;
-        }
-
-        try {
-            Integer.parseInt(parts[2]);
-        }
-        catch (NumberFormatException nfe) {
-            logger.error("Index is not a valid integer!", nfe);
-        }
-
-        return true;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,152 +0,0 @@
-package de.intevation.flys.artifacts.states;
-
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifacts.CallContext;
-import de.intevation.flys.artifacts.ChartArtifact;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.WINFOArtifact;
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.CrossSectionWaterLineFacet;
-import de.intevation.flys.artifacts.model.DataFacet;
-import de.intevation.flys.artifacts.model.EmptyFacet;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.ReportFacet;
-import de.intevation.flys.artifacts.model.WQKms;
-import de.intevation.flys.artifacts.model.WaterlevelFacet;
-import de.intevation.flys.utils.FLYSUtils;
-
-/** State in which a waterlevel has been calculated. */
-public class WaterlevelState
-extends      DefaultState
-implements   FacetTypes
-{
-    /** The logger that is used in this state. */
-    private static Logger logger = Logger.getLogger(WaterlevelState.class);
-
-
-    /**
-     * From this state can only be continued trivially.
-     */
-    @Override
-    protected String getUIProvider() {
-        return "continue";
-    }
-
-
-    /**
-     * Compute result or returned object from cache, create facets.
-     * @param old Object that was cached.
-     */
-    protected Object compute(
-        WINFOArtifact winfo,
-        CallContext   cc,
-        String        hash,
-        List<Facet>   facets,
-        Object        old
-    ) {
-        String id = getID();
-
-        CalculationResult res = old instanceof CalculationResult
-            ? (CalculationResult) old
-            : winfo.getWaterlevelData();
-
-        if (facets == null) {
-            return res;
-        }
-
-        boolean debug = logger.isDebugEnabled();
-
-        WQKms [] wqkms = (WQKms []) res.getData();
-
-        for (int i = 0; i < wqkms.length; i++) {
-            String name = wqkms[i].getName();
-
-            String nameW = FLYSUtils.createWspWTitle(winfo, cc, name);
-            String nameQ = FLYSUtils.createWspQTitle(winfo, cc, name);
-
-            // Hotfix for theme names. Themes with the same name cause problems
-            // aggregating chart legend items.
-            if (i > 0 && name.equals(wqkms[i - 1].getName())) {
-                nameW += "; Q=" + wqkms[i].get(0, new double[3])[1];
-                nameQ += " = " + wqkms[i].get(0, new double[3])[1];
-            }
-
-            if (debug) {
-                logger.debug("Create facet: " + nameW);
-                logger.debug("Create facet: " + nameQ);
-            }
-
-            Facet w = new WaterlevelFacet(
-                i, LONGITUDINAL_W, nameW, ComputeType.ADVANCE, id, hash);
-            Facet q = new WaterlevelFacet(
-                i, LONGITUDINAL_Q, nameQ, ComputeType.ADVANCE, id, hash);
-
-            facets.add(new CrossSectionWaterLineFacet(i, nameW));
-
-            facets.add(w);
-            facets.add(q);
-        }
-
-        if (wqkms.length > 0) {
-            Facet wst = new DataFacet(
-                WST, "WST data", ComputeType.ADVANCE, hash, id);
-            Facet csv = new DataFacet(
-                CSV, "CSV data", ComputeType.ADVANCE, hash, id);
-            Facet pdf = new DataFacet(
-                PDF, "PDF data", ComputeType.ADVANCE, hash, id);
-
-            facets.add(wst);
-            facets.add(csv);
-            facets.add(pdf);
-        }
-
-        if (res.getReport().hasProblems()) {
-            facets.add(new ReportFacet(ComputeType.ADVANCE, hash, id));
-        }
-
-        return res;
-    }
-
-
-    /**
-     * @param context Ignored.
-     */
-    @Override
-    public Object computeFeed(
-        FLYSArtifact artifact,
-        String       hash,
-        CallContext  context,
-        List<Facet>  facets,
-        Object       old
-    ) {
-        if (artifact instanceof ChartArtifact) {
-            facets.add(new EmptyFacet());
-            return null;
-        }
-        return compute((WINFOArtifact) artifact, context, hash, facets, old);
-    }
-
-
-    /**
-     * @param context Ignored.
-     */
-    @Override
-    public Object computeAdvance(
-        FLYSArtifact artifact,
-        String       hash,
-        CallContext  context,
-        List<Facet>  facets,
-        Object       old
-    ) {
-        if (artifact instanceof ChartArtifact) {
-            facets.add(new EmptyFacet());
-            return null;
-        }
-        return compute((WINFOArtifact) artifact, context, hash, facets, old);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/extreme/ExtremeCompute.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,116 +0,0 @@
-package de.intevation.flys.artifacts.states.extreme;
-
-import de.intevation.artifactdatabase.state.Facet;
-
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.flys.artifacts.access.ExtremeAccess;
-
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.CrossSectionWaterLineFacet;
-import de.intevation.flys.artifacts.model.DataFacet;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.ReportFacet;
-import de.intevation.flys.artifacts.model.WaterlevelFacet;
-import de.intevation.flys.artifacts.model.WQKms;
-
-import de.intevation.flys.artifacts.model.extreme.ExtremeCurveFacet;
-import de.intevation.flys.artifacts.model.extreme.ExtremeCalculation;
-import de.intevation.flys.artifacts.model.extreme.ExtremeResult;
-
-import de.intevation.flys.artifacts.resources.Resources;
-import de.intevation.flys.artifacts.states.DefaultState;
-
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-
-/** State in which to deliver extreme value analysis result. */
-public class ExtremeCompute
-extends      DefaultState
-implements   FacetTypes
-{
-    /** Private logger. */
-    private static Logger logger = Logger.getLogger(ExtremeCompute.class);
-
-
-    public ExtremeCompute() {
-    }
-
-
-    @Override
-    public Object computeAdvance(
-        FLYSArtifact artifact,
-        String       hash,
-        CallContext  context,
-        List<Facet>  facets,
-        Object       old
-    ) {
-        logger.debug("ExtremeCompute.computeAdvance");
-
-        CalculationResult res;
-
-        ExtremeAccess access = new ExtremeAccess(artifact, context);
-
-        if (old instanceof CalculationResult) {
-            res = (CalculationResult)old;
-        }
-        else {
-            ExtremeCalculation calc = new ExtremeCalculation(access);
-            res = calc.calculate();
-        }
-
-        if (facets == null) {
-            return res;
-        }
-
-        if (res.getReport().hasProblems()) {
-            facets.add(new ReportFacet());
-        }
-
-        ExtremeResult eres = (ExtremeResult) res.getData();
-        WQKms [] wqkms = (WQKms []) eres.getWQKms();
-
-        if (wqkms == null) {
-            logger.error("No computation result!");
-            return res;
-        }
-
-        for (int i = 0; i < wqkms.length; i++) {
-            String name = wqkms[i].getName();
-            // The name already contains "W(...)".
-            String qname = name.replace("W(","Q=");
-            qname = qname.substring(0,qname.length()-1);
-
-            Facet w = new WaterlevelFacet(
-                i, LONGITUDINAL_W, name, ComputeType.ADVANCE, id, hash);
-            Facet q = new WaterlevelFacet(
-                i, LONGITUDINAL_Q, qname, ComputeType.ADVANCE, id, hash);
-            Facet csFacet = new CrossSectionWaterLineFacet(i, name);
-
-            facets.add(w);
-            facets.add(q);
-            facets.add(csFacet);
-        }
-
-
-        facets.add(
-            new DataFacet(CSV, "CSV data", ComputeType.ADVANCE, hash, id));
-        facets.add(
-            new DataFacet(PDF, "PDF data", ComputeType.ADVANCE, hash, id));
-        facets.add(
-            new DataFacet(WST, "WST data", ComputeType.ADVANCE, hash, id));
-
-        facets.add(new ExtremeCurveFacet(Resources.getMsg(context.getMeta(),
-                                         "extreme_wq_curve", "extreme_wq_curve")));
-        facets.add(new ExtremeCurveFacet(Resources.getMsg(context.getMeta(),
-                                         "extreme_wq_base_curve", "extreme_wq_base_curve")
-                                         , true));
-
-        return res;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/extreme/ExtremeQInput.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,245 +0,0 @@
-package de.intevation.flys.artifacts.states.extreme;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Element;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.access.ExtremeAccess;
-
-import de.intevation.artifactdatabase.ProtocolUtils;
-import de.intevation.artifactdatabase.data.StateData;
-
-import de.intevation.artifacts.common.utils.XMLUtils;
-
-import de.intevation.flys.artifacts.model.RiverFactory;
-import de.intevation.flys.artifacts.model.WstValueTable;
-/*
-import de.intevation.flys.model.Gauge;
-import de.intevation.flys.model.Wst;
-import de.intevation.flys.utils.FLYSUtils;
-*/
-import de.intevation.flys.artifacts.model.Range;
-import de.intevation.flys.model.River;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.flys.artifacts.model.RangeWithValues;
-import de.intevation.flys.artifacts.states.DefaultState;
-import de.intevation.flys.artifacts.model.WstValueTableFactory;
-
-
-/** TODO Subclass WQAdapted. */
-
-/**
- * State to input Q data in segments for extreme value calculations..
- * The data item ranges is expected to have this format <from1>;<to1>;<value1>:<from2>;<to2>;<value2>:...
- * (;;;:;;;:;;;:...)
- */
-public class ExtremeQInput extends DefaultState {
-
-    /** The logger used in this state.*/
-    private static Logger logger = Logger.getLogger(ExtremeQInput.class);
-
-
-    /** Trivial, empty constructor. */
-    public ExtremeQInput() {
-    }
-
-
-    /**
-     * Create one element for each 'segment' of the selected river that
-     * is within the given kilometer range (TODO). Each element is a tuple of
-     * (from;to) where <i>from</i> is the lower bounds of the segment or the
-     * lower kilometer range. <i>to</i> is the upper bounds of the segment or
-     * the upper kilometer range.
-     *
-     * @param cr The ElementCreator.
-     * @param artifact The FLYS artifact.
-     * @param name The name of the data item.
-     * @param context The CallContext.
-     *
-     * @return a list of elements that consist of tuples of the intersected
-     *         segments of the selected river.
-     */
-    @Override
-    protected Element[] createItems(
-        XMLUtils.ElementCreator cr,
-        Artifact    artifact,
-        String      name,
-        CallContext context)
-    {
-        logger.debug("ExtremeQInput.createItems: " + name);
-
-        FLYSArtifact flysArtifact = (FLYSArtifact) artifact;
-
-        ExtremeAccess access = new ExtremeAccess(flysArtifact, context);
-        River river = RiverFactory.getRiver(access.getRiver());
-        WstValueTable wstValueTable = WstValueTableFactory.getTable(river);
-
-        List<Range> ranges   = wstValueTable.findSegments(access.getFrom(),
-            access.getTo());
-
-        int num = ranges != null ? ranges.size() : 0;
-
-        if (num == 0) {
-            logger.warn("Selected distance matches no segments.");
-            return null;
-        }
-
-        List<Element> elements = new ArrayList<Element>();
-
-        for (Range range: ranges) {
-            elements.add(createItem(
-                cr, new String[] { range.getStart() + ";" + range.getEnd(), ""}, new double[] {0,100000}));
-        }
-
-        Element[] els = new Element[elements.size()];
-
-        return elements.toArray(els);
-    }
-
-
-    /** Create sub-item ('row') of data thing. */
-    protected Element createItem(
-        XMLUtils.ElementCreator cr,
-        Object   obj,
-        double[] q
-        )
-    {
-        Element item  = ProtocolUtils.createArtNode(cr, "item", null, null);
-        Element label = ProtocolUtils.createArtNode(cr, "label", null, null);
-        Element value = ProtocolUtils.createArtNode(cr, "value", null, null);
-
-        String[] arr = (String[]) obj;
-
-        label.setTextContent(arr[0]);
-        value.setTextContent(arr[1]);
-
-        item.appendChild(label);
-        item.appendChild(value);
-
-        if (q != null) {
-            Element qRange = createRangeElement(cr, q, "Q");
-            item.appendChild(qRange);
-        }
-
-        return item;
-    }
-
-
-    /**
-     * Create elements to set min and max values of segments q (just min makes
-     * sense for extremes.
-     */
-    protected Element createRangeElement(
-        XMLUtils.ElementCreator cr,
-        double[] mm,
-        String   type)
-    {
-        Element range = ProtocolUtils.createArtNode(
-            cr, "range",
-            new String[] {"type"},
-            new String[] {type});
-
-        Element min = ProtocolUtils.createArtNode(cr, "min", null, null);
-        min.setTextContent(String.valueOf(mm[0]));
-
-        Element max = ProtocolUtils.createArtNode(cr, "max", null, null);
-        max.setTextContent(String.valueOf(mm[1]));
-
-        range.appendChild(min);
-        range.appendChild(max);
-
-        return range;
-    }
-
-
-    @Override
-    protected String getUIProvider() {
-        return "q_segmented_panel";
-    }
-
-
-    /** Validate given data (return true). */
-    @Override
-    public boolean validate(Artifact artifact)
-    throws IllegalArgumentException
-    {
-        logger.debug("ExtremeQInput.validate");
-
-        FLYSArtifact flys = (FLYSArtifact) artifact;
-        logger.debug("ExtremeQInput: " + getData(flys, "ranges"));
-
-        /*
-        // TODO sort out what has to be validated (prevent negative values?).
-        RangeWithValues[] rwvs = extractInput(getData(flys, "ranges"));
-
-        if (rwvs == null) {
-            throw new IllegalArgumentException("error_missing_wq_data");
-        }
-
-        List<Gauge> gauges = FLYSUtils.getGauges(flys);
-        River        river = FLYSUtils.getRiver(flys);
-        Wst            wst = WstFactory.getWst(river);
-
-        for (Gauge gauge: gauges) {
-            Range range  = gauge.getRange();
-            double lower = range.getA().doubleValue();
-            double upper = range.getB().doubleValue();
-
-            for (RangeWithValues rwv: rwvs) {
-                if (lower <= rwv.getStart() && upper >= rwv.getEnd()) {
-                    compareQsWithGauge(wst, gauge, rwv.getValues());
-                }
-            }
-        }
-        */
-
-        return true;
-    }
-
-
-    /** Form RangeWithValue-Array from state data. */
-    protected RangeWithValues[] extractInput(StateData data) {
-        if (data == null) {
-            return null;
-        }
-
-        String dataString = (String) data.getValue();
-        String[]   ranges = dataString.split(":");
-
-        List<RangeWithValues> rwv = new ArrayList<RangeWithValues>();
-
-        for (String range: ranges) {
-            String[] parts = range.split(";");
-
-            double lower = Double.parseDouble(parts[0]);
-            double upper = Double.parseDouble(parts[1]);
-
-            String[] values = parts[2].split(",");
-
-            int      num = values.length;
-            double[] res = new double[num];
-
-            for (int i = 0; i < num; i++) {
-                try {
-                    res[i] = Double.parseDouble(values[i]);
-                }
-                catch (NumberFormatException nfe) {
-                    logger.warn(nfe, nfe);
-                }
-            }
-
-            rwv.add(new RangeWithValues(lower, upper, res));
-        }
-
-        return rwv.toArray(new RangeWithValues[rwv.size()]);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/extreme/PercentInput.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,44 +0,0 @@
-package de.intevation.flys.artifacts.states.extreme;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifactdatabase.data.StateData;
-import de.intevation.artifacts.CallContext;
-import de.intevation.flys.artifacts.states.DefaultState;
-
-
-/**
- * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
- */
-public class PercentInput extends DefaultState {
-
-    /** The logger used in this class. */
-    private static Logger logger = Logger.getLogger(PercentInput.class);
-
-    public static final String PARAMETER_NAME = "percent";
-
-
-    /**
-     * The default constructor that initializes an empty State object.
-     */
-    public PercentInput () {
-    }
-
-
-    /** Tell UI what to display for input. */
-    @Override
-    protected String getUIProvider() {
-        return "percent_input";
-    }
-
-
-    @Override
-    protected String[] getDefaultsFor(CallContext context, StateData data) {
-        if (data != null && data.getName().equals(PARAMETER_NAME)) {
-            return new String[] {"5", "5"};
-        }
-
-        return null;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/fixation/AnalysisPeriods.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,28 +0,0 @@
-package de.intevation.flys.artifacts.states.fixation;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.flys.artifacts.states.DefaultState;
-
-/**
- * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
- */
-public class AnalysisPeriods extends DefaultState {
-
-    /** The logger used in this class. */
-    private static Logger logger = Logger.getLogger(AnalysisPeriods.class);
-
-
-    /**
-     * The default constructor that initializes an empty State object.
-     */
-    public AnalysisPeriods() {
-    }
-
-    @Override
-    protected String getUIProvider() {
-        return "fix.period_ana_panel";
-    }
-
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/fixation/EventSelect.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,95 +0,0 @@
-package de.intevation.flys.artifacts.states.fixation;
-
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Element;
-
-import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
-
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.states.DefaultState;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.utils.FLYSUtils;
-
-import de.intevation.flys.artifacts.model.FixingsOverview;
-import de.intevation.flys.artifacts.model.FixingsOverviewFactory;
-import de.intevation.flys.artifacts.model.FixingsOverview.IdFilter;
-import de.intevation.flys.artifacts.model.FixingsOverview.Fixing;
-
-
-/**
- * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
- */
-public class EventSelect extends DefaultState {
-
-    /** The logger used in this class. */
-    private static Logger logger = Logger.getLogger(EventSelect.class);
-
-
-    /**
-     * The default constructor that initializes an empty State object.
-     */
-    public EventSelect() {
-    }
-
-    @Override
-    protected String getUIProvider() {
-        return "fix.event_panel";
-    }
-
-    @Override
-    protected Element createStaticData(
-        FLYSArtifact   flys,
-        ElementCreator creator,
-        CallContext    cc,
-        String         name,
-        String         value,
-        String         type
-    ) {
-        int[] array = FLYSUtils.intArrayFromString(value);
-
-        Element dataElement = creator.create("data");
-        creator.addAttr(dataElement, "name", name, true);
-        creator.addAttr(dataElement, "type", type, true);
-
-        String river = FLYSUtils.getRiver(flys).getName();
-
-        FixingsOverview overview = FixingsOverviewFactory.getOverview(river);
-
-        for (int i = 0; i < array.length; i++) {
-            Element itemElement = creator.create("item");
-            creator.addAttr(
-                itemElement,
-                "value",
-                String.valueOf(array[i]),
-                true);
-
-            creator.addAttr(
-                itemElement,
-                "label",
-                getLabel(cc, array[i], overview),
-                true);
-            dataElement.appendChild(itemElement);
-        }
-        return dataElement;
-    }
-
-
-    public static String getLabel(
-        CallContext cc,
-        int value,
-        FixingsOverview overview
-    ) {
-        logger.debug("Create label for value: " + value);
-
-        IdFilter filter = new IdFilter(value);
-        List<Fixing.Column> columns = overview.filter(null, filter);
-        return  columns.isEmpty()
-            ? ""
-            : columns.get(0).getDescription();
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/fixation/FixAnalysisCompute.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,330 +0,0 @@
-package de.intevation.flys.artifacts.states.fixation;
-
-import java.text.DateFormat;
-import java.util.Date;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.FacetActivity;
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.access.FixAnalysisAccess;
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.DataFacet;
-import de.intevation.flys.artifacts.model.DateRange;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.ReportFacet;
-import de.intevation.flys.artifacts.model.fixings.FixAnalysisCalculation;
-import de.intevation.flys.artifacts.model.fixings.FixAnalysisEventsFacet;
-import de.intevation.flys.artifacts.model.fixings.FixAnalysisPeriodsFacet;
-import de.intevation.flys.artifacts.model.fixings.FixAnalysisResult;
-import de.intevation.flys.artifacts.model.fixings.FixAvSectorFacet;
-import de.intevation.flys.artifacts.model.fixings.FixDerivateFacet;
-import de.intevation.flys.artifacts.model.fixings.FixDeviationFacet;
-import de.intevation.flys.artifacts.model.fixings.FixLongitudinalAnalysisFacet;
-import de.intevation.flys.artifacts.model.fixings.FixLongitudinalAvSectorFacet;
-import de.intevation.flys.artifacts.model.fixings.FixLongitudinalDeviationFacet;
-import de.intevation.flys.artifacts.model.fixings.FixLongitudinalReferenceFacet;
-import de.intevation.flys.artifacts.model.fixings.FixOutlierFacet;
-import de.intevation.flys.artifacts.model.fixings.FixReferenceEventsFacet;
-import de.intevation.flys.artifacts.model.fixings.FixWQCurveFacet;
-import de.intevation.flys.artifacts.resources.Resources;
-import de.intevation.flys.artifacts.states.DefaultState;
-import de.intevation.flys.utils.IdGenerator;
-
-/**
- * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
- */
-public class FixAnalysisCompute
-extends      DefaultState
-implements   FacetTypes
-{
-    /** The log used in this class. */
-    private static Logger log = Logger.getLogger(FixAnalysisCompute.class);
-
-    private static final String I18N_REFERENCEPERIOD_SHORT = "fix.reference.period.event.short";
-
-    private static final String I18N_ANALYSISPERIODS = "fix.analysis.periods";
-
-    private static final String I18N_DERIVATIVE = "fix.derivative";
-
-    private static final String I18N_OUTLIER = "fix.outlier";
-
-    private static final String I18N_ANALYSIS = "fix.analysis.short";
-
-    private static final String I18N_DEVIATION = "fix.deviation";
-
-    private static final String I18N_REFERENCEDEVIATION = "fix.reference.deviation";
-
-    private static final String I18N_REFERENCEPERIOD = "state.fix.analysis.referenceperiod";
-
-    public static final String [] SECTOR_LABELS = {
-        "fix.mnq",
-        "fix.mq",
-        "fix.mhq",
-        "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(
-            "fixanalysis",
-            new FacetActivity() {
-                @Override
-                public Boolean isInitialActive(
-                    Artifact artifact,
-                    Facet    facet,
-                    String   output
-                ) {
-                    if (output.contains(FacetTypes.ChartType.FLSC.toString())) {
-                        // Longitudinal section chart
-                        String name = facet.getName();
-
-                        if (name.contains(FacetTypes.FIX_ANALYSIS_EVENTS_DWT)
-                         || name.contains(FacetTypes.FIX_ANALYSIS_EVENTS_LS)
-                         || name.contains(FacetTypes.FIX_ANALYSIS_EVENTS_WQ)
-                         || name.contains(FacetTypes.FIX_REFERENCE_EVENTS_DWT)
-                         || name.contains(FacetTypes.FIX_REFERENCE_EVENTS_LS)
-                         || name.contains(FacetTypes.FIX_REFERENCE_EVENTS_WQ)
-                        ) {
-                            return Boolean.FALSE;
-                        }
-                    }
-                    if (output.contains(FacetTypes.ChartType.FDWC.toString())
-                        && facet.getName().contains(FacetTypes.FIX_SECTOR_AVERAGE_DWT)) {
-                        return Boolean.FALSE;
-                    }
-
-                    return Boolean.TRUE;
-                }
-            });
-    }
-
-
-    /**
-     * The default constructor that initializes an empty State object.
-     */
-    public FixAnalysisCompute() {
-    }
-
-
-    @Override
-    public Object computeAdvance(
-        FLYSArtifact artifact,
-        String       hash,
-        CallContext  context,
-        List<Facet>  facets,
-        Object       old
-    ) {
-        log.debug("FixAnalysisCompute.computeAdvance");
-
-        CalculationResult res;
-
-        FixAnalysisAccess access =
-            new FixAnalysisAccess(artifact, context);
-
-        if (old instanceof CalculationResult) {
-            res = (CalculationResult)old;
-        }
-        else {
-            FixAnalysisCalculation calc = new FixAnalysisCalculation(access);
-            res = calc.calculate();
-        }
-
-        if (facets == null) {
-            return res;
-        }
-
-        if (res.getReport().hasProblems()) {
-            facets.add(new ReportFacet(ComputeType.ADVANCE, hash, id));
-        }
-
-        FixAnalysisResult fr = (FixAnalysisResult)res.getData();
-        if (fr == null) {
-            return res;
-        }
-
-        facets.add(
-            new DataFacet(CSV, "CSV data", ComputeType.ADVANCE, hash, id));
-        facets.add(
-            new DataFacet(
-                FIX_PARAMETERS, "parameters", ComputeType.ADVANCE, hash, id));
-        facets.add(
-            new DataFacet(AT, "AT data", ComputeType.ADVANCE, hash, id));
-
-        int maxId = -100;
-
-        int sectorMask = fr.getUsedSectorsInAnalysisPeriods();
-
-        int qsS = access.getQSectorStart();
-        int qsE = access.getQSectorEnd();
-
-        // TODO: i18n
-        DateFormat df = DateFormat.getDateInstance(DateFormat.MEDIUM);
-
-        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());
-
-            for (int j = qsS; j <= qsE; j++) {
-
-                // Only emit facets for sectors that really have data.
-                if ((sectorMask & (1 << j)) == 0) {
-                    continue;
-                }
-
-                String sector = SECTOR_LABELS[j];
-                String description = "\u0394W (" +
-                    Resources.getMsg(context.getMeta(),
-                        sector,
-                        sector) +
-                        ")";
-
-                int sectorNdx = j - qsS;
-                int facetNdx = i << 2;
-                facetNdx = facetNdx | j;
-
-                if (facetNdx > maxId) {
-                    maxId = facetNdx;
-                }
-
-                facets.add(
-                    new FixAvSectorFacet(
-                        facetNdx,
-                        FIX_SECTOR_AVERAGE_DWT + "_" + sectorNdx,
-                        description));
-                facets.add(
-                    new FixLongitudinalAvSectorFacet(
-                        facetNdx,
-                        FIX_SECTOR_AVERAGE_LS + "_" + sectorNdx,
-                        description + ":" + startDate + " - " + endDate));
-                // TODO: i18n
-                String dev = "Abweichung: " + description;
-                facets.add(
-                    new FixLongitudinalAvSectorFacet(
-                        facetNdx,
-                        FIX_SECTOR_AVERAGE_LS_DEVIATION + "_" + sectorNdx,
-                        dev));
-                facets.add(
-                    new FixAvSectorFacet(
-                        facetNdx,
-                        FIX_SECTOR_AVERAGE_WQ + "_" + sectorNdx,
-                        description));
-
-            }
-
-            String eventDesc =
-                Resources.getMsg(context.getMeta(),
-                                 I18N_ANALYSIS,
-                                 I18N_ANALYSIS);
-
-            int k = 0;
-            for (Date d: fr.getAnalysisEventsDates(i)) {
-                int anaNdx = i << 8;
-                anaNdx = anaNdx | k;
-                facets.add(new FixAnalysisEventsFacet(anaNdx,
-                    FIX_ANALYSIS_EVENTS_DWT,
-                    eventDesc + (i+1) + " - " + df.format(d)));
-                facets.add(new FixLongitudinalAnalysisFacet(anaNdx,
-                    FIX_ANALYSIS_EVENTS_LS,
-                    eventDesc + (i+1) + " - " + df.format(d)));
-                facets.add(new FixAnalysisEventsFacet(anaNdx,
-                    FIX_ANALYSIS_EVENTS_WQ,
-                    eventDesc + (i+1) +" - " + df.format(d)));
-                k++;
-            }
-        }
-
-        IdGenerator idg = new IdGenerator(maxId + 1);
-
-        String i18n_ref = Resources.getMsg(context.getMeta(),
-                I18N_REFERENCEPERIOD_SHORT,
-                I18N_REFERENCEPERIOD_SHORT);
-        String i18n_dev = Resources.getMsg(context.getMeta(),
-                I18N_REFERENCEDEVIATION,
-                I18N_REFERENCEDEVIATION);
-
-        int i = 0;
-        for (Date d: fr.getReferenceEventsDates()) {
-            int refNdx = idg.next() << 8;
-            refNdx |=  i;
-            facets.add(new FixReferenceEventsFacet(refNdx,
-                FIX_REFERENCE_EVENTS_DWT,
-                i18n_ref + " - " + df.format(d)));
-            refNdx = idg.next() << 8;
-            refNdx = refNdx | i;
-            facets.add(new FixLongitudinalReferenceFacet(refNdx,
-                FIX_REFERENCE_EVENTS_LS,
-                i18n_ref + " - " + df.format(d)));
-            refNdx = idg.next() << 8;
-            refNdx |= i;
-            facets.add(new FixReferenceEventsFacet(refNdx,
-                FIX_REFERENCE_EVENTS_WQ,
-                i18n_ref + " - " + df.format(d)));
-            i++;
-        }
-
-
-        facets.add(new FixLongitudinalDeviationFacet(idg.next(),
-            FIX_DEVIATION_LS,
-            i18n_dev));
-
-        String i18n_ana = Resources.getMsg(context.getMeta(),
-                I18N_ANALYSISPERIODS,
-                I18N_ANALYSISPERIODS);
-        facets.add(new FixAnalysisPeriodsFacet(idg.next(),
-            FIX_ANALYSIS_PERIODS_DWT,
-            i18n_ana));
-        facets.add(new FixAnalysisPeriodsFacet(idg.next(),
-            FIX_ANALYSIS_PERIODS_LS,
-            i18n_ana));
-        facets.add(new FixAnalysisPeriodsFacet(idg.next(),
-            FIX_ANALYSIS_PERIODS_WQ,
-            i18n_ana));
-
-        String i18n_refp = Resources.getMsg(context.getMeta(),
-                I18N_REFERENCEPERIOD,
-                I18N_REFERENCEPERIOD);
-        facets.add(new DataFacet(idg.next(),
-            FIX_REFERENCE_PERIOD_DWT,
-            i18n_refp,
-            ComputeType.ADVANCE, null, null));
-
-        facets.add(new FixWQCurveFacet(idg.next(), "W/Q"));
-
-        Boolean preprocessing = access.getPreprocessing();
-
-        if (preprocessing != null && preprocessing) {
-            facets.add(new FixOutlierFacet(
-                idg.next(),
-                FIX_OUTLIER,
-                Resources.getMsg(
-                    context.getMeta(), I18N_OUTLIER, I18N_OUTLIER)));
-        }
-
-        facets.add(new FixDerivateFacet(
-            idg.next(),
-            FIX_DERIVATE_CURVE,
-            Resources.getMsg(
-                context.getMeta(),
-                I18N_DERIVATIVE,
-                I18N_DERIVATIVE)));
-
-        facets.add(new FixDeviationFacet(
-            idg.next(),
-            FIX_DEVIATION_DWT,
-            Resources.getMsg(context.getMeta(),
-                I18N_DEVIATION,
-                I18N_DEVIATION)));
-        return res;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/fixation/FixRealizingCompute.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,166 +0,0 @@
-package de.intevation.flys.artifacts.states.fixation;
-
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.CallMeta;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.access.FixRealizingAccess;
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.CrossSectionWaterLineFacet;
-import de.intevation.flys.artifacts.model.DataFacet;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.ReportFacet;
-import de.intevation.flys.artifacts.model.WQCKms;
-import de.intevation.flys.artifacts.model.WQKms;
-import de.intevation.flys.artifacts.model.WQKmsFactory;
-import de.intevation.flys.artifacts.model.fixings.FixOutlierFacet;
-import de.intevation.flys.artifacts.model.fixings.FixRealizingCalculation;
-import de.intevation.flys.artifacts.model.fixings.FixRealizingResult;
-import de.intevation.flys.artifacts.model.fixings.FixEventFacet;
-import de.intevation.flys.artifacts.model.fixings.FixReferenceEventsFacet;
-import de.intevation.flys.artifacts.model.fixings.FixWQCurveFacet;
-import de.intevation.flys.artifacts.model.fixings.FixWaterlevelFacet;
-import de.intevation.flys.artifacts.resources.Resources;
-import de.intevation.flys.artifacts.states.DefaultState;
-
-/**
- * State to compute the fixation realizing (vollmer) results.
- *
- * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
- */
-public class FixRealizingCompute extends DefaultState implements FacetTypes {
-
-    /** The logger used in this class. */
-    private static Logger logger = Logger.getLogger(FixRealizingCompute.class);
-
-    public static final String I18N_WQ_CURVE = "fix.vollmer.wq.curve";
-
-    public static final String I18N_WQ_OUTLIER = "fix.vollmer.wq.outliers";
-
-    public static final String I18N_WQ_EVENTS = "fix.vollmer.wq.events";
-
-
-    /**
-     * The default constructor that initializes an empty State object.
-     */
-    public FixRealizingCompute() {
-    }
-
-
-    @Override
-    public Object computeAdvance(
-        FLYSArtifact artifact,
-        String       hash,
-        CallContext  context,
-        List<Facet>  facets,
-        Object       old
-    ) {
-        logger.debug("FixRealizingCompute.computeAdvance");
-
-        CalculationResult res;
-
-        FixRealizingAccess access =
-            new FixRealizingAccess(artifact, context);
-
-        if (old instanceof CalculationResult) {
-            res = (CalculationResult) old;
-        }
-        else {
-            FixRealizingCalculation calc = new FixRealizingCalculation(access);
-            res = calc.calculate();
-        }
-
-        if (facets == null) {
-            return res;
-        }
-
-        if (res.getReport().hasProblems()) {
-            facets.add(new ReportFacet());
-        }
-
-        String   id   = getID();
-        CallMeta meta = context.getMeta();
-
-        FixRealizingResult fixRes = (FixRealizingResult) res.getData();
-        WQKms [] wqkms = fixRes != null ? fixRes.getWQKms() : new WQKms[0];
-
-        for (int i = 0; i < wqkms.length; i++) {
-            String nameW = null;
-            String nameQ = null;
-
-            if (access.isQ()) {
-                nameQ = wqkms[i].getName();
-                nameW = "W(" + nameQ + ")";
-            }
-            else {
-                nameW = wqkms[i].getName();
-                nameQ = "Q(" + nameW + ")";
-            }
-
-            Facet w = new FixWaterlevelFacet(
-                i, LONGITUDINAL_W, nameW);
-
-            Facet q = new FixWaterlevelFacet(
-                i, LONGITUDINAL_Q, nameQ);
-            Facet csFacet = new CrossSectionWaterLineFacet(i, nameW);
-
-            facets.add(w);
-            facets.add(q);
-            facets.add(csFacet);
-
-            if (wqkms[i] instanceof WQCKms) {
-                String nameC = nameW.replace(
-                    "benutzerdefiniert",
-                    "benutzerdefiniert [korrigiert]");
-
-                Facet c = new FixWaterlevelFacet(
-                    i, DISCHARGE_LONGITUDINAL_C, nameC);
-
-                facets.add(c);
-            }
-        }
-
-        // Add one facet per event.
-        int i = 0;
-        for (int event: access.getEvents()) {
-            facets.add(new FixEventFacet(i, FIX_EVENTS, WQKmsFactory.getWQKmsName(event)));
-            i++;
-        }
-
-        if (wqkms.length > 0) {
-            facets.add(
-                new DataFacet(CSV, "CSV data", ComputeType.ADVANCE, hash, id));
-
-            facets.add(
-                new DataFacet(WST, "WST data", ComputeType.ADVANCE, hash, id));
-
-            facets.add(
-                new DataFacet(PDF, "PDF data", ComputeType.ADVANCE, hash, id));
-        }
-
-        facets.add(
-            new DataFacet(AT, "AT data", ComputeType.ADVANCE, hash, id));
-
-        facets.add(new FixWQCurveFacet(
-            0,
-            Resources.getMsg(meta, I18N_WQ_CURVE, I18N_WQ_CURVE) + " ("
-                + access.getFunction() + ")"));
-
-        facets.add(new FixReferenceEventsFacet(
-            0,
-            FIX_REFERENCE_EVENTS_WQ,
-            Resources.getMsg(meta, I18N_WQ_EVENTS, I18N_WQ_EVENTS)));
-
-        facets.add(new FixOutlierFacet(
-            0,
-            FIX_OUTLIER,
-            Resources.getMsg(meta, I18N_WQ_OUTLIER, I18N_WQ_OUTLIER)));
-
-        return res;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/fixation/FixationPeriod.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,28 +0,0 @@
-package de.intevation.flys.artifacts.states.fixation;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.flys.artifacts.states.DefaultState;
-
-/**
- * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
- */
-public class FixationPeriod extends DefaultState {
-
-    /** The logger used in this class. */
-    private static Logger logger = Logger.getLogger(FixationPeriod.class);
-
-
-    /**
-     * The default constructor that initializes an empty State object.
-     */
-    public FixationPeriod() {
-    }
-
-    @Override
-    protected String getUIProvider() {
-        return "fix.period_panel";
-    }
-
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/fixation/FixationSelect.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +0,0 @@
-package de.intevation.flys.artifacts.states.fixation;
-
-import org.w3c.dom.Element;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.artifacts.common.utils.XMLUtils;
-
-import de.intevation.flys.artifacts.resources.Resources;
-import de.intevation.flys.artifacts.states.DefaultState;
-
-
-/**
- * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
- */
-public class FixationSelect extends DefaultState {
-
-    /** Constant value for the default fixation calculation. */
-    public static final String CALCULATION_DEFAULT = "calculation.analysis";
-
-    /** Constant value for the vollmer fixation analysis. */
-    public static final String CALCULATION_VOLLMER = "calculation.vollmer";
-
-    /** An Array that holds all available calculation modes.*/
-    public static final String[] CALCULATIONS = {
-        CALCULATION_DEFAULT,
-        CALCULATION_VOLLMER
-    };
-
-    /** Error message that is thrown if no mode has been chosen. */
-    public static final String ERROR_NO_CALCULATION_MODE =
-        "error_feed_no_calculation_mode";
-
-    /** Error message that is thrown if an invalid calculation mode has been
-     * chosen. */
-    public static  final String ERROR_INVALID_CALCULATION_MODE =
-        "error_feed_invalid_calculation_mode";
-
-
-    /**
-     * The default constructor that initializes an empty State object.
-     */
-    public FixationSelect() {
-    }
-
-
-    @Override
-    protected Element[] createItems(
-        XMLUtils.ElementCreator ec,
-        Artifact                artifact,
-        String                  name,
-        CallContext             context)
-    {
-        CallMeta meta = context.getMeta();
-        Element[] calculations = new Element[CALCULATIONS.length];
-
-        for (int i = 0; i < CALCULATIONS.length; i++) {
-            String calc = CALCULATIONS[i];
-            calculations[i] = createItem(
-                ec,
-                new String[] {Resources.getMsg(meta, calc, calc), calc});
-        }
-        return calculations;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/fixation/FunctionSelect.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,52 +0,0 @@
-package de.intevation.flys.artifacts.states.fixation;
-
-import java.util.Collection;
-
-import org.w3c.dom.Element;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.common.utils.XMLUtils;
-import de.intevation.flys.artifacts.math.fitting.Function;
-import de.intevation.flys.artifacts.math.fitting.FunctionFactory;
-import de.intevation.flys.artifacts.states.DefaultState;
-
-/**
- * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
- */
-public class FunctionSelect extends DefaultState {
-
-    /**
-     * The default constructor that initializes an empty State object.
-     */
-    public FunctionSelect() {
-    }
-
-    @Override
-    public String getUIProvider() {
-        return "fix.functionselect";
-    }
-
-    @Override
-    protected Element[] createItems(
-        XMLUtils.ElementCreator ec,
-        Artifact                artifact,
-        String                  name,
-        CallContext             context)
-    {
-        FunctionFactory ff = FunctionFactory.getInstance();
-        Collection<Function> fc = ff.getFunctions();
-        Element[] functions = new Element[fc.size()];
-
-        int j = 0;
-        for (Function f: fc) {
-            String n = f.getName();
-            String d = f.getDescription();
-            functions[j] = createItem(ec, new String[] {d, n});
-            j++;
-        }
-
-        return functions;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/fixation/GaugeRange.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,28 +0,0 @@
-package de.intevation.flys.artifacts.states.fixation;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.flys.artifacts.states.DefaultState;
-
-/**
- * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
- */
-public class GaugeRange extends DefaultState {
-
-    /** The logger used in this class. */
-    private static Logger logger = Logger.getLogger(GaugeRange.class);
-
-
-    /**
-     * The default constructor that initializes an empty State object.
-     */
-    public GaugeRange() {
-    }
-
-
-    @Override
-    protected String getUIProvider() {
-        return "fix.gaugeselect_panel";
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/fixation/LocationSelect.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,55 +0,0 @@
-package de.intevation.flys.artifacts.states.fixation;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.Artifact;
-
-import de.intevation.flys.utils.FLYSUtils;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.states.RangeState;
-
-
-/**
- * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
- */
-public class LocationSelect extends RangeState {
-
-    /** The logger used in this class. */
-    private static Logger logger = Logger.getLogger(LocationSelect.class);
-
-    private static final String LOWER = "ld_from";
-
-    private static final String UPPER = "ld_to";
-
-    private static final String STEP = "ld_step";
-
-
-    /**
-     * The default constructor that initializes an empty State object.
-     */
-    public LocationSelect() {
-    }
-
-    @Override
-    protected String getUIProvider() {
-        return "fix.location_panel";
-    }
-
-    @Override
-    protected double[] getMinMax(Artifact artifact) {
-        FLYSArtifact flysArtifact = (FLYSArtifact) artifact;
-        return FLYSUtils.getRiverMinMax(flysArtifact);
-    }
-
-
-    /** Misuse to set location mode. */
-    @Override
-    public boolean validate(Artifact artifact)
-    throws IllegalArgumentException
-    {
-        ((FLYSArtifact) artifact).addStringData("ld_mode", "distance");
-        return super.validate(artifact);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/fixation/PreprocessingSelect.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,52 +0,0 @@
-package de.intevation.flys.artifacts.states.fixation;
-
-import org.w3c.dom.Element;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.artifacts.common.utils.XMLUtils;
-
-import de.intevation.flys.artifacts.resources.Resources;
-import de.intevation.flys.artifacts.states.DefaultState;
-
-
-/**
- * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
- */
-public class PreprocessingSelect extends DefaultState {
-
-    /**
-     * The default constructor that initializes an empty State object.
-     */
-    public PreprocessingSelect() {
-    }
-
-    @Override
-    protected Element[] createItems(
-        XMLUtils.ElementCreator ec,
-        Artifact                artifact,
-        String                  name,
-        CallContext             context)
-    {
-        CallMeta meta = context.getMeta();
-        Element[] elements = new Element[1];
-        elements[0] = createItem(
-                ec,
-                new String[] {
-                    Resources.getMsg(meta,
-                        "state.fix.preprocess",
-                        "state.fix.preprocess"),
-                    "preprocess"});
-
-        return elements;
-    }
-
-
-    @Override
-    protected String getUIProvider() {
-        return "fix.preprocessing_panel";
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/fixation/QSelect.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,29 +0,0 @@
-package de.intevation.flys.artifacts.states.fixation;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.flys.artifacts.states.DefaultState;
-
-/**
- * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
- */
-public class QSelect extends DefaultState {
-
-    /** The logger used in this class. */
-    private static Logger logger = Logger.getLogger(QSelect.class);
-
-
-    /**
-     * The default constructor that initializes an empty State object.
-     */
-    public QSelect() {
-    }
-
-
-    /** Show Q Select Panel in clients. */
-    @Override
-    protected String getUIProvider() {
-        return "fix.qselect_panel";
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/fixation/ReferencePeriod.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,28 +0,0 @@
-package de.intevation.flys.artifacts.states.fixation;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.flys.artifacts.states.DefaultState;
-
-/**
- * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
- */
-public class ReferencePeriod extends DefaultState {
-
-    /** The logger used in this class. */
-    private static Logger logger = Logger.getLogger(ReferencePeriod.class);
-
-
-    /**
-     * The default constructor that initializes an empty State object.
-     */
-    public ReferencePeriod() {
-    }
-
-    @Override
-    protected String getUIProvider() {
-        return "fix.period_ref_panel";
-    }
-
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/minfo/BedQualityPeriodsSelect.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,24 +0,0 @@
-package de.intevation.flys.artifacts.states.minfo;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.flys.artifacts.states.DefaultState;
-
-public class BedQualityPeriodsSelect extends DefaultState {
-
-    /** The logger used in this class. */
-    private static Logger logger = Logger.getLogger(BedQualityPeriodsSelect.class);
-
-
-    /**
-     * The default constructor that initializes an empty State object.
-     */
-    public BedQualityPeriodsSelect() {
-    }
-
-    @Override
-    protected String getUIProvider() {
-        return "bedquality_periods_select";
-    }
-
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/minfo/BedQualityState.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,227 +0,0 @@
-package de.intevation.flys.artifacts.states.minfo;
-
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.CallMeta;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.access.BedQualityAccess;
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.DataFacet;
-import de.intevation.flys.artifacts.model.DateRange;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.minfo.BedDensityFacet;
-import de.intevation.flys.artifacts.model.minfo.BedDiameterFacet;
-import de.intevation.flys.artifacts.model.minfo.BedDiameterResult;
-import de.intevation.flys.artifacts.model.minfo.BedParametersResult;
-import de.intevation.flys.artifacts.model.minfo.BedPorosityFacet;
-import de.intevation.flys.artifacts.model.minfo.BedQualityCalculation;
-import de.intevation.flys.artifacts.model.minfo.BedQualityDiameterResult;
-import de.intevation.flys.artifacts.model.minfo.BedQualityResult;
-import de.intevation.flys.artifacts.model.minfo.BedloadDiameterFacet;
-import de.intevation.flys.artifacts.model.minfo.BedloadDiameterResult;
-import de.intevation.flys.artifacts.resources.Resources;
-import de.intevation.flys.artifacts.states.DefaultState;
-
-
-public class BedQualityState extends DefaultState implements FacetTypes {
-
-    private static final long serialVersionUID = 1L;
-
-    private static final Logger logger = Logger
-        .getLogger(BedQualityState.class);
-
-    public static final String I18N_TOPLAYER = "bedquality.toplayer";
-    public static final String I18N_SUBLAYER = "bedquality.sublayer";
-
-    public static final String I18N_FACET_BED_POROSITY_TOPLAYER = "facet.bedquality.bed.porosity.toplayer";
-    public static final String I18N_FACET_BED_POROSITY_SUBLAYER = "facet.bedquality.bed.porosity.sublayer";
-    public static final String I18N_FACET_BED_DENSITY_TOPLAYER = "facet.bedquality.bed.density.toplayer";
-    public static final String I18N_FACET_BED_DENSITY_SUBLAYER = "facet.bedquality.bed.density.sublayer";
-    public static final String I18N_FACET_BED_DIAMETER_TOPLAYER = "facet.bedquality.bed.diameter.toplayer";
-    public static final String I18N_FACET_BED_DIAMETER_SUBLAYER = "facet.bedquality.bed.diameter.sublayer";
-    public static final String I18N_FACET_BEDLOAD_DIAMETER = "facet.bedquality.bedload.diameter";
-
-    @Override
-    public Object computeAdvance(FLYSArtifact artifact, String hash,
-        CallContext context, List<Facet> facets, Object old) {
-        logger.debug("BedQualityState.computeAdvance");
-
-        List<Facet> newFacets = new ArrayList<Facet>();
-
-        BedQualityAccess access = new BedQualityAccess(artifact, context);
-
-        CalculationResult res = old instanceof CalculationResult ? (CalculationResult) old
-            : new BedQualityCalculation().calculate(access);
-
-        if (facets == null || res == null) {
-            return res;
-        }
-
-        BedQualityResult[] results = (BedQualityResult[]) res.getData();
-
-        if (results == null || results.length == 0) {
-            logger.warn("Calculation computed no results!");
-            return res;
-        }
-
-        generateFacets(context, newFacets, results, getID(), hash);
-        logger.debug("Created " + newFacets.size() + " new Facets.");
-
-        facets.addAll(newFacets);
-
-        return res;
-    }
-
-    protected void generateFacets(CallContext context, List<Facet> newFacets,
-        BedQualityResult[] results, String stateId, String hash) {
-        logger.debug("BedQualityState.generateFacets");
-
-        CallMeta meta = context.getMeta();
-
-        newFacets.add(new DataFacet(CSV, "CSV data", ComputeType.ADVANCE, hash, id));
-        for (int idx = 0; idx < results.length; idx++) {
-            BedQualityResult result = results[idx];
-            DateRange range = result.getDateRange();
-            BedDiameterResult[] bedDiameter = result.getBedResults();
-            for (int j = 0; j < bedDiameter.length; j++) {
-                newFacets.add(new BedDiameterFacet((idx << 8) + j,
-                    BED_QUALITY_BED_DIAMETER_TOPLAYER,
-                    createDiameterTopLayerDescription(
-                        meta,
-                        bedDiameter[j],
-                        range),
-                    ComputeType.ADVANCE, stateId, hash));
-
-                newFacets.add(new BedDiameterFacet((idx << 8) +j,
-                    BED_QUALITY_BED_DIAMETER_SUBLAYER,
-                    createDiameterSubLayerDescription(
-                        meta,
-                        bedDiameter[j],
-                        range),
-                    ComputeType.ADVANCE, stateId, hash));
-            }
-            BedloadDiameterResult[] bedloadDiameter = result.getBedloadResults();
-            for (int j = 0;  j < bedloadDiameter.length; j++) {
-                newFacets.add(new BedloadDiameterFacet(
-                    (idx << 8) + j,
-                    BED_QUALITY_BEDLOAD_DIAMETER,
-                    createDiameterDescription(
-                        meta, bedloadDiameter[j]),
-                    ComputeType.ADVANCE,
-                    stateId,
-                    hash));
-
-            }
-            BedParametersResult[] bedParameters = result.getParameters();
-            for (int j = 0; j < bedParameters.length; j++) {
-                newFacets.add(new BedPorosityFacet((idx << 8) + j,
-                    BED_QUALITY_POROSITY_TOPLAYER,
-                    createPorosityTopLayerDescription(
-                        meta,
-                        bedParameters[j],
-                        range),
-                    ComputeType.ADVANCE, stateId, hash));
-
-                newFacets.add(new BedPorosityFacet((idx << 8) + j,
-                    BED_QUALITY_POROSITY_SUBLAYER,
-                    createPorositySubLayerDescription(
-                        meta,
-                        bedParameters[j],
-                        range),
-                    ComputeType.ADVANCE, stateId, hash));
-
-                newFacets.add(new BedDensityFacet((idx << 8) + j,
-                    BED_QUALITY_SEDIMENT_DENSITY_TOPLAYER,
-                    createDensityTopLayerDescription(
-                        meta,
-                        bedParameters[j],
-                        range),
-                    ComputeType.ADVANCE, stateId, hash));
-
-                newFacets.add(new BedDensityFacet((idx << 8) + j,
-                    BED_QUALITY_SEDIMENT_DENSITY_SUBLAYER,
-                    createDensitySubLayerDescription(
-                        meta,
-                        bedParameters[j],
-                        range),
-                    ComputeType.ADVANCE, stateId, hash));
-            }
-        }
-    }
-
-    protected String createPorosityTopLayerDescription(CallMeta meta,
-        BedParametersResult result, DateRange range) {
-        Date from = range != null ? range.getFrom() : new Date();
-        Date to = range != null ? range.getTo() : new Date();
-
-        String toplayer = Resources.getMsg(meta, I18N_TOPLAYER, I18N_TOPLAYER);
-        return Resources.getMsg(meta, I18N_FACET_BED_POROSITY_TOPLAYER,
-            I18N_FACET_BED_POROSITY_TOPLAYER, new Object[] { from, to, toplayer });
-    }
-
-    protected String createPorositySubLayerDescription(CallMeta meta,
-        BedParametersResult result, DateRange range) {
-        Date from = range != null ? range.getFrom() : new Date();
-        Date to = range != null ? range.getTo() : new Date();
-
-        String sublayer = Resources.getMsg(meta, I18N_SUBLAYER, I18N_SUBLAYER);
-        return Resources.getMsg(meta, I18N_FACET_BED_POROSITY_SUBLAYER,
-            I18N_FACET_BED_POROSITY_SUBLAYER, new Object[] { from, to, sublayer });
-    }
-
-    protected String createDensityTopLayerDescription(CallMeta meta,
-        BedParametersResult result, DateRange range) {
-        Date from = range != null ? range.getFrom() : new Date();
-        Date to = range != null ? range.getTo() : new Date();
-
-        String toplayer = Resources.getMsg(meta, I18N_TOPLAYER, I18N_TOPLAYER);
-        return Resources.getMsg(meta, I18N_FACET_BED_DENSITY_TOPLAYER,
-            I18N_FACET_BED_DENSITY_TOPLAYER, new Object[] { from, to, toplayer });
-    }
-
-    protected String createDensitySubLayerDescription(CallMeta meta,
-        BedParametersResult result, DateRange range) {
-        Date from = range != null ? range.getFrom() : new Date();
-        Date to = range != null ? range.getTo() : new Date();
-
-        String sublayer = Resources.getMsg(meta, I18N_SUBLAYER, I18N_SUBLAYER);
-        return Resources.getMsg(meta, I18N_FACET_BED_DENSITY_SUBLAYER,
-            I18N_FACET_BED_DENSITY_SUBLAYER, new Object[] { from, to, sublayer });
-    }
-
-    protected String createDiameterTopLayerDescription(CallMeta meta,
-        BedDiameterResult result, DateRange range) {
-        Date from = range != null ? range.getFrom() : new Date();
-        Date to = range != null ? range.getTo() : new Date();
-
-        String toplayer = Resources.getMsg(meta, I18N_TOPLAYER, I18N_TOPLAYER);
-
-        return Resources.getMsg(meta, I18N_FACET_BED_DIAMETER_TOPLAYER,
-            I18N_FACET_BED_DIAMETER_TOPLAYER, new Object[] { result.getType(),
-                from, to, toplayer });
-    }
-
-    protected String createDiameterSubLayerDescription(CallMeta meta,
-        BedDiameterResult result, DateRange range) {
-        Date from = range != null ? range.getFrom() : new Date();
-        Date to = range != null ? range.getTo() : new Date();
-
-        String sublayer = Resources.getMsg(meta, I18N_SUBLAYER, I18N_SUBLAYER);
-        return Resources.getMsg(meta, I18N_FACET_BED_DIAMETER_SUBLAYER,
-            I18N_FACET_BED_DIAMETER_SUBLAYER, new Object[] { result.getType(),
-                from, to, sublayer });
-    }
-
-    protected String createDiameterDescription(CallMeta meta,
-        BedQualityDiameterResult result) {
-        return Resources.getMsg(meta, I18N_FACET_BEDLOAD_DIAMETER,
-            I18N_FACET_BEDLOAD_DIAMETER, new Object[] { result.getType() });
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/minfo/CharDiameter.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,88 +0,0 @@
-package de.intevation.flys.artifacts.states.minfo;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.common.model.KVP;
-import de.intevation.flys.artifacts.resources.Resources;
-import de.intevation.flys.artifacts.states.MultiStringArrayState;
-
-public class CharDiameter extends MultiStringArrayState {
-
-    private static final Logger logger = Logger.getLogger(CharDiameter.class);
-
-    public static final String UI_PROVIDER = "parameter-matrix";
-
-    private static final String CHAR_DIAMETER_MIN  = "calc.bed.dmin";
-    private static final String CHAR_DIAMETER_MAX  = "calc.bed.dmax";
-    private static final String CHAR_DIAMETER_90  = "calc.bed.d90";
-    private static final String CHAR_DIAMETER_84  = "calc.bed.d84";
-    private static final String CHAR_DIAMETER_80  = "calc.bed.d80";
-    private static final String CHAR_DIAMETER_75  = "calc.bed.d75";
-    private static final String CHAR_DIAMETER_70  = "calc.bed.d70";
-    private static final String CHAR_DIAMETER_60  = "calc.bed.d60";
-    private static final String CHAR_DIAMETER_50  = "calc.bed.d50";
-    private static final String CHAR_DIAMETER_40  = "calc.bed.d40";
-    private static final String CHAR_DIAMETER_30  = "calc.bed.d30";
-    private static final String CHAR_DIAMETER_25  = "calc.bed.d25";
-    private static final String CHAR_DIAMETER_20  = "calc.bed.d20";
-    private static final String CHAR_DIAMETER_16  = "calc.bed.d16";
-    private static final String CHAR_DIAMETER_10  = "calc.bed.d10";
-
-    public static final String[] CHAR_DIAMETER = {
-        CHAR_DIAMETER_10,
-        CHAR_DIAMETER_16,
-        CHAR_DIAMETER_20,
-        CHAR_DIAMETER_25,
-        CHAR_DIAMETER_30,
-        CHAR_DIAMETER_40,
-        CHAR_DIAMETER_50,
-        CHAR_DIAMETER_60,
-        CHAR_DIAMETER_70,
-        CHAR_DIAMETER_75,
-        CHAR_DIAMETER_80,
-        CHAR_DIAMETER_84,
-        CHAR_DIAMETER_90,
-        CHAR_DIAMETER_MAX,
-        CHAR_DIAMETER_MIN
-    };
-
-    @Override
-    public String getUIProvider() {
-        return UI_PROVIDER;
-    }
-
-    @Override
-    protected KVP<String, String>[] getOptions(
-        Artifact artifact,
-        String parameterName,
-        CallContext context
-    )
-    throws IllegalArgumentException
-    {
-        CallMeta meta   = context.getMeta();
-
-        List<KVP<String, String>> rows = new ArrayList<KVP<String, String>>();
-        String key = parameterName;
-        for (int i = 0; i < CHAR_DIAMETER.length; ++i) {
-            String calc = CHAR_DIAMETER[i];
-            rows.add(new KVP (calc,
-                              Resources.getMsg(meta, calc, calc)));
-        }
-
-        return rows.toArray(new KVP[rows.size()]);
-    }
-
-    @Override
-    protected String getLabelFor(CallContext cc, String parameterName,
-            String value) throws IllegalArgumentException {
-
-        return Resources.getMsg(cc.getMeta(), value, value);
-    }
-
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/minfo/DifferenceSelect.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,135 +0,0 @@
-package de.intevation.flys.artifacts.states.minfo;
-
-import java.util.List;
-
-import org.w3c.dom.Element;
-
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.common.utils.XMLUtils;
-import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.access.BedHeightAccess;
-import de.intevation.flys.artifacts.states.DefaultState;
-import de.intevation.flys.utils.StringUtil;
-
-/**
- * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
- */
-public class DifferenceSelect extends DefaultState {
-
-    /**
-     * The default constructor that initializes an empty State object.
-     */
-    public DifferenceSelect() {
-    }
-    /**
-     *  Specify to display nothing (this is kind of a "final" state).
-     */
-    @Override
-    protected String getUIProvider() {
-        return "bedheights_twin_panel";
-    }
-
-    /**
-     * Overridden to do nothing.
-     */
-    @Override
-    public Object computeAdvance(
-        FLYSArtifact artifact,
-        String       hash,
-        CallContext  context,
-        List<Facet>  facets,
-        Object       old
-    ) {
-        //Get data and do stuff, do not calculate
-        return "";
-    }
-
-    @Override
-    protected Element[] createItems(
-        XMLUtils.ElementCreator ec,
-        Artifact                artifact,
-        String                  name,
-        CallContext             context)
-    {
-        Element[] elements = new Element[1];
-        BedHeightAccess bha = new BedHeightAccess((FLYSArtifact)artifact, context);
-        String time = bha.getYearEpoch();
-        elements[0] = createItem(
-            ec,
-            new String[] {"year-epoch", time});
-
-        return elements;
-    }
-
-    /**
-     * Creates the data element used for the static part of DESCRIBE document.
-     */
-    @Override
-    protected Element createStaticData(
-        FLYSArtifact   flys,
-        ElementCreator creator,
-        CallContext    cc,
-        String         name,
-        String         value,
-        String         type
-    ) {
-        Element dataElement = creator.create("data");
-        creator.addAttr(dataElement, "name", name, true);
-        creator.addAttr(dataElement, "type", type, true);
-
-        Element itemElement = creator.create("item");
-        creator.addAttr(itemElement, "value", value, true);
-
-        String[] labels = getLabels(cc, value);
-
-        creator.addAttr(
-            itemElement,
-            "label",
-            labels[0],
-            true);
-        dataElement.appendChild(itemElement);
-
-        return dataElement;
-    }
-
-
-    /**
-     * Get name to display for selected watelerlevels (for example "Q=123")
-     * from the CalculationResult.
-     */
-    public static String[] getLabels(CallContext cc, String value) {
-        String[] recommendations = value.split("#");
-        String displayString = "";
-
-        // Walk over all selected recommendations and create label
-        for (int i = 0; i < recommendations.length; i+=2) {
-            String[] minuendParts = StringUtil
-                .unbracket(recommendations[i+0])
-                .split(";");
-            if(minuendParts.length >= 4) {
-                displayString += "(" + minuendParts[3];
-            }
-            else {
-                displayString += "([error]";
-            }
-
-            displayString += " - ";
-
-            String[] subtrahendParts = StringUtil
-                .unbracket(recommendations[i+1])
-                .split(";");
-            if(subtrahendParts.length >= 4) {
-                displayString += subtrahendParts[3] + ") ";
-            }
-            else {
-                displayString += "[error])";
-            }
-        }
-
-        return new String[] { displayString };
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/minfo/DifferencesState.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,403 +0,0 @@
-package de.intevation.flys.artifacts.states.minfo;
-
-import java.text.DateFormat;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.FacetActivity;
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.CallMeta;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.access.BedDifferencesAccess;
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.minfo.BedDiffCalculation;
-import de.intevation.flys.artifacts.model.minfo.BedDiffEpochFacet;
-import de.intevation.flys.artifacts.model.minfo.BedDiffEpochFilterFacet;
-import de.intevation.flys.artifacts.model.minfo.BedDiffEpochResult;
-import de.intevation.flys.artifacts.model.minfo.BedDiffYearFacet;
-import de.intevation.flys.artifacts.model.minfo.BedDiffYearFilterFacet;
-import de.intevation.flys.artifacts.model.minfo.BedDiffYearResult;
-import de.intevation.flys.artifacts.model.minfo.BedDifferencesResult;
-import de.intevation.flys.artifacts.resources.Resources;
-import de.intevation.flys.artifacts.states.DefaultState;
-import de.intevation.flys.utils.Formatter;
-
-/**
- * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
- */
-public class DifferencesState
-extends DefaultState
-implements FacetTypes
-{
-    /** The logger used in this class. */
-    private static Logger logger = Logger.getLogger(DifferencesState.class);
-    public static final String I18N_DIFF_YEAR = "beddifference.year";
-    public static final String I18N_DIFF_EPOCH = "beddifference.epoch";
-
-    public static final String I18N_FACET_BED_DIFF_YEAR = "facet.bedheight.diff.year";
-    public static final String I18N_FACET_BED_DIFF_YEAR_RAW = "facet.bedheight.diff.year.raw";
-    public static final String I18N_FACET_BED_DIFF_ABSOLUTE = "facet.bedheight.diff.absolute";
-    public static final String I18N_FACET_BED_DIFF_ABSOLUTE_RAW = "facet.bedheight.diff.absolute.raw";
-    public static final String I18N_FACET_BED_DIFF_MORPH = "facet.bedheight.diff.morph";
-    public static final String I18N_FACET_BED_DIFF_EPOCH = "facet.bedheight.diff.epoch";
-    public static final String I18N_FACET_BED_DIFF_EPOCH_RAW = "facet.bedheight.diff.epoch.raw";
-    public static final String I18N_FACET_BED_DIFF_HEIGHT1 = "facet.bedheight.diff.height1";
-    public static final String I18N_FACET_BED_DIFF_HEIGHT1_RAW = "facet.bedheight.diff.height1.raw";
-    public static final String I18N_FACET_BED_DIFF_HEIGHT2 = "facet.bedheight.diff.height2";
-    public static final String I18N_FACET_BED_DIFF_HEIGHT2_RAW = "facet.bedheight.diff.height2.raw";
-
-    public DifferencesState() {
-    }
-
-    @Override
-    public Object computeAdvance(FLYSArtifact artifact, String hash,
-        CallContext context, List<Facet> facets, Object old) {
-        logger.debug("BedQualityState.computeAdvance");
-
-        List<Facet> newFacets = new ArrayList<Facet>();
-
-        BedDifferencesAccess access =
-            new BedDifferencesAccess(artifact, context);
-
-        CalculationResult res = old instanceof CalculationResult ? (CalculationResult) old
-            : new BedDiffCalculation().calculate(access);
-
-        if (facets == null || res == null) {
-            return res;
-        }
-
-        BedDifferencesResult[] results = (BedDifferencesResult[]) res.getData();
-
-        if (results == null || results.length == 0) {
-            logger.warn("Calculation computed no results!");
-            return res;
-        }
-
-        generateFacets(context, newFacets, results, getID(), hash);
-        logger.debug("Created " + newFacets.size() + " new Facets.");
-
-        facets.addAll(newFacets);
-
-        return res;
-    }
-
-    protected void generateFacets(CallContext context, List<Facet> newFacets,
-        BedDifferencesResult[] results, String stateId, String hash) {
-        logger.debug("BedQualityState.generateFacets");
-
-        CallMeta meta = context.getMeta();
-
-        for (int idx = 0; idx < results.length; idx++) {
-            if (results[idx] instanceof BedDiffYearResult) {
-                newFacets.add(new BedDiffYearFacet(
-                    idx,
-                    BED_DIFFERENCE_YEAR,
-                    createBedDiffYearDescription(
-                        meta,
-                        (BedDiffYearResult)results[idx],
-                        true),
-                    ComputeType.ADVANCE,
-                    stateId,
-                    hash));
-                newFacets.add(new BedDiffYearFacet(
-                    idx,
-                    BED_DIFFERENCE_MORPH_WIDTH,
-                    createBedDiffMorphDescription(
-                        meta,
-                        (BedDiffYearResult)results[idx]),
-                    ComputeType.ADVANCE,
-                    stateId,
-                    hash));
-                newFacets.add(new BedDiffYearFacet(
-                    idx,
-                    BED_DIFFERENCE_YEAR_HEIGHT1,
-                    createBedDiffHeightDescription(
-                        meta,
-                        (BedDiffYearResult)results[idx],
-                        0,
-                        true),
-                    ComputeType.ADVANCE,
-                    stateId,
-                    hash));
-                 newFacets.add(new BedDiffYearFacet(
-                    idx,
-                    BED_DIFFERENCE_YEAR_HEIGHT2,
-                    createBedDiffHeightDescription(
-                        meta,
-                        (BedDiffYearResult)results[idx],
-                        1,
-                        true),
-                    ComputeType.ADVANCE,
-                    stateId,
-                    hash));
-                newFacets.add(new BedDiffYearFacet(
-                    idx,
-                    BED_DIFFERENCE_HEIGHT_YEAR,
-                    createBedDiffAbsoluteDescription(
-                        meta,
-                        (BedDiffYearResult)results[idx],
-                        true),
-                    ComputeType.ADVANCE,
-                    stateId,
-                    hash));
-                newFacets.add(new BedDiffYearFilterFacet(
-                    idx,
-                    BED_DIFFERENCE_YEAR_FILTERED,
-                    createBedDiffYearDescription(
-                        meta,
-                        (BedDiffYearResult)results[idx],
-                        false),
-                    ComputeType.ADVANCE,
-                    stateId,
-                    hash));
-                newFacets.add(new BedDiffYearFilterFacet(
-                    idx,
-                    BED_DIFFERENCE_YEAR_HEIGHT1_FILTERED,
-                    createBedDiffHeightDescription(
-                        meta,
-                        (BedDiffYearResult)results[idx],
-                        0,
-                        false),
-                    ComputeType.ADVANCE,
-                    stateId,
-                    hash));
-                 newFacets.add(new BedDiffYearFilterFacet(
-                    idx,
-                    BED_DIFFERENCE_YEAR_HEIGHT2_FILTERED,
-                    createBedDiffHeightDescription(
-                        meta,
-                        (BedDiffYearResult)results[idx],
-                        1,
-                        false),
-                    ComputeType.ADVANCE,
-                    stateId,
-                    hash));
-                newFacets.add(new BedDiffYearFilterFacet(
-                    idx,
-                    BED_DIFFERENCE_HEIGHT_YEAR_FILTERED,
-                    createBedDiffAbsoluteDescription(
-                        meta,
-                        (BedDiffYearResult)results[idx],
-                        false),
-                    ComputeType.ADVANCE,
-                    stateId,
-                    hash));
-            }
-            if (results[idx] instanceof BedDiffEpochResult) {
-                newFacets.add(new BedDiffEpochFacet(
-                    idx,
-                    BED_DIFFERENCE_EPOCH,
-                    createBedDiffEpochDescription(
-                        meta,
-                        (BedDiffEpochResult)results[idx],
-                        true),
-                    ComputeType.ADVANCE,
-                    stateId,
-                    hash));
-                newFacets.add(new BedDiffEpochFacet(
-                    idx,
-                    BED_DIFFERENCE_EPOCH_HEIGHT1,
-                    createBedDiffHeightEpochDescription(
-                        meta,
-                        (BedDiffEpochResult)results[idx],
-                        0,
-                        true),
-                    ComputeType.ADVANCE,
-                    stateId,
-                    hash));
-                newFacets.add(new BedDiffEpochFacet(
-                    idx,
-                    BED_DIFFERENCE_EPOCH_HEIGHT2,
-                    createBedDiffHeightEpochDescription(
-                        meta,
-                        (BedDiffEpochResult)results[idx],
-                        1,
-                        true),
-                    ComputeType.ADVANCE,
-                    stateId,
-                    hash));
-                newFacets.add(new BedDiffEpochFilterFacet(
-                    idx,
-                    BED_DIFFERENCE_EPOCH_FILTERED,
-                    createBedDiffEpochDescription(
-                        meta,
-                        (BedDiffEpochResult)results[idx],
-                        false),
-                    ComputeType.ADVANCE,
-                    stateId,
-                    hash));
-                newFacets.add(new BedDiffEpochFilterFacet(
-                    idx,
-                    BED_DIFFERENCE_EPOCH_HEIGHT1_FILTERED,
-                    createBedDiffHeightEpochDescription(
-                        meta,
-                        (BedDiffEpochResult)results[idx],
-                        0,
-                        false),
-                    ComputeType.ADVANCE,
-                    stateId,
-                    hash));
-                newFacets.add(new BedDiffEpochFilterFacet(
-                    idx,
-                    BED_DIFFERENCE_EPOCH_HEIGHT2_FILTERED,
-                    createBedDiffHeightEpochDescription(
-                        meta,
-                        (BedDiffEpochResult)results[idx],
-                        1,
-                        false),
-                    ComputeType.ADVANCE,
-                    stateId,
-                    hash));
-            }
-        }
-    }
-
-    private String createBedDiffHeightDescription(
-        CallMeta meta,
-        BedDiffYearResult result,
-        int ndx,
-        boolean raw
-    ) {
-        String range = result.getStart() + " - " + result.getEnd();
-
-        String i18nHeight1 = I18N_FACET_BED_DIFF_HEIGHT1;
-        String i18nHeight2 = I18N_FACET_BED_DIFF_HEIGHT2;
-        if (raw) {
-            i18nHeight1 = I18N_FACET_BED_DIFF_HEIGHT1_RAW;
-            i18nHeight2 = I18N_FACET_BED_DIFF_HEIGHT2_RAW;
-        }
-        if (ndx == 0) {
-            return Resources.getMsg(meta, i18nHeight1,
-                i18nHeight2, new Object[] { range });
-        }
-        else {
-            return Resources.getMsg(meta, i18nHeight1,
-                i18nHeight2, new Object[] {range});
-        }
-    }
-
-    private String createBedDiffHeightEpochDescription(
-        CallMeta meta,
-        BedDiffEpochResult result,
-        int ndx,
-        boolean raw
-    ) {
-        DateFormat df = Formatter.getDateFormatter(meta, "yyyy");
-        String range =
-            df.format(result.getStart())
-            + " - " +
-            df.format(result.getEnd());
-
-        String i18nHeight1 = I18N_FACET_BED_DIFF_HEIGHT1;
-        String i18nHeight2 = I18N_FACET_BED_DIFF_HEIGHT2;
-        if (raw) {
-            i18nHeight1 = I18N_FACET_BED_DIFF_HEIGHT1_RAW;
-            i18nHeight2 = I18N_FACET_BED_DIFF_HEIGHT2_RAW;
-        }
-        if (ndx == 0) {
-            return Resources.getMsg(meta, i18nHeight1,
-                i18nHeight1, new Object[] { range });
-        }
-        else {
-            return Resources.getMsg(meta, i18nHeight2,
-                i18nHeight2, new Object[] {range});
-        }
-    }
-
-    protected String createBedDiffYearDescription(
-        CallMeta meta,
-        BedDiffYearResult result,
-        boolean raw
-    ) {
-        String range = result.getStart() + " - " + result.getEnd();
-
-        String i18n = I18N_FACET_BED_DIFF_YEAR;
-        if (raw) {
-            i18n = I18N_FACET_BED_DIFF_YEAR_RAW;
-        }
-        return Resources.getMsg(meta, i18n, i18n, new Object[] { range });
-    }
-
-    protected String createBedDiffMorphDescription(
-        CallMeta meta,
-        BedDiffYearResult result) {
-        String range = result.getStart() + " - " + result.getEnd();
-
-        return Resources.getMsg(meta, I18N_FACET_BED_DIFF_MORPH,
-            I18N_FACET_BED_DIFF_MORPH, new Object[] { range });
-    }
-
-    protected String createBedDiffAbsoluteDescription(
-        CallMeta meta,
-        BedDiffYearResult result,
-        boolean raw
-    ) {
-        String range = result.getStart() + " - " + result.getEnd();
-
-        String i18n = I18N_FACET_BED_DIFF_ABSOLUTE;
-        if (raw) {
-            i18n = I18N_FACET_BED_DIFF_ABSOLUTE_RAW;
-        }
-        return Resources.getMsg(meta, i18n, i18n, new Object[] { range });
-    }
-
-    protected String createBedDiffEpochDescription(
-        CallMeta meta,
-        BedDiffEpochResult result,
-        boolean raw) {
-        DateFormat df = Formatter.getDateFormatter(meta, "yyyy");
-        String range =
-            df.format(result.getStart())
-            + " - " +
-            df.format(result.getEnd());
-
-        String i18n = I18N_FACET_BED_DIFF_EPOCH;
-        if (raw) {
-            i18n = I18N_FACET_BED_DIFF_EPOCH_RAW;
-        }
-        return Resources.getMsg(meta, i18n, i18n, new Object[] { range });
-    }
-
-    static {
-        // Active/deactivate facets.
-        FacetActivity.Registry.getInstance().register(
-            "minfo",
-            new FacetActivity() {
-                @Override
-                public Boolean isInitialActive(
-                    Artifact artifact,
-                    Facet    facet,
-                    String   output
-                ) {
-                    String name = facet.getName();
-                    if (name.equals(BED_DIFFERENCE_EPOCH) ||
-                        name.equals(BED_DIFFERENCE_EPOCH_HEIGHT1) ||
-                        name.equals(BED_DIFFERENCE_EPOCH_HEIGHT2) ||
-                        name.equals(BED_DIFFERENCE_HEIGHT_YEAR) ||
-                        name.equals(BED_DIFFERENCE_YEAR) ||
-                        name.equals(BED_DIFFERENCE_YEAR_HEIGHT1) ||
-                        name.equals(BED_DIFFERENCE_YEAR_HEIGHT2)) {
-                        return Boolean.FALSE;
-                    }
-                    else if (name.equals(BED_DIFFERENCE_MORPH_WIDTH) ||
-                        name.equals(BED_DIFFERENCE_EPOCH_FILTERED) ||
-                        name.equals(BED_DIFFERENCE_EPOCH_HEIGHT1_FILTERED) ||
-                        name.equals(BED_DIFFERENCE_EPOCH_HEIGHT2_FILTERED) ||
-                        name.equals(BED_DIFFERENCE_HEIGHT_YEAR_FILTERED) ||
-                        name.equals(BED_DIFFERENCE_YEAR_FILTERED) ||
-                        name.equals(BED_DIFFERENCE_YEAR_HEIGHT1_FILTERED) ||
-                        name.equals(BED_DIFFERENCE_YEAR_HEIGHT2_FILTERED)){
-                        return Boolean.TRUE;
-                    }
-                    else {
-                        return null;
-                    }
-                }
-            });
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/minfo/SedimentLoadCalculate.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,432 +0,0 @@
-package de.intevation.flys.artifacts.states.minfo;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.CallMeta;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.access.SedimentLoadAccess;
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.DataFacet;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.ReportFacet;
-import de.intevation.flys.artifacts.model.minfo.SedimentLoadCalculation;
-import de.intevation.flys.artifacts.model.minfo.SedimentLoadFacet;
-import de.intevation.flys.artifacts.model.minfo.SedimentLoadResult;
-import de.intevation.flys.artifacts.resources.Resources;
-import de.intevation.flys.artifacts.states.DefaultState;
-
-
-public class SedimentLoadCalculate
-extends DefaultState
-implements FacetTypes
-{
-
-    private static final long serialVersionUID = 1L;
-
-    private static final Logger logger = Logger
-        .getLogger(SedimentLoadCalculate.class);
-
-    public static final String I18N_FACET_SEDIMENTLOAD_COARSE = "facet.sedimentload.coarse";
-    public static final String I18N_FACET_SEDIMENTLOAD_SAND = "facet.sedimentload.sand";
-    public static final String I18N_FACET_SEDIMENTLOAD_FINE_MIDDLE = "facet.sedimentload.fine_middle";
-    public static final String I18N_FACET_SEDIMENTLOAD_SUSPSAND = "facet.sedimentload.susp_sand";
-    public static final String I18N_FACET_SEDIMENTLOAD_SUSPSANDBED = "facet.sediemntload.susp_sand_bed";
-    public static final String I18N_FACET_SEDIMENTLOAD_SUSPSEDIMENT = "facet.sedimentload.susp_sediment";
-    public static final String I18N_FACET_SEDIMENTLOAD_TOTAL_LOAD = "facet.sedimentload.total_load";
-    public static final String I18N_FACET_SEDIMENTLOAD_TOTAL = "facet.sedimentload.total";
-
-    @Override
-    public Object computeAdvance(FLYSArtifact artifact, String hash,
-        CallContext context, List<Facet> facets, Object old) {
-        logger.debug("SedimentLoadCalculate.computeAdvance");
-
-        List<Facet> newFacets = new ArrayList<Facet>();
-
-        SedimentLoadAccess access = new SedimentLoadAccess(artifact, context);
-
-        CalculationResult res = old instanceof CalculationResult ? (CalculationResult) old
-            : new SedimentLoadCalculation().calculate(access);
-
-        if (facets == null || res == null) {
-            return res;
-        }
-
-        SedimentLoadResult[] results = (SedimentLoadResult[]) res.getData();
-
-        if (results == null || results.length == 0) {
-            logger.warn("Calculation computed no results!");
-            return res;
-        }
-
-        String type = access.getYearEpoch();
-        if (type.equals("year")) {
-            generateYearFacets(context, newFacets, results, getID(), hash);
-        }
-        else if (type.equals("epoch")) {
-            generateEpochFacets(context, newFacets, results, getID(), hash);
-        }
-        else if (type.equals("off_epoch")) {
-            generateOffEpochFacets(context, newFacets, results, getID(), hash);
-        }
-        logger.debug("Created " + newFacets.size() + " new Facets.");
-        if (res.getReport().hasProblems()) {
-            newFacets.add(new ReportFacet(ComputeType.ADVANCE, hash, id));
-        }
-        facets.addAll(newFacets);
-
-        return res;
-    }
-
-    protected void generateYearFacets(CallContext context, List<Facet> newFacets,
-        SedimentLoadResult[] results, String stateId, String hash) {
-        logger.debug("SedimentLoadCalculate.generateFacets");
-
-        CallMeta meta = context.getMeta();
-
-//      newFacets.add(new DataFacet(CSV, "CSV data", ComputeType.ADVANCE, hash, id));
-        for (int idx = 0; idx < results.length; idx++) {
-            SedimentLoadResult res = results[idx];
-            if (res.hasCoarseData()) {
-                newFacets.add(new SedimentLoadFacet(
-                    idx,
-                    SEDIMENT_LOAD_COARSE,
-                    Resources.getMsg(
-                        meta,
-                        I18N_FACET_SEDIMENTLOAD_COARSE,
-                        I18N_FACET_SEDIMENTLOAD_COARSE) +
-                        " - " + res.getStartYear(),
-                    ComputeType.ADVANCE,
-                    stateId,
-                    hash));
-            }
-            if (res.hasSandData()) {
-                newFacets.add(new SedimentLoadFacet(
-                    idx,
-                    SEDIMENT_LOAD_SAND,
-                    Resources.getMsg(
-                        meta,
-                        I18N_FACET_SEDIMENTLOAD_SAND,
-                        I18N_FACET_SEDIMENTLOAD_SAND) +
-                        " - " + res.getStartYear(),
-                    ComputeType.ADVANCE,
-                    stateId,
-                    hash));
-            }
-            if (res.hasFineMiddleData()) {
-                newFacets.add(new SedimentLoadFacet(
-                    idx,
-                    SEDIMENT_LOAD_FINEMIDDLE,
-                    Resources.getMsg(
-                        meta,
-                        I18N_FACET_SEDIMENTLOAD_FINE_MIDDLE,
-                        I18N_FACET_SEDIMENTLOAD_FINE_MIDDLE) +
-                        " - " + res.getStartYear(),
-                    ComputeType.ADVANCE,
-                    stateId,
-                    hash));
-            }
-            if (res.hasSuspSandData()) {
-                newFacets.add(new SedimentLoadFacet(
-                    idx,
-                    SEDIMENT_LOAD_SUSP_SAND,
-                    Resources.getMsg(
-                        meta,
-                        I18N_FACET_SEDIMENTLOAD_SUSPSAND,
-                        I18N_FACET_SEDIMENTLOAD_SUSPSAND)
-                        + " - " + res.getStartYear(),
-                    ComputeType.ADVANCE,
-                    stateId,
-                    hash));
-            }
-            if (res.hasSuspSandBedData()) {
-                newFacets.add(new SedimentLoadFacet(
-                    idx,
-                    SEDIMENT_LOAD_SUSP_SAND_BED,
-                    Resources.getMsg(
-                        meta,
-                        I18N_FACET_SEDIMENTLOAD_SUSPSANDBED,
-                        I18N_FACET_SEDIMENTLOAD_SUSPSANDBED) +
-                        " - " + res.getStartYear(),
-                    ComputeType.ADVANCE,
-                    stateId,
-                    hash));
-            }
-            if (res.hasSuspSedimentData()) {
-                newFacets.add(new SedimentLoadFacet(
-                    idx,
-                    SEDIMENT_LOAD_SUSP_SEDIMENT,
-                    Resources.getMsg(
-                        meta,
-                        I18N_FACET_SEDIMENTLOAD_SUSPSEDIMENT,
-                        I18N_FACET_SEDIMENTLOAD_SUSPSEDIMENT) +
-                        " - " + res.getStartYear(),
-                    ComputeType.ADVANCE,
-                    stateId,
-                    hash));
-
-            }
-            if (res.hasTotalData()) {
-                newFacets.add(new SedimentLoadFacet(
-                    idx,
-                    SEDIMENT_LOAD_TOTAL,
-                    Resources.getMsg(
-                        meta,
-                        I18N_FACET_SEDIMENTLOAD_TOTAL,
-                        I18N_FACET_SEDIMENTLOAD_TOTAL) +
-                        " - " + res.getStartYear(),
-                    ComputeType.ADVANCE,
-                    stateId,
-                    hash));
-            }
-        }
-    }
-
-    protected void generateEpochFacets(
-        CallContext context,
-        List<Facet> newFacets,
-        SedimentLoadResult[] results,
-        String stateId,
-        String hash
-    ) {
-        logger.debug("SedimentLoadCalculate.generateEpochFacets");
-
-        CallMeta meta = context.getMeta();
-
-//      newFacets.add(new DataFacet(CSV, "CSV data", ComputeType.ADVANCE, hash, id));
-        for (int idx = 0; idx < results.length; idx++) {
-            SedimentLoadResult res = results[idx];
-            if (res.hasCoarseData()) {
-                newFacets.add(new SedimentLoadFacet(
-                    idx,
-                    SEDIMENT_LOAD_COARSE,
-                    Resources.getMsg(
-                        meta,
-                        I18N_FACET_SEDIMENTLOAD_COARSE,
-                        I18N_FACET_SEDIMENTLOAD_COARSE) +
-                        " - " + res.getStartYear() + "-" + res.getEndYear(),
-                    ComputeType.ADVANCE,
-                    stateId,
-                    hash));
-            }
-            if (res.hasSandData()) {
-                newFacets.add(new SedimentLoadFacet(
-                    idx,
-                    SEDIMENT_LOAD_SAND,
-                    Resources.getMsg(
-                        meta,
-                        I18N_FACET_SEDIMENTLOAD_SAND,
-                        I18N_FACET_SEDIMENTLOAD_SAND) +
-                        " - " + res.getStartYear() + "-" + res.getEndYear(),
-                    ComputeType.ADVANCE,
-                    stateId,
-                    hash));
-            }
-            if (res.hasFineMiddleData()) {
-                newFacets.add(new SedimentLoadFacet(
-                    idx,
-                    SEDIMENT_LOAD_FINEMIDDLE,
-                    Resources.getMsg(
-                        meta,
-                        I18N_FACET_SEDIMENTLOAD_FINE_MIDDLE,
-                        I18N_FACET_SEDIMENTLOAD_FINE_MIDDLE) +
-                        " - " + res.getStartYear(),
-                    ComputeType.ADVANCE,
-                    stateId,
-                    hash));
-            }
-            if (res.hasSuspSandData()) {
-                newFacets.add(new SedimentLoadFacet(
-                    idx,
-                    SEDIMENT_LOAD_SUSP_SAND,
-                    Resources.getMsg(
-                        meta,
-                        I18N_FACET_SEDIMENTLOAD_SUSPSAND,
-                        I18N_FACET_SEDIMENTLOAD_SUSPSAND)
-                        + " - " + res.getStartYear() + "-" + res.getEndYear(),
-                    ComputeType.ADVANCE,
-                    stateId,
-                    hash));
-            }
-            if (res.hasSuspSandBedData()) {
-                newFacets.add(new SedimentLoadFacet(
-                    idx,
-                    SEDIMENT_LOAD_SUSP_SAND_BED,
-                    Resources.getMsg(
-                        meta,
-                        I18N_FACET_SEDIMENTLOAD_SUSPSANDBED,
-                        I18N_FACET_SEDIMENTLOAD_SUSPSANDBED) +
-                        " - " + res.getStartYear() + "-" + res.getEndYear(),
-                    ComputeType.ADVANCE,
-                    stateId,
-                    hash));
-            }
-            if (res.hasSuspSedimentData()) {
-                newFacets.add(new SedimentLoadFacet(
-                    idx,
-                    SEDIMENT_LOAD_SUSP_SEDIMENT,
-                    Resources.getMsg(
-                        meta,
-                        I18N_FACET_SEDIMENTLOAD_SUSPSEDIMENT,
-                        I18N_FACET_SEDIMENTLOAD_SUSPSEDIMENT) +
-                        " - " + res.getStartYear() + "-" + res.getEndYear(),
-                    ComputeType.ADVANCE,
-                    stateId,
-                    hash));
-
-            }
-            if (res.hasTotalLoadData()) {
-                newFacets.add(new SedimentLoadFacet(
-                    idx,
-                    SEDIMENT_LOAD_TOTAL_LOAD,
-                    Resources.getMsg(
-                        meta,
-                        I18N_FACET_SEDIMENTLOAD_TOTAL_LOAD,
-                        I18N_FACET_SEDIMENTLOAD_TOTAL_LOAD) +
-                        " - " + res.getStartYear() + "-" + res.getEndYear(),
-                    ComputeType.ADVANCE,
-                    stateId,
-                    hash));
-            }
-            if (res.hasTotalData()) {
-                newFacets.add(new SedimentLoadFacet(
-                    idx,
-                    SEDIMENT_LOAD_TOTAL,
-                    Resources.getMsg(
-                        meta,
-                        I18N_FACET_SEDIMENTLOAD_TOTAL,
-                        I18N_FACET_SEDIMENTLOAD_TOTAL) +
-                        " - " + res.getStartYear() + "-" + res.getEndYear(),
-                    ComputeType.ADVANCE,
-                    stateId,
-                    hash));
-            }
-        }
-    }
-
-    protected void generateOffEpochFacets(
-        CallContext context,
-        List<Facet> newFacets,
-        SedimentLoadResult[] results,
-        String stateId,
-        String hash
-    ) {
-        logger.debug("SedimentLoadCalculate.generateOffEpochFacets");
-
-        CallMeta meta = context.getMeta();
-
-//      newFacets.add(new DataFacet(CSV, "CSV data", ComputeType.ADVANCE, hash, id));
-        for (int idx = 0; idx < results.length; idx++) {
-            SedimentLoadResult res = results[idx];
-            if (res.hasCoarseData()) {
-                newFacets.add(new SedimentLoadFacet(
-                    idx,
-                    SEDIMENT_LOAD_COARSE,
-                    Resources.getMsg(
-                        meta,
-                        I18N_FACET_SEDIMENTLOAD_COARSE,
-                        I18N_FACET_SEDIMENTLOAD_COARSE) +
-                        " - " + res.getStartYear() + "-" + res.getEndYear(),
-                    ComputeType.ADVANCE,
-                    stateId,
-                    hash));
-            }
-            if (res.hasSandData()) {
-                newFacets.add(new SedimentLoadFacet(
-                    idx,
-                    SEDIMENT_LOAD_SAND,
-                    Resources.getMsg(
-                        meta,
-                        I18N_FACET_SEDIMENTLOAD_SAND,
-                        I18N_FACET_SEDIMENTLOAD_SAND) +
-                        " - " + res.getStartYear() + "-" + res.getEndYear(),
-                    ComputeType.ADVANCE,
-                    stateId,
-                    hash));
-            }
-            if (res.hasFineMiddleData()) {
-                newFacets.add(new SedimentLoadFacet(
-                    idx,
-                    SEDIMENT_LOAD_FINEMIDDLE,
-                    Resources.getMsg(
-                        meta,
-                        I18N_FACET_SEDIMENTLOAD_FINE_MIDDLE,
-                        I18N_FACET_SEDIMENTLOAD_FINE_MIDDLE) +
-                        " - " + res.getStartYear() + "-" + res.getEndYear(),
-                    ComputeType.ADVANCE,
-                    stateId,
-                    hash));
-            }
-            if (res.hasSuspSandData()) {
-                newFacets.add(new SedimentLoadFacet(
-                    idx,
-                    SEDIMENT_LOAD_SUSP_SAND,
-                    Resources.getMsg(
-                        meta,
-                        I18N_FACET_SEDIMENTLOAD_SUSPSAND,
-                        I18N_FACET_SEDIMENTLOAD_SUSPSAND) +
-                        " - " + res.getStartYear() + "-" + res.getEndYear(),
-                    ComputeType.ADVANCE,
-                    stateId,
-                    hash));
-            }
-            if (res.hasSuspSandBedData()) {
-                newFacets.add(new SedimentLoadFacet(
-                    idx,
-                    SEDIMENT_LOAD_SUSP_SAND_BED,
-                    Resources.getMsg(
-                        meta,
-                        I18N_FACET_SEDIMENTLOAD_SUSPSANDBED,
-                        I18N_FACET_SEDIMENTLOAD_SUSPSANDBED) +
-                        " - " + res.getStartYear() + "-" + res.getEndYear(),
-                    ComputeType.ADVANCE,
-                    stateId,
-                    hash));
-            }
-            if (res.hasSuspSedimentData()) {
-                newFacets.add(new SedimentLoadFacet(
-                    idx,
-                    SEDIMENT_LOAD_SUSP_SEDIMENT,
-                    Resources.getMsg(
-                        meta,
-                        I18N_FACET_SEDIMENTLOAD_SUSPSEDIMENT,
-                        I18N_FACET_SEDIMENTLOAD_SUSPSEDIMENT) +
-                        " - " + res.getStartYear() + "-" + res.getEndYear(),
-                    ComputeType.ADVANCE,
-                    stateId,
-                    hash));
-
-            }
-            if (res.hasTotalLoadData()) {
-                newFacets.add(new SedimentLoadFacet(
-                    idx,
-                    SEDIMENT_LOAD_TOTAL_LOAD,
-                    Resources.getMsg(
-                        meta,
-                        I18N_FACET_SEDIMENTLOAD_TOTAL_LOAD,
-                        I18N_FACET_SEDIMENTLOAD_TOTAL_LOAD) +
-                        " - " + res.getStartYear() + "-" + res.getEndYear(),
-                    ComputeType.ADVANCE,
-                    stateId,
-                    hash));
-            }
-            if (res.hasTotalData()) {
-                newFacets.add(new SedimentLoadFacet(
-                    idx,
-                    SEDIMENT_LOAD_TOTAL,
-                    Resources.getMsg(
-                        meta,
-                        I18N_FACET_SEDIMENTLOAD_TOTAL,
-                        I18N_FACET_SEDIMENTLOAD_TOTAL) +
-                        " - " + res.getStartYear() + "-" + res.getEndYear(),
-                    ComputeType.ADVANCE,
-                    stateId,
-                    hash));
-            }
-        }
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/minfo/SedimentLoadDistanceSelect.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,13 +0,0 @@
-package de.intevation.flys.artifacts.states.minfo;
-
-import de.intevation.flys.artifacts.states.DistanceOnlySelect;
-
-
-public class SedimentLoadDistanceSelect
-extends DistanceOnlySelect
-{
-    @Override
-    protected String getUIProvider() {
-        return "minfo.sedimentload_distance_select";
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/minfo/SedimentLoadEpochSelect.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,25 +0,0 @@
-package de.intevation.flys.artifacts.states.minfo;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.flys.artifacts.states.DefaultState;
-
-
-public class SedimentLoadEpochSelect
-extends DefaultState
-{
-    /** The logger used in this class. */
-    private static Logger logger = Logger.getLogger(SedimentLoadEpochSelect.class);
-
-
-    /**
-     * The default constructor that initializes an empty State object.
-     */
-    public SedimentLoadEpochSelect() {
-    }
-
-    @Override
-    protected String getUIProvider() {
-        return "minfo.sedimentload_epoch_select";
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/minfo/SedimentLoadOffEpochSelect.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,25 +0,0 @@
-package de.intevation.flys.artifacts.states.minfo;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.flys.artifacts.states.DefaultState;
-
-
-public class SedimentLoadOffEpochSelect
-extends DefaultState
-{
-   /** The logger used in this class. */
-    private static Logger logger = Logger.getLogger(SedimentLoadOffEpochSelect.class);
-
-
-    /**
-     * The default constructor that initializes an empty State object.
-     */
-    public SedimentLoadOffEpochSelect() {
-    }
-
-    @Override
-    protected String getUIProvider() {
-        return "minfo.sedimentload_offepoch_select";
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/minfo/SedimentLoadUnit.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-package de.intevation.flys.artifacts.states.minfo;
-
-import org.apache.log4j.Logger;
-import org.w3c.dom.Element;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.common.utils.XMLUtils;
-import de.intevation.flys.artifacts.resources.Resources;
-import de.intevation.flys.artifacts.states.DefaultState;
-
-
-public class SedimentLoadUnit
-extends DefaultState
-{
-   /** The logger used in this class. */
-    private static Logger logger = Logger.getLogger(SedimentLoadUnit.class);
-
-
-    /**
-     * The default constructor that initializes an empty State object.
-     */
-    public SedimentLoadUnit() {
-    }
-
-    @Override
-    protected String getUIProvider() {
-        return "minfo.sedimentload_unit_select";
-    }
-
-    @Override
-    protected Element[] createItems(
-        XMLUtils.ElementCreator ec,
-        Artifact                artifact,
-        String                  name,
-        CallContext             context)
-    {
-        CallMeta meta = context.getMeta();
-        Element[] elements = new Element[2];
-        elements[0] = createItem(
-                ec,
-                new String[] {
-                    Resources.getMsg(meta,
-                        "state.minfo.t_per_a",
-                        "state.minfo.t_per_a"),
-                    "t_per_a"});
-
-        elements[1] = createItem(
-            ec,
-            new String[] {
-                Resources.getMsg(meta,
-                    "state.minfo.m3_per_a",
-                    "state.minfo.m3_per_a"),
-                "m3_per_a"});
-
-       return elements;
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/minfo/SedimentLoadYearSelect.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,48 +0,0 @@
-package de.intevation.flys.artifacts.states.minfo;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.Artifact;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.flys.artifacts.access.SedimentLoadAccess;
-import de.intevation.flys.artifacts.states.DefaultState;
-
-
-/** State in which to fetch years for sedminent load calculation. */
-public class SedimentLoadYearSelect
-extends DefaultState
-{
-    /** The logger used in this class. */
-    private static Logger logger = Logger.getLogger(SedimentLoadYearSelect.class);
-
-
-    /**
-     * The default constructor that initializes an empty State object.
-     */
-    public SedimentLoadYearSelect() {
-    }
-
-
-    /** Year Select Widget. */
-    @Override
-    protected String getUIProvider() {
-        return "minfo.sedimentload_year_select";
-    }
-
-
-    @Override
-    public boolean validate(Artifact artifact)
-    throws IllegalArgumentException
-    {
-        SedimentLoadAccess access = new SedimentLoadAccess((FLYSArtifact) artifact, null);
-
-        // Second year should be later than first.
-        if (access.getPeriod() != null && access.getPeriod()[1] < access.getPeriod()[0])
-           throw new IllegalArgumentException("error_years_wrong");
-
-        return true;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/minfo/YearEpochSelect.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,72 +0,0 @@
-package de.intevation.flys.artifacts.states.minfo;
-
-import org.w3c.dom.Element;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.common.utils.XMLUtils;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.resources.Resources;
-import de.intevation.flys.artifacts.states.DefaultState;
-
-/**
- * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
- */
-public class YearEpochSelect extends DefaultState {
-
-    /**
-     * The default constructor that initializes an empty State object.
-     */
-    public YearEpochSelect() {
-    }
-
-    @Override
-    protected String getUIProvider() {
-        return "minfo.bed.year_epoch";
-    }
-
-    @Override
-    protected Element[] createItems(
-        XMLUtils.ElementCreator ec,
-        Artifact                artifact,
-        String                  name,
-        CallContext             context)
-    {
-        CallMeta meta = context.getMeta();
-        Element[] elements;
-        if (((FLYSArtifact)artifact).getCurrentStateId()
-            .equals("state.minfo.sediment.load.year_epoch")
-        ) {
-            elements = new Element[3];
-            elements[2] = createItem(
-                ec,
-                new String[] {
-                    Resources.getMsg(meta,
-                        "state.minfo.off_epoch",
-                        "state.minfo.off_epoch"),
-                    "off_epoch"});
-        }
-        else {
-            elements = new Element[2];
-        }
-        elements[0] = createItem(
-                ec,
-                new String[] {
-                    Resources.getMsg(meta,
-                        "state.minfo.year",
-                        "state.minfo.year"),
-                    "year"});
-
-        elements[1] = createItem(
-            ec,
-            new String[] {
-                Resources.getMsg(meta,
-                    "state.minfo.epoch",
-                    "state.minfo.epoch"),
-                "epoch"});
-
-       return elements;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/sq/OutlierMethod.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,76 +0,0 @@
-package de.intevation.flys.artifacts.states.sq;
-
-import org.apache.log4j.Logger;
-import org.w3c.dom.Element;
-
-import de.intevation.artifactdatabase.ProtocolUtils;
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.common.utils.XMLUtils;
-import de.intevation.flys.artifacts.resources.Resources;
-import de.intevation.flys.artifacts.states.DefaultState;
-
-
-public class OutlierMethod
-extends DefaultState
-{
-    /** The logger that is used in this class.*/
-    private static Logger logger = Logger.getLogger(OutlierMethod.class);
-
-    public static final String STD_DEV   = "outlier.method.std-dev";
-    public static final String GRUBBS = "outlier.method.grubbs";
-
-    public static final String[] METHODS = {
-        STD_DEV,
-        GRUBBS,
-  };
-
-
-    @Override
-    protected String getUIProvider() {
-        return "";
-    }
-
-    @Override
-    protected Element[] createItems(
-        XMLUtils.ElementCreator cr,
-        Artifact    artifact,
-        String      name,
-        CallContext context)
-    {
-        CallMeta meta = context.getMeta();
-
-        Element[] methods = new Element[METHODS.length];
-
-        int i = 0;
-
-        for (String method: METHODS) {
-            methods[i++] = createItem(
-                cr, new String[] {
-                    Resources.getMsg(meta, method, method),
-                    method
-                });
-        }
-
-        return methods;
-    }
-
-
-    @Override
-    protected Element createItem(XMLUtils.ElementCreator cr, Object obj) {
-        Element item  = ProtocolUtils.createArtNode(cr, "item", null, null);
-        Element label = ProtocolUtils.createArtNode(cr, "label", null, null);
-        Element value = ProtocolUtils.createArtNode(cr, "value", null, null);
-
-        String[] arr = (String[]) obj;
-
-        label.setTextContent(arr[0]);
-        value.setTextContent(arr[1]);
-
-        item.appendChild(label);
-        item.appendChild(value);
-
-        return item;
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/sq/SQPeriodSelect.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-package de.intevation.flys.artifacts.states.sq;
-
-import de.intevation.flys.artifacts.states.PeriodsSelect;
-
-public class SQPeriodSelect extends PeriodsSelect {
-
-    public static final String UI_PROVIDER = "sq.period.select";
-
-    private static final long serialVersionUID = 1L;
-
-
-    @Override
-    public String getUIProvider() {
-        return UI_PROVIDER;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/sq/SQStaticState.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,80 +0,0 @@
-package de.intevation.flys.artifacts.states.sq;
-
-import java.text.DateFormat;
-import java.util.List;
-
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.access.StaticSQRelationAccess;
-import de.intevation.flys.artifacts.model.sq.StaticSQContainer;
-import de.intevation.flys.artifacts.model.sq.StaticSQFactory;
-import de.intevation.flys.artifacts.model.sq.StaticSQRelation;
-import de.intevation.flys.artifacts.states.StaticState;
-
-
-public class SQStaticState
-extends StaticState
-{
-    private static final Logger log =
-        Logger.getLogger(SQStaticState.class);
-
-    public SQStaticState() {
-        super();
-    }
-
-    public SQStaticState(String name) {
-        super(name);
-    }
-
-    @Override
-    public Object staticCompute(
-        List<Facet> facets,
-        FLYSArtifact artifact
-    ) {
-        StaticSQRelationAccess access = new StaticSQRelationAccess(artifact);
-
-        String river = access.getRiver();
-        String measurementStation = access.getMeasurementStation();
-
-        int ms = -1;
-        try {
-            ms = Integer.parseInt(measurementStation);
-        }
-        catch (NumberFormatException nfe) {
-            log.error("Unparseable measurement station: " + measurementStation);
-            return null;
-        }
-
-        StaticSQContainer sqRelations =
-            StaticSQFactory.getSQRelations(river, ms);
-
-        for (StaticSQRelation.Parameter p: StaticSQRelation.Parameter.values()) {
-
-            List<StaticSQRelation> relations =
-                sqRelations.getRelationsByParameter(p);
-
-            if (!relations.isEmpty()) {
-                int count = 0;
-
-                for (StaticSQRelation relation : relations) {
-                    String name = "sq_" + p.toString().toLowerCase() + "_curve";
-                    DateFormat df =
-                        DateFormat.getDateInstance(DateFormat.SHORT);
-                    String desc = p.toString() + ": " +
-                        df.format(relation.getStartTime()) + " - " +
-                        df.format(relation.getStopTime());
-                    facets.add(new StaticSQRelationFacet(
-                        count,
-                        name,
-                        desc,
-                        relation));
-                    count++;
-                }
-            }
-        }
-        return null;
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/sq/StaticSQRelationFacet.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-package de.intevation.flys.artifacts.states.sq;
-
-import de.intevation.artifactdatabase.state.DefaultFacet;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.flys.artifacts.math.fitting.Function;
-import de.intevation.flys.artifacts.math.fitting.FunctionFactory;
-import de.intevation.flys.artifacts.model.sq.SQFunction;
-import de.intevation.flys.artifacts.model.sq.StaticSQRelation;
-
-
-public class StaticSQRelationFacet
-extends DefaultFacet
-implements Facet
-{
-    public static final String FUNCTION = "sq-pow";
-
-    private StaticSQRelation relation;
-
-
-    public StaticSQRelationFacet(
-        int ndx,
-        String name,
-        String description,
-        StaticSQRelation relation) {
-        super(ndx, name, description);
-        this.relation = relation;
-    }
-
-    @Override
-    public Object getData(Artifact artifact, CallContext context) {
-        double qmax = relation.getQmax();
-        double[] coeffs = new double[] {relation.getA(), relation.getB()};
-        Function func = FunctionFactory.getInstance().getFunction(FUNCTION);
-        de.intevation.flys.artifacts.math.Function function =
-            func.instantiate(coeffs);
-        SQFunction sqf = new SQFunction(function, 0, qmax);
-        return sqf;
-    }
-
-    @Override
-    public Facet deepCopy() {
-        StaticSQRelationFacet copy =
-            new StaticSQRelationFacet(index, name, description, relation);
-        copy.set(this);
-        return copy;
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/transitions/DefaultTransition.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,104 +0,0 @@
-package de.intevation.flys.artifacts.transitions;
-
-import org.w3c.dom.Node;
-
-import de.intevation.artifacts.Artifact;
-
-import de.intevation.artifactdatabase.state.State;
-import de.intevation.artifactdatabase.transition.Transition;
-
-
-/**
- * The default implementation of a <code>Transition</code>.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class DefaultTransition implements Transition {
-
-    /** The ID of the current state */
-    protected String from;
-
-    /** The ID of the target state */
-    protected String to;
-
-
-    /**
-     * The default constructor.
-     */
-    public DefaultTransition() {
-    }
-
-
-    /**
-     * The default constructor.
-     *
-     * @param from The current state.
-     * @param to The target state.
-     */
-    public DefaultTransition(String from, String to) {
-        this.from = from;
-        this.to   = to;
-    }
-
-
-    public void init(Node config) {
-        // nothing to do in the default transition
-    }
-
-
-    /**
-     * Returns the current state ID.
-     *
-     * @return the current state ID.
-     */
-    public String getFrom() {
-        return from;
-    }
-
-
-    /**
-     * Returns the target state ID.
-     *
-     * @return the target state ID.
-     */
-    public String getTo() {
-        return to;
-    }
-
-
-    /**
-     * Set the current state ID.
-     *
-     * @param from current state ID.
-     */
-    public void setFrom(String from) {
-        this.from = from;
-    }
-
-
-    /**
-     * Set the target state ID.
-     *
-     * @param to the target state ID.
-     */
-    public void setTo(String to) {
-        this.to = to;
-    }
-
-
-    /**
-     * Determines if its valid to step from state <i>a</i> of an artifact
-     * <i>artifact</i> to state <i>b</i>. This method always returns true - no
-     * validation takes place.
-     *
-     * @param artifact The owner artifact of state a and b.
-     * @param a The current state.
-     * @param b The target state.
-     *
-     * @return true, if it is valid to step from a to b, otherwise false.
-     */
-    public boolean isValid(Artifact artifact, State a, State b) {
-        return true;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/transitions/TransitionFactory.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,71 +0,0 @@
-package de.intevation.flys.artifacts.transitions;
-
-import javax.xml.xpath.XPathConstants;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Node;
-
-import de.intevation.artifactdatabase.transition.Transition;
-
-import de.intevation.artifacts.common.utils.XMLUtils;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class TransitionFactory {
-
-    /** The logger used in this class */
-    private static Logger logger = Logger.getLogger(TransitionFactory.class);
-
-    /** The XPath to the classname of the transition */
-    public static final String XPATH_TRANSITION = "@transition";
-
-    /** The XPath to the current state ID */
-    public static final String XPATH_CURRENT_STATE = "from/@state";
-
-    /** The XPath to the target state ID */
-    public static final String XPATH_TARGET_STATE = "to/@state";
-
-
-    /**
-     * Creates a new Transition based on the configured class provided by
-     * <code>transitionConf</code>.
-     *
-     * @param transitionConf The configuration of the transition.
-     *
-     * @return a Transition.
-     */
-    public static Transition createTransition(Node transitionConf) {
-        String clazz = (String) XMLUtils.xpath(
-            transitionConf, XPATH_TRANSITION, XPathConstants.STRING);
-
-        Transition transition = null;
-
-        try {
-            transition = (Transition) Class.forName(clazz).newInstance();
-
-            String from = (String) XMLUtils.xpath(
-                transitionConf, XPATH_CURRENT_STATE, XPathConstants.STRING);
-            String to = (String) XMLUtils.xpath(
-                transitionConf, XPATH_TARGET_STATE, XPathConstants.STRING);
-
-            transition.init(transitionConf);
-            transition.setFrom(from);
-            transition.setTo(to);
-        }
-        catch (InstantiationException ie) {
-            logger.error(ie, ie);
-        }
-        catch (IllegalAccessException iae) {
-            logger.error(iae, iae);
-        }
-        catch (ClassNotFoundException cnfe) {
-            logger.error(cnfe, cnfe);
-        }
-
-        return transition;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/transitions/ValueCompareTransition.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,119 +0,0 @@
-package de.intevation.flys.artifacts.transitions;
-
-import javax.xml.xpath.XPathConstants;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Node;
-
-import de.intevation.artifacts.Artifact;
-
-import de.intevation.artifactdatabase.data.StateData;
-import de.intevation.artifactdatabase.state.State;
-
-import de.intevation.artifacts.common.utils.XMLUtils;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-
-/**
- * This transition compares data objects with a <i>equal</i> or <i>notequal</i>
- * operator.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class ValueCompareTransition extends DefaultTransition {
-
-    /** The logger that is used in this transition.*/
-    private static Logger log = Logger.getLogger(ValueCompareTransition.class);
-
-
-    /** XPath that points to the condition's operator.*/
-    public static final String XPATH_OPERATOR = "condition/@operator";
-
-    /** XPath that points to the condition's value.*/
-    public static final String XPATH_VALUE    = "condition/@value";
-
-    /** XPath that points  to the condition's dataname.*/
-    public static final String XPATH_DATANAME = "condition/@data";
-
-    /** The value of the 'equal' operator.*/
-    public static final String OPERATOR_EQUAL = "equal";
-
-    /** The value of the 'not equal' operator.*/
-    public static final String OPERATOR_NOTEQUAL = "notequal";
-
-
-    /** The operator.*/
-    protected String operator;
-
-    /** The value used for comparison.*/
-    protected String value;
-
-    /** The name of the data used for the comparison.*/
-    protected String dataname;
-
-
-
-    public ValueCompareTransition() {
-    }
-
-
-    public ValueCompareTransition(String from, String to) {
-        super(from, to);
-    }
-
-
-    /** Setup member values from the document. */
-    @Override
-    public void init(Node config) {
-        log.debug("ValueCompareTransition.init");
-
-        String tmp = (String) XMLUtils.xpath(
-            config,
-            XPATH_OPERATOR,
-            XPathConstants.STRING);
-        operator = tmp.trim().toLowerCase();
-
-        value = (String) XMLUtils.xpath(
-            config,
-            XPATH_VALUE,
-            XPathConstants.STRING);
-
-        dataname = (String) XMLUtils.xpath(
-            config,
-            XPATH_DATANAME,
-            XPathConstants.STRING);
-    }
-
-
-    @Override
-    public boolean isValid(Artifact artifact, State a, State b) {
-        log.debug("ValueCompareTransition.isValid");
-
-        FLYSArtifact flysArtifact = (FLYSArtifact) artifact;
-
-        StateData dataObj = flysArtifact.getData(dataname);
-        String    dataVal = dataObj != null ? (String) dataObj.getValue() : "";
-
-        if (log.isDebugEnabled()) {
-            log.debug("Compare settings:");
-            log.debug("-- dataname: " + dataname);
-            log.debug("-- operator: " + operator);
-            log.debug("-- compare value: " + value);
-            log.debug("-- state value: " + dataVal);
-        }
-
-        if (operator.equals(OPERATOR_EQUAL)) {
-            return value.equals(dataVal);
-        }
-        else if (operator.equals(OPERATOR_NOTEQUAL)) {
-            return !(value.equals(dataVal));
-        }
-
-        log.error("Wrong operator set! No comparison takes place.");
-
-        return false;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/collections/AttributeParser.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,167 +0,0 @@
-package de.intevation.flys.collections;
-
-import java.util.List;
-import java.util.Map;
-
-import javax.xml.xpath.XPathConstants;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-
-import de.intevation.artifacts.ArtifactNamespaceContext;
-
-import de.intevation.artifactdatabase.state.DefaultOutput;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.Output;
-import de.intevation.artifactdatabase.state.Settings;
-
-import de.intevation.artifacts.common.utils.XMLUtils;
-
-import de.intevation.flys.artifacts.model.ManagedDomFacet;
-import de.intevation.flys.exports.ChartSettings;
-
-/**
- * Access parts of the Attribute parts of a FLYSCollections description
- * document.
- */
-public class AttributeParser {
-
-    /** Constant XPath that points to the outputmodes of an artifact. */
-    public static final String XPATH_ARTIFACT_OUTPUTMODES =
-        "/art:attribute/art:outputs/art:output";
-
-
-    private static Logger logger = Logger.getLogger(AttributeParser.class);
-
-
-    protected Document attributeDocument;
-
-    protected CollectionAttribute attribute;
-
-
-    public AttributeParser(Document attributeDocument) {
-        this.attributeDocument = attributeDocument;
-    }
-
-
-    public void parse() {
-        logger.debug("AttributeParser.parse");
-
-        attribute = new CollectionAttribute();
-
-        NodeList outs = (NodeList) XMLUtils.xpath(
-            attributeDocument,
-            XPATH_ARTIFACT_OUTPUTMODES,
-            XPathConstants.NODESET,
-            ArtifactNamespaceContext.INSTANCE);
-
-        int num = outs != null ? outs.getLength() : 0;
-
-        logger.debug("Attribute has " + num + " outputs.");
-
-        for (int i = 0; i < num; i++) {
-            Node out = outs.item(i);
-
-            parseOutput(out);
-        }
-    }
-
-
-    public CollectionAttribute getCollectionAttribute() {
-        if (attribute == null) {
-            parse();
-        }
-
-        return attribute;
-    }
-
-
-    public Document getAttributeDocument() {
-        return attributeDocument;
-    }
-
-
-    public Map<String, Output> getOuts() {
-        return attribute.getOutputs();
-    }
-
-
-    /**
-     * Access all facets.
-     * @return list of all facets.
-     */
-    public List<Facet> getFacets() {
-        return attribute.getFacets();
-    }
-
-
-    protected void parseOutput(Node out) {
-        String name = ((Element)out).getAttribute("name");
-
-        if (name.length() == 0) {
-            logger.warn("No Output name specified. Cancel parsing!");
-            return;
-        }
-
-        Output o = attribute.getOutput(name);
-
-        if (o == null) {
-            logger.debug("Create new output: " + name);
-
-            o = new DefaultOutput(name, null, null);
-            attribute.addOutput(name, o);
-        }
-
-        parseSettings(out, name);
-        parseItems(out, name);
-    }
-
-    private static final Node getChild(Element element, String name) {
-        NodeList children = element.getChildNodes();
-        for (int i = 0, N = children.getLength(); i < N; ++i) {
-            Node child = children.item(i);
-            if (child.getNodeType() == Node.ELEMENT_NODE
-            && child.getLocalName().equals(name)) {
-                return child;
-            }
-        }
-        return null;
-    }
-
-
-    protected void parseSettings(Node out, String outname) {
-        Node settingsNode = getChild((Element)out, "settings");
-
-        if (settingsNode == null) {
-            logger.debug("No Settings found for Output '" + outname + "'");
-            return;
-        }
-
-        Settings settings = ChartSettings.parse(settingsNode);
-        attribute.setSettings(outname, settings);
-    }
-
-
-    protected void parseItems(Node out, String outname) {
-        String uri = ArtifactNamespaceContext.NAMESPACE_URI;
-        Element element = (Element)out;
-
-        NodeList themes = element.getElementsByTagNameNS(uri, "facet");
-
-        int num = themes.getLength();
-
-        logger.debug("Output has " + num + " themes.");
-
-        for (int i = 0; i < num; i++) {
-            Element theme = (Element) themes.item(i);
-            if (theme.getParentNode() == out) {
-                attribute.addFacet(outname, new ManagedDomFacet(theme));
-            }
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/collections/AttributeWriter.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,313 +0,0 @@
-package de.intevation.flys.collections;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.ArtifactDatabase;
-import de.intevation.artifacts.ArtifactDatabaseException;
-
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.FacetActivity;
-import de.intevation.artifactdatabase.state.Output;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.model.ManagedFacet;
-
-/**
- * Create attribute- element of describe document of an ArtifactCollection.
- * The attribute-element contains the merged output of all outputmodes and
- * facets that are part of the collection.
- */
-public class AttributeWriter {
-
-    /** ArtifactDatabase used to fetch Artifacts. */
-    protected ArtifactDatabase db = null;
-
-    protected Map<String, Output> oldAttr;
-
-    protected Map<String, Output> newAttr;
-
-    /** List of already seen facets. */
-    protected List<Facet>         oldFacets;
-
-    /** List of "new" facets. */
-    protected List<Facet>         newFacets;
-
-    /**
-     * "Compatibility matrix", mapws list of facet names to output names.
-     * Any facet that is not found in the list for a specific output will
-     * not be added to the resulting document.
-     */
-    protected Map<String, List<String>> compatibilities;
-
-
-    /** The result of the <i>write()</i> operation.*/
-    protected CollectionAttribute attribute;
-
-
-    private static Logger logger = Logger.getLogger(AttributeWriter.class);
-
-
-    /**
-     * Create a AttributeWriter.
-     * Attributes not present in newAttr will not be included in the document.
-     * @param db      Database to fetch artifacts.
-     * @param oldAttr "Old" (possibly user-changed) outputs.
-     * @param newAttr "New" (eventually re-read in its original, unchanged
-     *                form) outputs.
-     * @param matrix Compatibility matrix, mapping output names to list of
-     *               facet names that can be included in this out.
-     */
-    public AttributeWriter(
-        ArtifactDatabase    db,
-        CollectionAttribute attribute,
-        Map<String, Output> oldAttr,
-        List<Facet>         oldFacets,
-        Map<String, Output> newAttr,
-        List<Facet>         newFacets,
-        Map<String, List<String>> matrix)
-    {
-        this.db        = db;
-        this.attribute = attribute;
-        this.oldAttr   = oldAttr;
-        this.newAttr   = newAttr;
-        this.oldFacets = oldFacets;
-        this.newFacets = newFacets;
-        this.compatibilities = matrix;
-    }
-
-
-    /**
-     * Create document by merging outputs given in
-     * constructor.
-     *
-     * The "new" set rules about existance of attributes, so anything not
-     * present in it will not be included in the resulting document.
-     * The "old" set rules about the content of attributes (as user changes
-     * are recorded here and not in the new set).
-     *
-     * @return document with merged outputs as described.
-     */
-    protected CollectionAttribute write() {
-
-        boolean debug = logger.isDebugEnabled();
-
-        for (Map.Entry<String, Output> entry: newAttr.entrySet()) {
-            String outName = entry.getKey();
-            Output a       = entry.getValue();
-
-            if (!attribute.hasOutput(outName)) {
-                attribute.addOutput(outName, a);
-            }
-
-            attribute.clearFacets(outName);
-
-            if (debug) {
-                logger.debug("Merge Output: " + outName);
-                logger.debug("   old Facets: " + oldFacets.size());
-                logger.debug("   new Facets: " + newFacets.size());
-            }
-
-            writeOutput(a.getName(), newFacets, oldFacets);
-        }
-
-        // THIS CALL IS ABSOLUTELY NECESSARY!
-        attribute.cleanEmptyOutputs();
-
-        return attribute;
-    }
-
-
-    /**
-     * @param outputName the "new" outputs name
-     * @param newOutFacets Facets of the new outputs
-     * @param oldOutFacets Facets of the old outputs (can be null)
-     */
-    protected void writeOutput(
-        String      outputName,
-        List<Facet> newOutFacets,
-        List<Facet> oldOutFacets
-    ) {
-        List<String> compatFacets = this.compatibilities.get(outputName);
-
-        if (logger.isDebugEnabled() && compatFacets != null) {
-            logger.debug("Compabitle Facets:");
-            for (String compatible: compatFacets) {
-                logger.debug(   "- " + compatible);
-            }
-        }
-
-        try {
-            writeFacets(outputName, newOutFacets, oldOutFacets, compatFacets);
-        }
-        catch (ArtifactDatabaseException ade) {
-            logger.error(ade, ade);
-        }
-    }
-
-
-    /**
-     * @param newFacets the new facets
-     * @param oldFacets the old facets
-     * @param compatibleFacets List of facets to accept
-     * @return true if any facets are written to the out.
-     */
-    protected boolean writeFacets(
-        String        outputName,
-        List<Facet>   newFacets,
-        List<Facet>   oldFacets,
-        List<String>  compatibleFacets)
-    throws ArtifactDatabaseException
-    {
-        if (compatibleFacets == null) {
-            logger.warn("No compatible facets, not generating out " + outputName + ".");
-            return false;
-        }
-
-        int num = newFacets.size();
-
-        // Add all new Facets either in their old state or (if really
-        // new) as they are.
-        List<ManagedFacet> currentFacets      = new ArrayList<ManagedFacet>();
-        List<ManagedFacet> genuinelyNewFacets = new ArrayList<ManagedFacet>();
-
-        boolean debug = logger.isDebugEnabled();
-        if (debug) {
-           logger.debug("Compatible facets are " + compatibleFacets);
-        }
-
-        for (int i = 0; i < num; i++) {
-            ManagedFacet facet = (ManagedFacet) newFacets.get(i);
-
-            if (debug) {
-                logger.debug("Try to add Facet: " + facet.getName());
-            }
-
-            if (!compatibleFacets.contains(facet.getName())) {
-                logger.warn("Have incompatible facet, skip: " + facet.getName());
-                continue;
-            }
-            else {
-                logger.debug("Have compatible facet: " + facet.getName());
-            }
-
-            ManagedFacet picked = pickFacet(facet, oldFacets);
-
-            if (facet.equals(picked)) {
-                genuinelyNewFacets.add(picked);
-            }
-            else {
-                currentFacets.add(picked);
-            }
-        }
-
-        FacetActivity.Registry registry = FacetActivity.Registry.getInstance();
-
-        // With each genuinely new Facet, figure out whether it comes to live
-        // in/activate.
-        for (ManagedFacet newMF: genuinelyNewFacets) {
-            FLYSArtifact flys =
-                (FLYSArtifact)db.getRawArtifact(newMF.getArtifact());
-
-            boolean isActive = registry.isInitialActive(
-                flys.getName(), flys, newMF, outputName);
-
-            newMF.setActive(isActive ? 1 : 0);
-        }
-
-        // For each genuinely new Facet check positional conflicts.
-        for (ManagedFacet newMF: genuinelyNewFacets) {
-            boolean conflicts = true;
-            // Loop until all conflicts resolved.
-            while (conflicts) {
-                conflicts = false;
-                for (ManagedFacet oldMF: currentFacets) {
-                    if (newMF.getPosition() == oldMF.getPosition()) {
-                        conflicts = true;
-                        if (debug) {
-                            logger.debug(
-                                "Positional conflict while merging " +
-                                "facets, pushing newest facet 1 up (" +
-                                newMF.getPosition() + ")");
-                        }
-                        newMF.setPosition(newMF.getPosition() + 1);
-                        break;
-                    }
-                }
-            }
-            currentFacets.add(newMF);
-        }
-
-        // Fill/correct "gaps" (e.g. position 1,2,5 are taken, after gap filling
-        // expect positions 1,2,3 [5->3])
-        // Preparations to be able to detect gaps.
-        Map<Integer, ManagedFacet> mfmap =
-            new HashMap<Integer, ManagedFacet>();
-        int max = 0;
-        for (ManagedFacet mf: currentFacets) {
-            int pos = mf.getPosition();
-            mfmap.put(Integer.valueOf(pos), mf);
-            if (pos > max) max = pos;
-        }
-
-        // Finally do gap correction.
-        if (max != currentFacets.size()) {
-            int gap = 0;
-            for (int i = 1; i <= max; i++) {
-                ManagedFacet mf = mfmap.get(Integer.valueOf(i));
-                if (mf == null) {
-                    gap++;
-                    continue;
-                }
-                mf.setPosition(mf.getPosition() - gap);
-            }
-        }
-
-        // Now add all facets.
-        for (ManagedFacet oldMF: currentFacets) {
-            attribute.addFacet(outputName, oldMF);
-        }
-
-        return !currentFacets.isEmpty();
-    }
-
-
-    /**
-     * Returns the facet to be added to Document.
-     * Return the new facet only if the "same" facet was not present before.
-     * Return the "old" facet otherwise (user-defined information sticks
-     * to it).
-     * @param facet     the new facet.
-     * @param oldFacets the old facets, new facet is compared against each of
-     *                  these.
-     * @return facet if genuinely new, matching old facet otherwise.
-     */
-    protected ManagedFacet pickFacet(ManagedFacet facet, List<Facet> oldFacets)
-    {
-        if (oldFacets == null) {
-            logger.debug("No old facets to compare a new to found.");
-            return facet;
-        }
-
-        String hash = facet.getName() + facet.getIndex() + facet.getArtifact();
-
-        // Compare "new" facet with all old facets.
-        // Take oldFacet if that facet was already present (otherwise
-        // information is lost, the new one otherwise.
-        for (Facet oFacet: oldFacets) {
-            ManagedFacet oldFacet = (ManagedFacet) oFacet;
-            String oldHash = oldFacet.getName()
-                           + oldFacet.getIndex()
-                           + oldFacet.getArtifact();
-            if (hash.equals(oldHash)) {
-                return oldFacet;
-            }
-        }
-        return facet;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/collections/CollectionAttribute.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,313 +0,0 @@
-package de.intevation.flys.collections;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.ArtifactNamespaceContext;
-
-import de.intevation.artifacts.common.utils.XMLUtils;
-import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
-
-import de.intevation.artifactdatabase.state.DefaultOutput;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.Output;
-import de.intevation.artifactdatabase.state.Settings;
-
-
-/** Create attribute part of collection document. */
-public class CollectionAttribute {
-
-    /** Privately owned logger. */
-    private static final Logger logger =
-        Logger.getLogger(CollectionAttribute.class);
-
-    protected ElementCreator ec;
-
-    protected Map<String, Output> outputMap;
-
-    protected Node loadedRecommendations;
-
-
-    public CollectionAttribute() {
-    }
-
-
-    public void addOutput(String key, Output output) {
-        if (outputMap == null) {
-            outputMap = new HashMap<String, Output>();
-        }
-
-        if (key != null && key.length() > 0 && output != null) {
-            outputMap.put(
-                key,
-                new DefaultOutput(
-                    output.getName(),
-                    output.getDescription(),
-                    output.getMimeType(),
-                    new ArrayList<Facet>(),
-                    output.getType()));
-        }
-    }
-
-
-    public void cleanEmptyOutputs() {
-        if (outputMap == null) {
-            return;
-        }
-
-        List<String> removeUs = new ArrayList<String>();
-
-        for (Map.Entry<String, Output> entry: outputMap.entrySet()) {
-            Output o = entry.getValue();
-
-            List<Facet> facets = o.getFacets();
-            if (facets == null || facets.isEmpty()) {
-                removeUs.add(entry.getKey());
-            }
-        }
-
-        for (String key: removeUs) {
-            outputMap.remove(key);
-        }
-    }
-
-
-    public void setSettings(String outputKey, Settings settings) {
-        if (settings == null) {
-            logger.warn("Tried to set empty Settings for '" + outputKey + "'");
-            return;
-        }
-
-        if (outputMap == null) {
-            logger.warn("Tried to add facet 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);
-            return;
-        }
-
-        output.setSettings(settings);
-    }
-
-
-    public void addFacet(String outputKey, Facet facet) {
-        if (facet == null) {
-            logger.warn("Tried to add empty facet.");
-            return;
-        }
-
-        if (outputMap == null) {
-            logger.warn("Tried to add facet 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);
-            return;
-        }
-
-        logger.debug("Add facet for '" + outputKey + "': " + facet.getName());
-        output.addFacet(facet);
-    }
-
-
-    public void setLoadedRecommendations(Node loadedRecommendations) {
-        // TODO Replace this Node with a Java class object.
-        this.loadedRecommendations = loadedRecommendations;
-    }
-
-
-    public void clearFacets(String outputKey) {
-        if (outputKey == null || outputKey.length() == 0) {
-            logger.warn("Tried to clear Facets, but no Output key specified!");
-            return;
-        }
-
-        if (outputMap == null) {
-            logger.warn("Tried to clear Facets, but no Outputs existing!");
-            return;
-        }
-
-        Output output = outputMap.get(outputKey);
-        if (output == null) {
-            logger.warn("Tried to clear Facets for unknown Out: " + outputKey);
-            return;
-        }
-
-        output.setFacets(new ArrayList<Facet>());
-    }
-
-
-    public Document toXML() {
-        Document doc = XMLUtils.newDocument();
-
-        ec = new ElementCreator(
-            doc,
-            ArtifactNamespaceContext.NAMESPACE_URI,
-            ArtifactNamespaceContext.NAMESPACE_PREFIX);
-
-        Element root = ec.create("attribute");
-
-        appendOutputs(root);
-        appendLoadedRecommendations(root);
-
-        doc.appendChild(root);
-
-        return doc;
-    }
-
-    /** True if output with outputName is found. */
-    public boolean hasOutput(String outputName) {
-        return getOutput(outputName) != null;
-    }
-
-    public Map<String, Output> getOutputs() {
-        return outputMap;
-    }
-
-
-    public Output getOutput(String name) {
-        if (name == null || name.length() == 0) {
-            logger.warn("No Output name specified.");
-            return null;
-        }
-
-        if (outputMap == null || outputMap.isEmpty()) {
-            logger.warn("Tried to retrieve Output, but no Outputs existing.");
-            return null;
-        }
-
-        return outputMap.get(name);
-    }
-
-
-    public List<Facet> getFacets(String output) {
-        if (output == null || output.length() == 0) {
-            logger.warn("No Output name specified.");
-            return new ArrayList<Facet>();
-        }
-
-        if (outputMap == null) {
-            logger.warn("Tried to retrieve facets, but no Outputs existing.");
-            return new ArrayList<Facet>();
-        }
-
-        Output o = outputMap.get(output);
-
-        if (o == null) {
-            logger.warn("No Output '" + output + "' existing.");
-            return new ArrayList<Facet>();
-        }
-
-        return o.getFacets();
-    }
-
-
-    public List<Facet> getFacets() {
-        List<Facet> allFacets = new ArrayList<Facet>();
-
-        if (outputMap == null || outputMap.isEmpty()) {
-            logger.warn("No Outputs existing.");
-            return allFacets;
-        }
-
-        for (String outputName: outputMap.keySet()) {
-            allFacets.addAll(getFacets(outputName));
-        }
-
-        return allFacets;
-    }
-
-
-    protected void appendOutputs(Element root) {
-        if (outputMap == null || outputMap.isEmpty()) {
-            logger.warn("No outputs to append.");
-            return;
-        }
-
-        logger.debug("Append " + outputMap.size() + " Output Elements.");
-
-        Element outputsEl = ec.create("outputs");
-
-        for (Map.Entry<String, Output> entry: outputMap.entrySet()) {
-            appendOutput(outputsEl, entry.getKey(), entry.getValue());
-        }
-
-        root.appendChild(outputsEl);
-    }
-
-
-    protected void appendOutput(Element root, String name, Output output) {
-        if (name == null || name.length() == 0 || output == null) {
-            logger.warn("Tried to appendOutput, but Output is invalid.");
-            return;
-        }
-
-        logger.debug("Append Output Element for '" + name + "'");
-
-        Element outputEl = ec.create("output");
-        ec.addAttr(outputEl, "name", name);
-
-        appendSettings(outputEl, output.getSettings());
-        appendFacets(outputEl, output.getFacets());
-
-        root.appendChild(outputEl);
-    }
-
-
-    protected void appendSettings(Element root, Settings settings) {
-        if (settings == null) {
-            logger.warn("Tried to append Settings, but Settings is empty!");
-            return;
-        }
-
-        settings.toXML(root);
-    }
-
-
-    protected void appendFacets(Element root, List<Facet> facets) {
-        if (facets == null || facets.isEmpty()) {
-            logger.warn("Tried to append 0 Facets.");
-            return;
-        }
-
-        Document owner = root.getOwnerDocument();
-
-        logger.debug("Append " + facets.size() + " facets.");
-
-        for (Facet facet: facets) {
-            Node facetNode = facet.toXML(owner);
-
-            if (facetNode != null) {
-                root.appendChild(facetNode);
-            }
-        }
-    }
-
-
-    protected void appendLoadedRecommendations(Element root) {
-        if (loadedRecommendations == null) {
-            logger.debug("No loaded recommendations existing yet.");
-            return;
-        }
-
-        Document owner = root.getOwnerDocument();
-
-        root.appendChild(owner.importNode(loadedRecommendations, true));
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/collections/CollectionDescriptionHelper.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,203 +0,0 @@
-package de.intevation.flys.collections;
-
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-
-import javax.xml.xpath.XPathConstants;
-
-import org.apache.log4j.Logger;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-
-import de.intevation.artifacts.ArtifactDatabase;
-import de.intevation.artifacts.ArtifactDatabaseException;
-import de.intevation.artifacts.ArtifactNamespaceContext;
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.common.utils.XMLUtils;
-import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
-
-
-public class CollectionDescriptionHelper {
-
-    private static final Logger logger =
-        Logger.getLogger(CollectionDescriptionHelper.class);
-
-
-    public static final String XPATH_ARTIFACT_STATE_DATA =
-        "/art:result/art:ui/art:static/art:state/art:data";
-
-    /** Constant XPath that points to the outputmodes of an artifact. */
-    public static final String XPATH_ARTIFACT_OUTPUTMODES =
-        "/art:result/art:outputmodes";
-
-
-    protected ElementCreator ec;
-
-    protected CallContext      context;
-    protected ArtifactDatabase database;
-
-    protected String name;
-    protected String uuid;
-    protected Date   creation;
-    protected long   ttl;
-
-    protected List<String>        artifacts;
-    protected CollectionAttribute attribute;
-
-
-    /**
-     * @param name The name of the collection.
-     * @param uuid The uuid of the collection.
-     * @param creation The creation time of the collection.
-     * @param ttl The time to live of the collection.
-     */
-    public CollectionDescriptionHelper(
-        String      name,
-        String      uuid,
-        Date        creation,
-        long        ttl,
-        CallContext callContext
-    ) {
-        this.name      = name;
-        this.uuid      = uuid;
-        this.creation  = creation;
-        this.ttl       = ttl;
-        this.context   = callContext;
-        this.database  = callContext.getDatabase();
-        this.artifacts = new ArrayList<String>();
-    }
-
-
-    public void addArtifact(String uuid) {
-        if (uuid != null && uuid.length() > 0) {
-            artifacts.add(uuid);
-        }
-    }
-
-
-    public void setAttribute(CollectionAttribute attribute) {
-        if (attribute != null) {
-            this.attribute = attribute;
-        }
-    }
-
-
-    public Document toXML() {
-        Document doc = XMLUtils.newDocument();
-
-        ec = new ElementCreator(
-            doc,
-            ArtifactNamespaceContext.NAMESPACE_URI,
-            ArtifactNamespaceContext.NAMESPACE_PREFIX);
-
-        Element root = ec.create("artifact-collection");
-        doc.appendChild(root);
-
-        String creationTime = creation != null
-            ? Long.toString(creation.getTime())
-            : "";
-
-        ec.addAttr(root, "name", name, true);
-        ec.addAttr(root, "uuid", uuid, true);
-        ec.addAttr(root, "creation", creationTime, true);
-        ec.addAttr(root, "ttl", String.valueOf(ttl), true);
-
-        appendArtifacts(root);
-        appendAttribute(root);
-
-        return doc;
-    }
-
-
-    /**
-     * Appends parts of the DESCRIBE document of each Artifact to <i>root</i>.
-     *
-     * @param root The root node.
-     */
-    protected void appendArtifacts(Element root) {
-        Element artifactsEl = ec.create("artifacts");
-
-        for (String uuid: artifacts) {
-            try {
-                Element e = buildArtifactNode(uuid);
-
-                if (e != null) {
-                    artifactsEl.appendChild(e);
-                }
-            }
-            catch (ArtifactDatabaseException dbe) {
-                logger.warn(dbe, dbe);
-            }
-        }
-
-        root.appendChild(artifactsEl);
-    }
-
-
-    /**
-     * Create the Artifacts Node that contains outputmode and statedata.
-     *
-     * @param uuid uuid of the artifact.
-     */
-    protected Element buildArtifactNode(String uuid)
-    throws    ArtifactDatabaseException
-    {
-        logger.debug("Append artifact '" + uuid + "' to collection description");
-
-        // TODO
-        String hash = "MYHASH";
-
-        Element ci = ec.create("artifact");
-        ec.addAttr(ci, "uuid", uuid, true);
-        ec.addAttr(ci, "hash", hash, true);
-
-        // XXX I am not sure if it works well every time with an empty document
-        // in the describe operation of an artifact.
-        Document description = database.describe(uuid, null, context.getMeta());
-
-        // Add outputmode element(s).
-        Node outputModes = (Node) XMLUtils.xpath(
-            description,
-            XPATH_ARTIFACT_OUTPUTMODES,
-            XPathConstants.NODE,
-            ArtifactNamespaceContext.INSTANCE);
-
-        if (outputModes != null) {
-            Document doc = ci.getOwnerDocument();
-            ci.appendChild(doc.importNode(outputModes, true));
-        }
-
-        // Add state-data element(s).
-        Node dataNode = ci.appendChild(
-            ci.getOwnerDocument().createElement("art:data-items"));
-
-        NodeList dataNodes = (NodeList) XMLUtils.xpath(
-            description,
-            XPATH_ARTIFACT_STATE_DATA,
-            XPathConstants.NODESET,
-            ArtifactNamespaceContext.INSTANCE);
-
-        if (dataNodes != null) {
-            Document doc = ci.getOwnerDocument();
-            for (int i = 0, D = dataNodes.getLength(); i < D; i++) {
-                dataNode.appendChild(doc.importNode(dataNodes.item(i), true));
-            }
-        }
-
-        return ci;
-    }
-
-
-    protected void appendAttribute(Element root) {
-        if (attribute != null) {
-            Document owner = root.getOwnerDocument();
-            Document attr  = attribute.toXML();
-
-            root.appendChild(owner.importNode(attr.getFirstChild(), true));
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,625 +0,0 @@
-package de.intevation.flys.collections;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.xml.xpath.XPathConstants;
-
-import org.apache.log4j.Logger;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-
-import de.intevation.artifactdatabase.Backend;
-import de.intevation.artifactdatabase.Backend.PersistentArtifact;
-import de.intevation.artifactdatabase.DefaultArtifactCollection;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.Output;
-import de.intevation.artifactdatabase.state.Settings;
-import de.intevation.artifactdatabase.state.StateEngine;
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.ArtifactDatabase;
-import de.intevation.artifacts.ArtifactDatabaseException;
-import de.intevation.artifacts.ArtifactNamespaceContext;
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.common.utils.XMLUtils;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.context.FLYSContext;
-import de.intevation.flys.exports.OutGenerator;
-import de.intevation.flys.exports.OutputHelper;
-import de.intevation.flys.utils.FLYSUtils;
-
-/**
- * Collection of artifacts, can do outs, describe.
- * Lots of stuff done in AttributeParser and AttributeWriter.
- * Critical out and facet merging.
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class FLYSArtifactCollection extends DefaultArtifactCollection {
-    /** The logger used in this class. */
-    private static Logger log = Logger.getLogger(FLYSArtifactCollection.class);
-
-    /** Constant XPath that points to the outputmodes of an artifact. */
-    public static final String XPATH_ARTIFACT_OUTPUTMODES =
-        "/art:result/art:outputmodes";
-
-    public static final String XPATH_ARTIFACT_STATE_DATA =
-        "/art:result/art:ui/art:static/art:state/art:data";
-
-    public static final String XPATH_COLLECTION_ITEMS =
-        "/art:result/art:artifact-collection/art:collection-item";
-
-    public static final String XPATH_OUT_NAME = "/art:action/@art:name";
-
-    public static final String XPATH_OUT_TYPE = "/art:action/@art:type";
-
-    /** Xpath to master artifacts uuid. */
-    public static final String XPATH_MASTER_UUID =
-        "/art:artifact-collection/art:artifact/@art:uuid";
-
-    public static final String XPATH_LOADED_RECOMMENDATIONS =
-        "/art:attribute/art:loaded-recommendations";
-
-
-    /**
-     * Create and return description Document for this collection.
-     */
-    @Override
-    public Document describe(CallContext context) {
-        log.debug("FLYSArtifactCollection.describe: " + identifier);
-
-        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);
-
-            oldAttrs = removeAttributes(oldAttrs, context);
-            parser   = new AttributeParser(oldAttrs);
-
-            CollectionAttribute newAttr = mergeAttributes(
-                db, context, parser, aUUIDs);
-
-            if (checkOutputSettings(newAttr, context)) {
-                saveCollectionAttribute(db, context, newAttr);
-            }
-
-            helper.setAttribute(newAttr);
-
-            if (aUUIDs != null) {
-                for (String uuid: aUUIDs) {
-                    helper.addArtifact(uuid);
-                }
-            }
-        }
-        catch (ArtifactDatabaseException ade) {
-            log.error("Error while merging attribute documents.", ade);
-
-            helper.setAttribute(parser.getCollectionAttribute());
-        }
-
-        return helper.toXML();
-    }
-
-
-    /**
-     * Merge the current art:outputs nodes with the the outputs provided by the
-     * artifacts in the Collection.
-     *
-     * @param uuids Artifact uuids.
-     */
-    protected CollectionAttribute mergeAttributes(
-        ArtifactDatabase db,
-        CallContext      context,
-        AttributeParser  oldParser,
-        String[]         uuids
-    ) {
-        CollectionAttribute cAttribute =
-            buildOutAttributes(db, context, oldParser, uuids);
-
-        if (cAttribute == null) {
-            log.warn("mergeAttributes: cAttribute == null");
-            return null;
-        }
-
-        cAttribute.setLoadedRecommendations(
-            getLoadedRecommendations(oldParser.getAttributeDocument()));
-
-        saveCollectionAttribute(db, context, cAttribute);
-
-        return cAttribute;
-    }
-
-
-    protected Document removeAttributes(Document attrs, CallContext context) {
-        Node outs = (Node) XMLUtils.xpath(
-            attrs,
-            "/art:attribute/art:outputs",
-            XPathConstants.NODE,
-            ArtifactNamespaceContext.INSTANCE);
-
-        NodeList nodes = (NodeList) XMLUtils.xpath(
-            attrs,
-            "/art:attribute/art:outputs/art:output",
-            XPathConstants.NODESET,
-            ArtifactNamespaceContext.INSTANCE);
-
-        if (nodes != null) {
-            for (int i = 0; i < nodes.getLength(); i++) {
-                Element e = (Element)nodes.item(i);
-                if(!outputExists(e.getAttribute("name"), context)) {
-                    outs.removeChild(e);
-                }
-            }
-        }
-        return attrs;
-    }
-
-
-    /**
-     * True if current MasterArtifact has given output.
-     * @param name Name of the output of interest.
-     * @param context current context
-     * @return true if current master artifact has given output.
-     */
-    protected boolean outputExists(String name, CallContext context) {
-        FLYSArtifact master = getMasterArtifact(context);
-        List<Output> outList = master.getOutputs(context);
-
-        for (Output o : outList) {
-            if (name.equals(o.getName())) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * @param db The ArtifactDatabase which is required to save the attribute
-     * into.
-     * @param attribute The CollectionAttribute that should be stored in the
-     * database.
-     *
-     * @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.");
-
-        Document doc = attribute.toXML();
-
-        try {
-            // Save the merged document into database.
-            db.setCollectionAttribute(identifier(), context.getMeta(), doc);
-
-            log.info("Saving CollectionAttribute was successful.");
-
-            return true;
-        }
-        catch (ArtifactDatabaseException adb) {
-            log.error(adb, adb);
-        }
-
-        return false;
-    }
-
-
-    /**
-     * Merge the recommendations which have already been loaded from the old
-     * attribute document into the new attribute document. This is necessary,
-     * because mergeAttributes() only merges the art:outputs nodes - all
-     * other nodes are skipped.
-     */
-    protected Node getLoadedRecommendations(Document oldAttrs) {
-        Element loadedRecoms = (Element) XMLUtils.xpath(
-            oldAttrs,
-            XPATH_LOADED_RECOMMENDATIONS,
-            XPathConstants.NODE,
-            ArtifactNamespaceContext.INSTANCE);
-
-        return loadedRecoms;
-    }
-
-
-    /**
-     * Evaluates the Output settings. If an Output has no Settings set, the
-     * relevant OutGenerator is used to initialize a default Settings object.
-     *
-     * @param attribute The CollectionAttribute.
-     * @param cc The CallContext.
-     *
-     * @return true, if the CollectionAttribute was modified, otherwise false.
-     */
-    protected boolean checkOutputSettings(
-        CollectionAttribute attribute,
-        CallContext         cc
-    ) {
-        boolean modified = false;
-
-        Map<String, Output> outputMap = attribute != null
-            ? attribute.getOutputs()
-            : null;
-
-        if (outputMap == null || outputMap.isEmpty()) {
-            log.debug("No Output Settings check necessary.");
-            return modified;
-        }
-
-
-        for (Map.Entry<String, Output> entry: outputMap.entrySet()) {
-            String outName = entry.getKey();
-            Output output  = entry.getValue();
-
-            if (outName.equals("sq_overview")) {
-                continue;
-            }
-            Settings settings = output.getSettings();
-
-            if (settings == null) {
-                log.debug("No Settings set for Output '" + outName + "'.");
-                output.setSettings(
-                    createInitialOutputSettings(cc, attribute, outName));
-
-                modified = true;
-            }
-        }
-
-        return modified;
-    }
-
-
-    /**
-     * This method uses the the OutGenerator for the specified Output
-     * <i>out</i> to create an initial Settings object.
-     *
-     * @param cc The CallContext object.
-     * @param attr The CollectionAttribute.
-     * @param out The name of the output.
-     *
-     * @return a default Settings object for the specified Output.
-     */
-    protected Settings createInitialOutputSettings(
-        CallContext         cc,
-        CollectionAttribute attr,
-        String              out
-    ) {
-        OutGenerator outGen = FLYSContext.getOutGenerator(cc, out, null);
-
-        if (outGen == null) {
-            return null;
-        }
-
-        // 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);
-
-        try {
-            Document outAttr = getAttribute(cc, attr, out);
-            OutputHelper helper = new OutputHelper(identifier());
-            helper.doOut(outGen, out, out, outAttr, cc);
-        }
-        catch (ArtifactDatabaseException adbe) {
-            log.error(adbe, adbe);
-        }
-        catch (IOException ioe) {
-            log.error(ioe, ioe);
-        }
-
-        return outGen.getSettings();
-    }
-
-
-    @Override
-    public void out(
-        String       type,
-        Document     format,
-        OutputStream out,
-        CallContext  context)
-    throws IOException
-    {
-        boolean debug = log.isDebugEnabled();
-
-        long reqBegin = System.currentTimeMillis();
-
-        if (debug) {
-            log.debug(XMLUtils.toString(format));
-            log.debug("FLYSArtifactCollection.out");
-        }
-
-        String name = XMLUtils.xpathString(
-            format, XPATH_OUT_NAME, ArtifactNamespaceContext.INSTANCE);
-
-        String subtype = XMLUtils.xpathString(
-            format, XPATH_OUT_TYPE, ArtifactNamespaceContext.INSTANCE);
-
-        if (debug) {
-            log.debug("-> Output name = " + name);
-            log.debug("-> Output type = " + type);
-            log.debug("-> Output subtype = " + subtype);
-        }
-
-        OutGenerator generator = null;
-        if (type != null
-             && type.length() > 0
-             && type.indexOf("chartinfo") > 0)
-        {
-            generator = FLYSContext.getOutGenerator(context, type, subtype);
-        }
-        else {
-            generator = FLYSContext.getOutGenerator(context, name, subtype);
-        }
-
-        if (generator == null) {
-            log.error("There is no generator specified for output: " + name);
-            // TODO Throw an exception.
-
-            return;
-        }
-
-        Document        oldAttrs  = getAttribute();
-        AttributeParser parser    = new AttributeParser(oldAttrs);
-        CollectionAttribute cAttr = parser.getCollectionAttribute();
-
-        Output output = cAttr.getOutput(name);
-        Settings settings = null;
-        if (output != null) {
-            settings = output.getSettings();
-
-            if (debug) {
-                List<Facet> facets = output.getFacets();
-                for(Facet facet: facets) {
-                    log.debug("  -- Facet " + facet.getName());
-                }
-            }
-        }
-
-        generator.init(format, out, context);
-        generator.setSettings(settings);
-        generator.setCollection(this);
-        prepareMasterArtifact(generator, context);
-
-        try {
-            Document attr = getAttribute(context, cAttr, name);
-            OutputHelper helper = new OutputHelper(identifier());
-            if (name.equals("sq_overview")) {
-                helper.doOut(generator, name, subtype, format, context);
-            }
-            helper.doOut(generator, name, subtype, attr, context);
-            generator.generate();
-        }
-        catch (ArtifactDatabaseException adbe) {
-            log.error(adbe, adbe);
-        }
-
-        if (debug) {
-            long duration = System.currentTimeMillis() -reqBegin;
-            log.info("Processing out(" + name + ") took " + duration + " ms.");
-        }
-    }
-
-
-    /**
-     * 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
-    ) {
-        // Get master artifact.
-        FLYSArtifact master = getMasterArtifact(cc);
-        if (master != null) {
-            log.debug("Set master Artifact to uuid: " + master.identifier());
-            generator.setMasterArtifact(master);
-        }
-        else {
-            log.warn("Could not set master artifact.");
-        }
-    }
-
-
-    /**
-     * @return masterartifact or null if exception/not found.
-     */
-    protected FLYSArtifact getMasterArtifact(CallContext context)
-    {
-        try {
-            ArtifactDatabase db = context.getDatabase();
-            CallMeta callMeta   = context.getMeta();
-            Document document   = db.getCollectionsMasterArtifact(
-                identifier(), callMeta);
-
-            String masterUUID   = XMLUtils.xpathString(
-                document, XPATH_MASTER_UUID, ArtifactNamespaceContext.INSTANCE);
-            FLYSArtifact masterArtifact =
-                (FLYSArtifact) getArtifact(masterUUID, context);
-            return masterArtifact;
-        }
-        catch (ArtifactDatabaseException ade) {
-            log.error(ade, ade);
-        }
-        return null;
-    }
-
-
-    /**
-     * Return merged output document.
-     * @param uuids List of artifact uuids.
-     */
-    protected CollectionAttribute buildOutAttributes(
-        ArtifactDatabase db,
-        CallContext      context,
-        AttributeParser  aParser,
-        String[]         uuids)
-    {
-        FLYSContext flysContext = FLYSUtils.getFlysContext(context);
-        StateEngine engine = (StateEngine) flysContext.get(
-            FLYSContext.STATE_ENGINE_KEY);
-
-        if (engine == null) {
-            log.error("buildOutAttributes: engine == null");
-            return null;
-        }
-
-        FLYSArtifact masterArtifact = getMasterArtifact(context);
-
-        if (masterArtifact == null) {
-            log.debug("buildOutAttributes: masterArtifact == null");
-            return null;
-        }
-
-        OutputParser oParser = new OutputParser(db, context);
-
-        if (uuids != null) {
-            for (String uuid: uuids) {
-                try {
-                    oParser.parse(uuid);
-                }
-                catch (ArtifactDatabaseException ade) {
-                    log.warn(ade, ade);
-                }
-            }
-        }
-
-        aParser.parse();
-
-        AttributeWriter aWriter = new AttributeWriter(
-            db,
-            aParser.getCollectionAttribute(),
-            aParser.getOuts(),
-            aParser.getFacets(),
-            oParser.getOuts(),
-            oParser.getFacets(),
-            engine.getCompatibleFacets(masterArtifact.getStateHistoryIds())
-            );
-        return aWriter.write();
-    }
-
-
-    /**
-     * Returns the "attribute" (part of description document) for a specific
-     * output type.
-     *
-     * @param context The CallContext object.
-     * @param cAttr The CollectionAttribute.
-     * @param output The name of the desired output type.
-     *
-     * @return the attribute for the desired output type.
-     */
-    protected Document getAttribute(
-        CallContext         context,
-        CollectionAttribute cAttr,
-        String              output)
-    throws    ArtifactDatabaseException
-    {
-        Document attr = cAttr.toXML();
-
-        Map<String, String> vars = new HashMap<String, String>();
-        vars.put("output", output);
-
-        Node out = (Node) XMLUtils.xpath(
-            attr,
-            "art:attribute/art:outputs/art:output[@name=$output]",
-            XPathConstants.NODE,
-            ArtifactNamespaceContext.INSTANCE,
-            vars);
-
-
-        if (out != null) {
-            Document o = XMLUtils.newDocument();
-
-            o.appendChild(o.importNode(out, true));
-
-            return o;
-        }
-
-        return null;
-    }
-
-
-    /**
-     * This method returns the list of artifact UUIDs that this collections
-     * contains.
-     *
-     * @param context The CallContext that is necessary to get information about
-     * the ArtifactDatabase.
-     *
-     * @return a list of uuids.
-     */
-    protected String[] getArtifactUUIDs(CallContext context)
-    throws    ArtifactDatabaseException
-    {
-        log.debug("FLYSArtifactCollection.getArtifactUUIDs");
-
-        ArtifactDatabase db = context.getDatabase();
-        CallMeta meta       = context.getMeta();
-
-        Document itemList = db.listCollectionArtifacts(identifier(), meta);
-        NodeList items    = (NodeList) XMLUtils.xpath(
-            itemList,
-            XPATH_COLLECTION_ITEMS,
-            XPathConstants.NODESET,
-            ArtifactNamespaceContext.INSTANCE);
-
-        if (items == null || items.getLength() == 0) {
-            log.debug("No artifacts found in this collection.");
-            return null;
-        }
-
-        int num = items.getLength();
-
-        List<String> uuids = new ArrayList<String>(num);
-
-        for (int i = 0; i < num; i++) {
-            String uuid = XMLUtils.xpathString(
-                items.item(i),
-                "@art:uuid",
-                ArtifactNamespaceContext.INSTANCE);
-
-            if (uuid != null && uuid.trim().length() != 0) {
-                uuids.add(uuid);
-            }
-        }
-
-        return uuids.toArray(new String[uuids.size()]);
-    }
-
-
-    /**
-     * Returns a concrete Artifact of this collection specified by its uuid.
-     *
-     * @param uuid The Artifact's uuid.
-     * @param context The CallContext.
-     *
-     * @return an Artifact.
-     */
-    protected Artifact getArtifact(String uuid, CallContext context)
-    throws    ArtifactDatabaseException
-    {
-        log.debug("FLYSArtifactCollection.getArtifact");
-
-        Backend backend               = Backend.getInstance();
-        PersistentArtifact persistent = backend.getArtifact(uuid);
-
-        return persistent != null ? persistent.getArtifact() : null;
-    }
-
-
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/collections/OutputParser.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,139 +0,0 @@
-package de.intevation.flys.collections;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.ArtifactDatabase;
-import de.intevation.artifacts.ArtifactDatabaseException;
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.artifactdatabase.state.DefaultOutput;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.Output;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.model.ManagedFacetAdapter;
-
-
-/**
- * The OutputParsers task is to pull Artifacts from database and put
- * its outputs and facets into some structures.
- */
-public class OutputParser {
-
-    /** Constant XPath that points to the outputmodes of an artifact. */
-    public static final String XPATH_ARTIFACT_OUTPUTMODES =
-        "/art:result/art:outputmodes/art:output";
-
-    private static Logger logger = Logger.getLogger(OutputParser.class);
-
-    protected ArtifactDatabase db;
-    protected CallMeta         meta;
-    protected CallContext      context;
-
-    /** Map outputs name to Output. */
-    protected Map<String, Output> outs;
-
-    /** Map facets name to list of Facets. */
-    protected List<Facet> facets;
-
-
-    /**
-     * @param db Database used to fetch artifacts, outputs and facets.
-     */
-    public OutputParser(ArtifactDatabase db, CallContext context) {
-        this.db      = db;
-        this.meta    = context.getMeta();
-        this.context = context;
-        this.outs    = new HashMap<String, Output>();
-        this.facets  = new ArrayList<Facet>();
-    }
-
-
-    /**
-     * Gets raw artifact with given id and sorts outputs in mapping.
-     * Converts Facets to ManagedFacets on the way.
-     * @param uuid uuid of artifact to load from database.
-     */
-    public void parse(String uuid)
-    throws ArtifactDatabaseException
-    {
-        logger.debug("OutputParser.parse: " + uuid);
-
-        FLYSArtifact flys = (FLYSArtifact) db.getRawArtifact(uuid);
-
-        List<Output> outList = flys.getOutputs(context);
-
-        logger.debug("   has " + outList.size() + " Outputs.");
-
-        for (Output out: outList) {
-            String name = out.getName();
-            logger.debug("Process Output '" + name + "'");
-
-            Output o = outs.get(name);
-            int  pos = 1;
-
-            if (o == null) {
-                o = new DefaultOutput(
-                    out.getName(),
-                    out.getDescription(),
-                    out.getMimeType(),
-                    new ArrayList<Facet>(),
-                    out.getType());
-                outs.put(name, o);
-            }
-            else {
-                logger.debug("OutputParser.parse: Use 'old' Output");
-                pos = o.getFacets().size() + 1;
-            }
-
-            List<Facet> mfacets = facet2ManagedFacet(uuid, out.getFacets(), pos);
-            o.addFacets(mfacets);
-            this.facets.addAll(mfacets);
-        }
-    }
-
-
-    /**
-     * Access mapping of Outputname to Output.
-     */
-    public Map<String, Output> getOuts() {
-        return outs;
-    }
-
-
-    /**
-     * Access all facets.
-     */
-    public List<Facet> getFacets() {
-        return this.facets;
-    }
-
-
-    /**
-     * Creates a list of ManagedFacets from list of Facets.
-     * @param pos Position of first facet (for each other the positions
-     *            will be increased).
-     */
-    protected List<Facet> facet2ManagedFacet(
-        String      uuid,
-        List<Facet> old,
-        int         pos)
-    {
-        List<Facet> newFacets = new ArrayList<Facet>(old.size());
-
-        logger.debug("There are " + old.size() + " Facets for this Output.");
-
-        for (Facet f: old) {
-            newFacets.add(new ManagedFacetAdapter(f, uuid, pos++, 1, 1));
-        }
-
-        return newFacets;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/ATExporter.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,145 +0,0 @@
-package de.intevation.flys.exports;
-
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.IOException;
-
-import org.w3c.dom.Document;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.artifactdatabase.state.ArtifactAndFacet;
-import de.intevation.artifactdatabase.state.Settings;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.flys.artifacts.model.WQ;
-import de.intevation.flys.collections.FLYSArtifactCollection;
-
-import de.intevation.flys.model.Gauge;
-import de.intevation.flys.model.River;
-import de.intevation.flys.model.TimeInterval;
-import de.intevation.flys.utils.FLYSUtils;
-import de.intevation.flys.artifacts.access.RangeAccess;
-
-public class ATExporter
-implements   OutGenerator
-{
-    private static Logger logger = Logger.getLogger(ATExporter.class);
-
-    public static final String DEFAULT_ENCODING = "UTF-8";
-
-    protected WQ           data;
-    protected CallContext  context;
-    protected OutputStream out;
-    protected FLYSArtifact master;
-
-    protected FLYSArtifactCollection collection;
-
-
-    public ATExporter() {
-    }
-
-    @Override
-    public void init(Document request, OutputStream out, CallContext context) {
-        this.context = context;
-        this.out     = out;
-    }
-
-
-    @Override
-    public void setMasterArtifact(Artifact master) {
-        this.master = (FLYSArtifact) master;
-    }
-
-    @Override
-    public void setCollection(FLYSArtifactCollection collection) {
-        this.collection = collection;
-    }
-
-    @Override
-    public void doOut(
-        ArtifactAndFacet artifactf,
-        Document attr,
-        boolean  visible
-    ) {
-        data = (WQ)artifactf.getData(context);
-    }
-
-    @Override
-    public void generate() throws IOException {
-
-        if (data == null) {
-            logger.debug("no W/Q data");
-            return;
-        }
-
-        ATWriter at;
-        try {
-            at = new ATWriter(data);
-        }
-        catch (IllegalArgumentException iae) {
-            logger.error("creating ATWriter failed", iae);
-            throw new IOException(iae);
-        }
-
-        River river = FLYSUtils.getRiver(master);
-        RangeAccess rangeAccess = new RangeAccess(master, context);
-        double[] kms = rangeAccess.getLocations();
-
-        Gauge gauge = river.determineGaugeByPosition(kms[0]);
-        if (Math.abs(kms[0] - gauge.getStation().doubleValue()) < 1e-4) {
-            // at gauge.
-            TimeInterval interval =
-                gauge.fetchMasterDischargeTable().getTimeInterval();
-            at.write(
-                new OutputStreamWriter(out, DEFAULT_ENCODING),
-                context.getMeta(),
-                river.getName(),
-                kms[0],
-                gauge.getName(),
-                gauge.getDatum(),
-                interval.getStartTime(),
-                100d);
-        }
-        else {
-            // at km
-            at.write(
-                new OutputStreamWriter(out),
-                context.getMeta(),
-                river.getName(),
-                kms[0],
-                null,
-                null,
-                null,
-                0d);
-        }
-
-    }
-
-
-    /**
-     * Returns an instance of <i>EmptySettings</i> currently!
-     *
-     * @return an instance of <i>EmptySettings</i>.
-     */
-    @Override
-    public Settings getSettings() {
-        return new EmptySettings();
-    }
-
-
-    /**
-     * This method is not implemented!
-     *
-     * @param settings A settings object.
-     */
-    @Override
-    public void setSettings(Settings settings) {
-        // do nothing here
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/ATWriter.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,204 +0,0 @@
-package de.intevation.flys.exports;
-
-import java.io.IOException;
-import java.io.Writer;
-import java.io.PrintWriter;
-import java.math.BigDecimal;
-
-import java.text.DateFormat;
-import java.util.Date;
-import java.util.Locale;
-
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.flys.artifacts.model.WQ;
-import de.intevation.flys.artifacts.resources.Resources;
-
-import org.apache.commons.math.analysis.UnivariateRealFunction;
-
-import org.apache.commons.math.analysis.interpolation.SplineInterpolator;
-import org.apache.commons.math.analysis.interpolation.LinearInterpolator;
-
-import org.apache.commons.math.analysis.polynomials.PolynomialFunction;
-
-import org.apache.commons.math.FunctionEvaluationException;
-
-import org.apache.log4j.Logger;
-
-/** Write AT files. */
-public class ATWriter
-{
-    private static Logger logger = Logger.getLogger(ATWriter.class);
-
-    public static final int COLUMNS = 10;
-
-    public static final String I18N_AT_HEADER =
-        "export.discharge.curve.at.header";
-
-    public static final String I18N_AT_GAUGE_HEADER =
-        "export.discharge.curve.at.gauge.header";
-
-    public static final String EMPTY = "         ";
-
-    protected double minW;
-    protected double maxW;
-    protected double minQ;
-    protected double maxQ;
-
-    protected UnivariateRealFunction qFunc;
-
-    public ATWriter() {
-    }
-
-    public ATWriter(WQ wq) throws IllegalArgumentException {
-
-        int [] bounds = wq.longestIncreasingWRangeIndices();
-
-        if (logger.isDebugEnabled()) {
-            logger.debug("exporting w between indices " +
-                bounds[0] + " and " + bounds[1] + " (" +
-                wq.getW(bounds[0]) + ", " + wq.getW(bounds[1]));
-        }
-
-        if (bounds[1]-bounds[0] < 1) { // Only first w can be written out.
-            minW = maxW = wq.getW(bounds[0]);
-            minQ = maxQ = wq.getQ(bounds[0]);
-            // constant function
-            qFunc = new PolynomialFunction(new double [] { minQ });
-            return;
-        }
-
-        double [] ws = new double[bounds[1]-bounds[0]];
-        double [] qs = new double[ws.length];
-
-        for (int i = 0; i < ws.length; ++i) {
-            int idx = bounds[0]+i;
-            ws[i] = wq.getW(idx);
-            qs[i] = wq.getQ(idx);
-        }
-
-        qFunc = ws.length < 3
-            ? new LinearInterpolator().interpolate(ws, qs)
-            : new SplineInterpolator().interpolate(ws, qs);
-
-        minW = wq.getW(bounds[0]);
-        maxW = wq.getW(bounds[1]);
-        minQ = wq.getQ(bounds[0]);
-        maxQ = wq.getQ(bounds[1]);
-    }
-
-    public double getQ(double w) {
-
-        try {
-            return qFunc.value(w);
-        }
-        catch (FunctionEvaluationException aode) {
-            // should not happen
-            logger.warn("spline interpolation failed", aode);
-            return w <= minW ? minQ : maxQ;
-        }
-    }
-
-    public static void printQ(PrintWriter out, double q) {
-        String format;
-             if (q <   1d) format = " % 8.3f";
-        else if (q <  10d) format = " % 8.2f";
-        else if (q < 100d) format = " % 8.1f";
-        else {
-            format = " % 8.0f";
-            if (q > 1000d) q = Math.rint(q/10d)*10d;
-        }
-        out.printf(Locale.US, format, q);
-    }
-
-
-    protected static void printGaugeHeader(
-        PrintWriter out,
-        CallMeta    callMeta,
-        String      river,
-        double      km,
-        String      gName,
-        BigDecimal  datum,
-        Date        date
-    ) {
-        DateFormat f = DateFormat.getDateInstance();
-        out.print(Resources.getMsg(
-            callMeta,
-            I18N_AT_GAUGE_HEADER,
-            I18N_AT_GAUGE_HEADER,
-            new Object[] { river, gName, f.format(date), datum } ));
-        out.print("\r\n");
-    }
-
-    protected static void printHeader(
-        PrintWriter out,
-        CallMeta    callMeta,
-        String      river,
-        double      km
-    ) {
-        out.print(Resources.getMsg(
-            callMeta,
-            I18N_AT_HEADER,
-            I18N_AT_HEADER,
-            new Object[] { river, km } ));
-        out.print("\r\n");
-    }
-
-    public void write(
-        Writer writer,
-        CallMeta meta,
-        String river,
-        double km,
-        String gName,
-        BigDecimal datum,
-        Date date,
-        double scale)
-    throws IOException
-    {
-        PrintWriter out = new PrintWriter(writer);
-
-        // A header is required, because the desktop version of FLYS will skip
-        // the first row.
-        if (gName != null) {
-            printGaugeHeader(out, meta, river, km, gName, datum, date);
-        }
-        else {
-            printHeader(out, meta, river, km);
-        }
-
-        double rest = (minW * 100.0) % 10.0;
-
-        double startW = Math.rint((minW - rest*0.01)*10.0)*0.1;
-
-        if (logger.isDebugEnabled()) {
-            logger.debug("startW: " + startW);
-            logger.debug("rest: " + rest);
-        }
-
-        int col = 0;
-        for (double w = startW; w <= maxW; w += 0.01) {
-            if (col == 0) {
-                out.printf(Locale.US, "%8d", (int)Math.round(w * scale));
-            }
-
-            if (w < minW) {
-                out.print(EMPTY);
-            }
-            else {
-                printQ(out, getQ(w));
-            }
-
-            if (++col >= COLUMNS) {
-                out.print("\r\n");
-                col = 0;
-            }
-        }
-
-        if (col > 0) {
-            out.print("\r\n");
-        }
-
-        out.flush();
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/AbstractExporter.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,346 +0,0 @@
-package de.intevation.flys.exports;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-
-import java.text.NumberFormat;
-
-import org.w3c.dom.Document;
-
-import org.apache.log4j.Logger;
-
-import au.com.bytecode.opencsv.CSVWriter;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.artifactdatabase.state.ArtifactAndFacet;
-import de.intevation.artifactdatabase.state.Settings;
-
-import de.intevation.artifacts.common.ArtifactNamespaceContext;
-import de.intevation.artifacts.common.utils.XMLUtils;
-
-import de.intevation.flys.artifacts.resources.Resources;
-import de.intevation.flys.collections.FLYSArtifactCollection;
-
-import de.intevation.flys.utils.Formatter;
-
-
-/**
- * An abstract exporter that implements some basic methods for exporting data of
- * artifacts.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public abstract class AbstractExporter implements OutGenerator {
-
-    /** The logger used in this exporter.*/
-    private static Logger logger = Logger.getLogger(AbstractExporter.class);
-
-    /* XXX: Why does AbstractExporter do not implement FacetTypes? */
-    public static String FIX_PARAMETERS = "fix_parameters";
-
-    /** The name of the CSV facet which triggers the CSV creation. */
-    public static final String FACET_CSV = "csv";
-
-    /** The name of the PDF facet which triggers the PDF creation. */
-    public static final String FACET_PDF = "pdf";
-
-    /** The default charset for the CSV export. */
-    public static final String DEFAULT_CSV_CHARSET = "UTF-8";
-
-    /** The default separator for the CSV export. */
-    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 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;
-
-    /** The selected facet. */
-    protected String facet;
-
-    /** The collection.*/
-    protected FLYSArtifactCollection collection;
-
-    /** The master artifact. */
-    protected Artifact master;
-
-    private NumberFormat kmFormat;
-
-    private NumberFormat wFormat;
-
-    private NumberFormat qFormat;
-
-
-    /**
-     * Concrete subclasses need to use this method to write their special data
-     * objects into the CSV document.
-     *
-     * @param writer The CSVWriter.
-     */
-    protected abstract void writeCSVData(CSVWriter writer) throws IOException;
-
-
-    /**
-     * Concrete subclasses need to use this method to write their special data
-     * objects into the PDF document.
-     */
-    protected abstract void writePDF(OutputStream out);
-
-
-    /**
-     * This method enables concrete subclasses to collected its own special
-     * data.
-     *
-     * @param data The artifact that stores the data that has to be
-     * exported.
-     */
-    protected abstract void addData(Object data);
-
-
-    @Override
-    public void init(Document request, OutputStream out, CallContext context) {
-        logger.debug("AbstractExporter.init");
-
-        this.request = request;
-        this.out     = out;
-        this.context = context;
-    }
-
-
-    @Override
-    public void setMasterArtifact(Artifact master) {
-        this.master = master;
-    }
-
-    /** Get the callcontext that this exporter has been initialized
-     * with. */
-    public CallContext getCallContext() {
-        return this.context;
-    }
-
-
-    @Override
-    public void setCollection(FLYSArtifactCollection collection) {
-        this.collection = collection;
-    }
-
-
-    /**
-     * This doOut() just collects the data of multiple artifacts. Therefore, it
-     * makes use of the addData() method which enables concrete subclasses to
-     * store its data on its own. The real output creation takes place in the
-     * concrete generate() methods.
-     *
-     * @param artifactFacet The artifact and facet.
-     * The facet to add - NOTE: the facet needs to fit to the first
-     * facet inserted into this exporter. Otherwise this artifact/facet is
-     * skipped.
-     * @param attr The attr document.
-     */
-    @Override
-    public void doOut(
-        ArtifactAndFacet artifactFacet,
-        Document         attr,
-        boolean          visible
-    ) {
-        String name = artifactFacet.getFacetName();
-
-        logger.debug("AbstractExporter.doOut: " + name);
-
-        if (!isFacetValid(name)) {
-            logger.warn("Facet '" + name + "' not valid. No output created!");
-            return;
-        }
-
-        addData(artifactFacet.getData(context));
-    }
-
-
-    /**
-     * Generates an export based on a specified facet.
-     */
-    @Override
-    public void generate()
-    throws IOException
-    {
-        logger.debug("AbstractExporter.generate");
-
-        if (facet == null) {
-            throw new IOException("invalid (null) facet for exporter");
-        }
-
-        if (facet.equals(FACET_CSV)) {
-            generateCSV();
-        }
-        else if (facet.equals(FACET_PDF)) {
-            generatePDF();
-        }
-        else {
-            throw new IOException(
-                "invalid facet for exporter: '" + facet + "'");
-        }
-    }
-
-
-    /**
-     * Determines if the desired facet is valid for this exporter. If no facet
-     * is currently set, <i>facet</i> is set.
-     *
-     * @param facet The desired facet.
-     *
-     * @return true, if <i>facet</i> is valid, otherwise false.
-     */
-    protected boolean isFacetValid(String facet) {
-        logger.debug("AbstractExporter.isFacetValid : " + facet + " (" + getFacet() + ")" );
-
-        String thisFacet = getFacet();
-
-        if (thisFacet == null || thisFacet.length() == 0) {
-            return false;
-        }
-        else if (facet == null || facet.length() == 0) {
-            return false;
-        }
-        else {
-            return thisFacet.equals(facet);
-        }
-    }
-
-
-    /**
-     * Returns the name of the desired facet.
-     *
-     * @return the name of the desired facet.
-     */
-    protected String getFacet() {
-        if (facet == null) {
-            facet = getFacetFromRequest();
-        }
-
-        return facet;
-    }
-
-
-    /**
-     * Extracts the name of the requested facet from request document.
-     *
-     * @return the name of the requested facet.
-     */
-    protected String getFacetFromRequest() {
-        return XMLUtils.xpathString(
-            request, XPATH_FACET, ArtifactNamespaceContext.INSTANCE);
-    }
-
-
-    protected String msg(String key, String def) {
-        return Resources.getMsg(context.getMeta(), key, def);
-    }
-
-    protected String msg(String key, String def, Object[] args) {
-        return Resources.getMsg(context.getMeta(), key, def, args);
-    }
-
-
-    /**
-     * This method starts CSV creation. It makes use of writeCSVData() which has
-     * to be implemented by concrete subclasses.
-     */
-    protected void generateCSV()
-    throws    IOException
-    {
-        logger.info("AbstractExporter.generateCSV");
-
-        char quote = '"';
-        char escape = '\\';
-
-        CSVWriter writer = new CSVWriter(
-            new OutputStreamWriter(
-                out,
-                DEFAULT_CSV_CHARSET),
-            DEFAULT_CSV_SEPARATOR, quote, escape, "\r\n");
-
-        writeCSVData(writer);
-
-        writer.close();
-    }
-
-
-    /**
-     * This method starts PDF creation.
-     */
-    protected void generatePDF()
-    throws    IOException
-    {
-        logger.info("AbstractExporter.generatePDF");
-        writePDF(this.out);
-    }
-
-
-    /**
-     * Returns an instance of <i>EmptySettings</i> currently!
-     *
-     * @return an instance of <i>EmptySettings</i>.
-     */
-    public Settings getSettings() {
-        return new EmptySettings();
-    }
-
-
-    /**
-     * This method is not implemented. Override it in subclasses if those need a
-     * <i>Settings</i> object.
-     */
-    public void setSettings(Settings settings) {
-        // do nothing
-    }
-
-
-    /**
-     * Returns the number formatter for kilometer values.
-     *
-     * @return the number formatter for kilometer values.
-     */
-    protected NumberFormat getKmFormatter() {
-        if (kmFormat == null) {
-            kmFormat = Formatter.getWaterlevelKM(context);
-        }
-        return kmFormat;
-    }
-
-
-    /**
-     * Returns the number formatter for W values.
-     *
-     * @return the number formatter for W values.
-     */
-    protected NumberFormat getWFormatter() {
-        if (wFormat == null) {
-            wFormat = Formatter.getWaterlevelW(context);
-        }
-        return wFormat;
-    }
-
-
-    /**
-     * Returns the number formatter for Q values.
-     *
-     * @return the number formatter for Q values.
-     */
-    protected NumberFormat getQFormatter() {
-        if (qFormat == null) {
-            qFormat = Formatter.getWaterlevelQ(context);
-        }
-        return qFormat;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/AxisSection.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,106 +0,0 @@
-package de.intevation.flys.exports;
-
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-
-import de.intevation.artifactdatabase.state.Attribute;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class AxisSection extends TypeSection {
-
-    public static final String IDENTIFIER_ATTR = "id";
-    public static final String LABEL_ATTR      = "label";
-    public static final String FONTSIZE_ATTR   = "font-size";
-    public static final String FIXATION_ATTR   = "fixation";
-    public static final String UPPERRANGE_ATTR = "upper";
-    public static final String LOWERRANGE_ATTR = "lower";
-
-
-    public AxisSection() {
-        super("axis");
-    }
-
-
-    public void setIdentifier(String identifier) {
-        setStringValue(IDENTIFIER_ATTR, identifier);
-    }
-
-
-    public String getIdentifier() {
-        return getStringValue(IDENTIFIER_ATTR);
-    }
-
-
-    public void setLabel(String label) {
-        setStringValue(LABEL_ATTR, label);
-    }
-
-
-    public String getLabel() {
-        return getStringValue(LABEL_ATTR);
-    }
-
-
-    public void setFontSize(int fontSize) {
-        if (fontSize <= 0) {
-            return;
-        }
-
-        setIntegerValue(FONTSIZE_ATTR, fontSize);
-    }
-
-
-    public Integer getFontSize() {
-        return getIntegerValue(FONTSIZE_ATTR);
-    }
-
-
-    public void setFixed(boolean fixed) {
-        setBooleanValue(FIXATION_ATTR, fixed);
-    }
-
-
-    public Boolean isFixed() {
-        return getBooleanValue(FIXATION_ATTR);
-    }
-
-
-    public void setUpperRange(double upperRange) {
-        setDoubleValue(UPPERRANGE_ATTR, upperRange);
-    }
-
-
-    public Double getUpperRange() {
-        return getDoubleValue(UPPERRANGE_ATTR);
-    }
-
-
-    public void setLowerRange(double lowerRange) {
-        setDoubleValue(LOWERRANGE_ATTR, lowerRange);
-    }
-
-
-    public Double getLowerRange() {
-        return getDoubleValue(LOWERRANGE_ATTR);
-    }
-
-
-    @Override
-    public void toXML(Node parent) {
-        Document owner = parent.getOwnerDocument();
-        Element   axis = owner.createElement("axis");
-
-        parent.appendChild(axis);
-
-        for (String key: getKeys()) {
-            Attribute attr = getAttribute(key);
-            attr.toXML(axis);
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/BooleanAttribute.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,35 +0,0 @@
-package de.intevation.flys.exports;
-
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class BooleanAttribute extends VisibleAttribute {
-
-
-    public BooleanAttribute(String name, boolean value, boolean visible) {
-        super(name, value, visible);
-    }
-
-
-    /**
-     * Calls VisibleAttribute.toXML() and appends afterwards an attribute
-     * <i>type</i> with value <i>boolean</i>.
-     *
-     * @param parent The parent Node.
-     *
-     * @return the new Node that represents this Attribute.
-     */
-    @Override
-    public Node toXML(Node parent) {
-
-        Element ele = (Element) super.toXML(parent);
-        ele.setAttribute("type", "boolean");
-
-        return ele;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/ChartArea.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,57 +0,0 @@
-package de.intevation.flys.exports;
-
-import org.jfree.chart.axis.ValueAxis;
-
-import org.jfree.data.Range;
-
-/** Two Ranges that span a rectangular area. */
-public class ChartArea {
-    protected Range xRange;
-    protected Range yRange;
-
-    public ChartArea(Range rangeX, Range rangeY) {
-        this.xRange = rangeX;
-        this.yRange = rangeY;
-    }
-
-    public ChartArea(ValueAxis axisX, ValueAxis axisY) {
-        this.xRange = axisX.getRange();
-        this.yRange = axisY.getRange();
-    }
-
-    public double ofLeft(double percent) {
-        return xRange.getLowerBound()
-            + xRange.getLength() * percent;
-    }
-
-    public double ofRight(double percent) {
-        return xRange.getUpperBound()
-            - xRange.getLength() * percent;
-    }
-
-    public double ofGround(double percent) {
-        return yRange.getLowerBound()
-            + yRange.getLength() * percent;
-    }
-
-    public double atTop() {
-        return yRange.getUpperBound();
-    }
-
-    public double atGround() {
-        return yRange.getLowerBound();
-    }
-
-    public double atRight() {
-        return xRange.getUpperBound();
-    }
-
-    public double atLeft() {
-        return xRange.getLowerBound();
-    }
-
-    public double above(double percent, double base) {
-        return base + yRange.getLength() * percent;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/ChartExportHelper.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,408 +0,0 @@
-/*
- * Copyright (c) 2011 by Intevation GmbH
- *
- * This program is free software under the LGPL (>=v2.1)
- * Read the file LGPL.txt coming with the software for details
- * or visit http://www.gnu.org/licenses/ if it does not exist.
- */
-package de.intevation.flys.exports;
-
-import com.lowagie.text.Document;
-import com.lowagie.text.DocumentException;
-import com.lowagie.text.PageSize;
-import com.lowagie.text.Rectangle;
-
-import com.lowagie.text.pdf.PdfContentByte;
-import com.lowagie.text.pdf.PdfTemplate;
-import com.lowagie.text.pdf.PdfWriter;
-
-import java.awt.Graphics2D;
-import java.awt.Transparency;
-
-import java.awt.geom.Rectangle2D.Double;
-
-import java.awt.geom.Rectangle2D;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.UnsupportedEncodingException;
-import org.jfree.chart.ChartRenderingInfo;
-
-import javax.imageio.ImageIO;
-
-import au.com.bytecode.opencsv.CSVWriter;
-
-import org.apache.batik.svggen.SVGGraphics2D;
-import org.apache.batik.svggen.SVGGraphics2DIOException;
-
-import org.apache.log4j.Logger;
-
-import org.jfree.chart.JFreeChart;
-import org.jfree.chart.plot.XYPlot;
-import org.jfree.data.xy.XYDataset;
-
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.artifacts.common.utils.XMLUtils;
-
-
-/**
- * This class is a helper class which supports some methods to export charts
- * into specific formats.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class ChartExportHelper {
-
-    public static final String FORMAT_PNG = "png";
-
-    public static final String FORMAT_PDF = "pdf";
-
-    public static final String FORMAT_SVG = "svg";
-
-    public static final String FORMAT_CSV = "csv";
-
-    /**
-     * Constant field to define A4 as default page size.
-     */
-    public static final String  DEFAULT_PAGE_SIZE = "A4";
-
-    /**
-     * Constant field to define UTF-8 as default encoding.
-     */
-    public static final String  DEFAULT_ENCODING  = "UTF-8";
-
-    /** The default separator for the CSV export. */
-    public static final char DEFAULT_CSV_SEPARATOR = ',';
-
-
-    /**
-     * Logger used for logging with log4j.
-     */
-    private static Logger log = Logger.getLogger(ChartExportHelper.class);
-
-
-    /**
-     * A method to export a <code>JFreeChart</code> as image to an
-     * <code>OutputStream</code> with a given format, width and height.
-     *
-     * @param out OutputStream
-     * @param chart JFreeChart object to be exported.
-     * @param cc context, in which e.g. size is stored.
-     *
-     * @throws IOException if writing image to OutputStream failed.
-     */
-    public static void exportImage(
-        OutputStream out,
-        JFreeChart   chart,
-        CallContext  cc
-    )
-    throws IOException
-    {
-        log.info("export chart as png");
-
-        ChartRenderingInfo info = new ChartRenderingInfo();
-
-        String format = (String) cc.getContextValue("chart.image.format");
-
-        int[] size = getSize(cc);
-
-        ImageIO.write(
-            chart.createBufferedImage(
-                size[0], size[1], Transparency.BITMASK, info
-            ),
-            format,
-            out
-        );
-    }
-
-
-    /**
-     * A method to export a <code>JFreeChart</code> as SVG to an
-     * <code>OutputStream</code>.
-     *
-     * @param out OutputStream
-     * @param chart JFreeChart to be exported
-     * @param context The CallContext object that contains extra chart
-     * parameters.
-     */
-    public static void exportSVG(
-        OutputStream out,
-        JFreeChart   chart,
-        CallContext  context
-    ) {
-        String encoding = (String) context.getContextValue("chart.encoding");
-
-        log.info("export chart as svg");
-
-        if (encoding == null)
-            encoding = DEFAULT_ENCODING;
-
-        org.w3c.dom.Document document = XMLUtils.newDocument();
-        SVGGraphics2D        graphics = new SVGGraphics2D(document);
-
-        int[] size = getSize(context);
-
-        ChartRenderingInfo info = new ChartRenderingInfo();
-
-        chart.draw(graphics, new Rectangle2D.Double(0.0D, 0.0D,size[0],size[1]), info);
-
-        try {
-            graphics.stream(new OutputStreamWriter(out, encoding));
-        }
-        catch (SVGGraphics2DIOException svge) {
-            log.error("Error while writing svg export to output stream.", svge);
-        }
-        catch (UnsupportedEncodingException uee) {
-            log.error("Unsupported encoding: " + encoding, uee);
-        }
-    }
-
-
-    /**
-     * A method to export a <code>JFreeChart</code> as PDF to an
-     * <code>OutputStream</code>.
-     *
-     * @param out OutputStream
-     * @param chart JFreeChart
-     */
-    public static void exportPDF(
-        OutputStream out,
-        JFreeChart   chart,
-        CallContext  cc
-    ) {
-        log.info("export chart as pdf.");
-
-        String pageFormat = (String) cc.getContextValue("chart.page.format");
-
-        if (pageFormat == null)
-            pageFormat = DEFAULT_PAGE_SIZE;
-
-        // Max size of the chart.
-        Rectangle page = PageSize.getRectangle(pageFormat);
-        float pageWidth  = page.getWidth();
-        float pageHeight = page.getHeight();
-
-        // The chart width.
-        int[] size = getSize(cc);
-
-        boolean landscape = size[0] > size[1];
-
-        float width  = 0;
-        float height = 0;
-        if (landscape) {
-            width  = pageHeight;
-            height = pageWidth;
-        }
-        else {
-            width  = pageWidth;
-            height = pageHeight;
-        }
-
-        float marginLeft = (Float) cc.getContextValue(
-            "chart.marginLeft");
-
-        float marginRight = (Float) cc.getContextValue(
-            "chart.marginRight");
-
-        float marginTop = (Float) cc.getContextValue(
-            "chart.marginTop");
-
-        float marginBottom = (Float) cc.getContextValue(
-            "chart.marginBottom");
-
-        float spaceX = width  - marginLeft - marginRight;
-        if (size[0] > spaceX) {
-            log.warn("Width of the chart is too big for pdf -> resize it now.");
-            double ratio = ((double)spaceX) / size[0];
-            size[0]  *= ratio;
-            size[1] *= ratio;
-            log.debug("Resized chart to " + size[0] + "x" + size[1]);
-        }
-
-        float spaceY = height - marginTop  - marginBottom;
-        if (size[1] > spaceY) {
-            log.warn("Height of the chart is too big for pdf -> resize it now.");
-            double ratio = ((double)spaceY) / size[1];
-            size[0]  *= ratio;
-            size[1] *= ratio;
-            log.debug("Resized chart to " + size[0] + "x" + size[1]);
-        }
-
-        Document document = null;
-        if (landscape) {
-            document = new Document(page.rotate());
-            log.debug("Create landscape pdf.");
-        }
-        else
-            document = new Document(page);
-
-        try {
-            PdfWriter writer = PdfWriter.getInstance(document, out);
-
-            document.addSubject(chart.getTitle().getText());
-            document.addCreationDate();
-            document.open();
-
-            PdfContentByte content  = writer.getDirectContent();
-
-            PdfTemplate template = content.createTemplate(width, height);
-            Graphics2D  graphics = template.createGraphics(width, height);
-
-            double[] origin = getCenteredAnchor(
-                marginLeft, marginRight, marginBottom, marginTop,
-                width, height,
-                size[0], size[1]);
-
-            Rectangle2D area = new Rectangle2D.Double(
-                origin[0], origin[1], size[0], size[1]);
-
-            ChartRenderingInfo info = new ChartRenderingInfo();
-
-            chart.draw(graphics, area, info);
-            graphics.dispose();
-            content.addTemplate(template, 0f, 0f);
-        }
-        catch (DocumentException de) {
-            log.error("Error while exporting chart to pdf.", de);
-        }
-        finally {
-            document.close();
-        }
-    }
-
-
-    /**
-     * A method to export a CSV file to an
-     * <code>OutputStream</code>.
-     *
-     * @param out OutputStream
-     * @param chart JFreeChart containing the data.
-     * @param context The CallContext object that contains extra parameters.
-     */
-    public static void exportCSV(
-        OutputStream out,
-        JFreeChart chart,
-        CallContext context)
-    {
-        log.debug("export chart as CSV");
-        CSVWriter writer = null;
-        try {
-            writer = new CSVWriter(
-                new OutputStreamWriter(
-                    out,
-                    DEFAULT_ENCODING),
-                DEFAULT_CSV_SEPARATOR);
-        }
-        catch(UnsupportedEncodingException uee) {
-            log.warn("Wrong encoding for CSV export.");
-            return;
-        }
-        XYPlot plot = chart.getXYPlot();
-        int count = plot.getDatasetCount();
-        for (int i = 0; i < count; i++) {
-            XYDataset data = plot.getDataset(i);
-            int scount = data.getSeriesCount();
-            for (int j = 0; j < scount; j++) {
-                Comparable seriesKey = data.getSeriesKey(j);
-                log.debug("series key: " + seriesKey.toString());
-                writeCSVHeader(writer, seriesKey.toString());
-                writeCSVData(writer, data);
-            }
-        }
-        try {
-            writer.close();
-        }
-        catch(IOException ioe) {
-            log.error("Writing CSV export failed!");
-        }
-    }
-
-
-    protected static void writeCSVHeader(CSVWriter writer, String key) {
-        writer.writeNext(new String[] {"#"});
-        writer.writeNext(new String[] {"# " + key});
-        writer.writeNext(new String[] {"#"});
-        writer.writeNext(new String[] {"X", "Y"});
-    }
-
-
-    protected static void writeCSVData(CSVWriter writer, XYDataset data) {
-        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));
-                writer.writeNext(new String[] {
-                    data.getX(i, j).toString(),
-                    data.getY(i, j).toString()});
-            }
-        }
-    }
-
-
-    public static int[] getSize(CallContext cc) {
-        int[] size = new int[2];
-
-        size[0] = (Integer) cc.getContextValue("chart.width");
-        size[1] = (Integer) cc.getContextValue("chart.height");
-
-        return size;
-    }
-
-
-    /**
-     * This method returns the anchor of the chart so that the chart is centered
-     * according to the given parameters.
-     *
-     * @param mLeft Left margin
-     * @param mRight Right margin
-     * @param mBottom Bottom margin
-     * @param mTop Top margin
-     * @param width The complete width of the drawing area.
-     * @param height The complete height of the drawing area.
-     * @param chartWidth The width of the chart.
-     * @param chartHeight The height of the chart.
-     *
-     * @return an array that contains the anchor for a chart with the given
-     * parameters. The first value is the x point, the second value is the y
-     * point.
-     */
-    public static double[] getCenteredAnchor(
-        double mLeft,      double mRight,      double mBottom, double mTop,
-        double width,      double height,
-        double chartWidth, double chartHeight
-    ) {
-        if (log.isDebugEnabled()) {
-            log.debug("Calculate centered origin...");
-            log.debug("-> PDF width    : " + width);
-            log.debug("-> PDF height   : " + height);
-            log.debug("-> Chart width  : " + chartWidth);
-            log.debug("-> Chart height : " + chartHeight);
-            log.debug("-> margin left  : " + mLeft);
-            log.debug("-> margin right : " + mRight);
-            log.debug("-> margin bottom: " + mBottom);
-            log.debug("-> margin top   : " + mTop);
-        }
-
-        double[] origin = new double[2];
-
-        double centerX = width  / 2;
-        double centerY = height / 2;
-
-        origin[0] = centerX - chartWidth / 2;
-        origin[1] = centerY - chartHeight / 2;
-
-        origin[0] = origin[0] >= mLeft ? origin[0] : mLeft;
-        origin[1] = origin[1] >= mTop ? origin[1] : mTop;
-
-        if (log.isDebugEnabled()) {
-            log.debug("==> centered left origin: " + origin[0]);
-            log.debug("==> centered top  origin: " + origin[1]);
-        }
-
-        return origin;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/ChartGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1915 +0,0 @@
-package de.intevation.flys.exports;
-
-import de.intevation.artifactdatabase.state.ArtifactAndFacet;
-import de.intevation.artifactdatabase.state.Settings;
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.ArtifactNamespaceContext;
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.PreferredLocale;
-import de.intevation.artifacts.common.utils.XMLUtils;
-import de.intevation.flys.artifacts.access.RangeAccess;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.resources.Resources;
-import de.intevation.flys.collections.FLYSArtifactCollection;
-import de.intevation.flys.jfree.Bounds;
-import de.intevation.flys.jfree.CollisionFreeXYTextAnnotation;
-import de.intevation.flys.jfree.DoubleBounds;
-import de.intevation.flys.jfree.EnhancedLineAndShapeRenderer;
-import de.intevation.flys.jfree.FLYSAnnotation;
-import de.intevation.flys.jfree.StableXYDifferenceRenderer;
-import de.intevation.flys.jfree.StickyAxisAnnotation;
-import de.intevation.flys.jfree.Style;
-import de.intevation.flys.jfree.StyledAreaSeriesCollection;
-import de.intevation.flys.jfree.StyledSeries;
-import de.intevation.flys.model.River;
-import de.intevation.flys.themes.LineStyle;
-import de.intevation.flys.themes.TextStyle;
-import de.intevation.flys.themes.ThemeAccess;
-import de.intevation.flys.utils.FLYSUtils;
-import de.intevation.flys.utils.ThemeUtil;
-
-import java.awt.BasicStroke;
-import java.awt.Color;
-import java.awt.Font;
-import java.awt.Paint;
-import java.awt.Stroke;
-import java.awt.TexturePaint;
-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.jfree.chart.JFreeChart;
-import org.jfree.chart.LegendItem;
-import org.jfree.chart.LegendItemCollection;
-import org.jfree.chart.annotations.XYLineAnnotation;
-import org.jfree.chart.annotations.XYTextAnnotation;
-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.jfree.ui.TextAnchor;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-import de.intevation.flys.utils.Formatter;
-
-/**
- * The base class for chart creation. It should provide some basic things that
- * equal in all chart types.
- *
- * Annotations are added as FLYSAnnotations and come in mutliple basic forms:
- * TextAnnotations are labels somewhere in data space, StickyAnnotations are
- * labels of a slice or line in one data dimension (i.e. visualized as label
- * on a single axis).
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public abstract class ChartGenerator implements OutGenerator {
-
-    private static Logger logger = Logger.getLogger(ChartGenerator.class);
-
-    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";
-
-    protected static float ANNOTATIONS_AXIS_OFFSET = 0.02f;
-
-    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 FLYSArtifactCollection 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<FLYSAnnotation> annotations = new ArrayList<FLYSAnnotation>();
-
-    /**
-     * A mini interface that allows to walk over the YAXIS enums defined in
-     * subclasses.
-     */
-    public interface YAxisWalker {
-
-        int length();
-
-        String getId(int idx);
-    } // end of YAxisWalker interface
-
-
-
-    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
-
-
-
-    /**
-     * Default constructor that initializes internal data structures.
-     */
-    public ChartGenerator() {
-        datasets = new TreeMap<Integer, AxisDataset>();
-    }
-
-
-    /**
-     * Adds annotations to list. The given annotation will be visible.
-     */
-    public void addAnnotations(FLYSAnnotation annotation) {
-        annotations.add(annotation);
-    }
-
-    /**
-     * Add a text and a line annotation.
-     * @param area convenience to determine positions in plot.
-     * @param theme (optional) theme document
-     */
-    protected void addStickyAnnotation(
-        StickyAxisAnnotation annotation,
-        XYPlot plot,
-        ChartArea area,
-        LineStyle lineStyle,
-        TextStyle textStyle,
-        Document theme
-    ) {
-        // 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).
-            // FIXME: Remove dependency to XYChartGenerator here
-            AxisDataset dataset = getAxisDataset(
-                new Integer(annotation.getAxisSymbol()));
-            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 (ThemeUtil.parseShowVerticalLine(theme)) {
-                        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 (ThemeUtil.parseShowHorizontalLine(theme)) {
-                        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 (ThemeUtil.parseShowHorizontalLine(theme)) {
-                        XYLineAnnotation hitLineAnnotation =
-                            createStickyLineAnnotation(
-                                StickyAxisAnnotation.SimpleAxis.Y_AXIS,
-                                annotation.getPos(), annotation.getHitPoint(),
-                                area, lineStyle);
-                        plot.getRenderer(rendererIndex).addAnnotation(hitLineAnnotation,
-                            org.jfree.ui.Layer.BACKGROUND);
-                    }
-                    if (ThemeUtil.parseShowVerticalLine(theme)) {
-                        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.
-     */
-    protected 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.
-     */
-    protected 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.
-     */
-    protected 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.
-     */
-    protected 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);
-        }
-    }
-
-    /**
-     * Add the annotations (Sticky, Text and hyk zones) stored
-     * in the annotations field.
-     * @param plot Plot to add annotations to.
-     */
-    protected void addAnnotationsToRenderer(XYPlot plot) {
-        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 (FLYSAnnotation fa: annotations) {
-
-            // Access text styling, if any.
-            Document 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();
-                if (fa.getLabel() != null) {
-                    LegendItemCollection lic = new LegendItemCollection();
-                    LegendItemCollection old = plot.getFixedLegendItems();
-                    lic.add(createLegendItem(theme, fa.getLabel()));
-                    // (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);
-            }
-
-            // 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);
-            }
-        }
-    }
-
-
-    /**
-     * 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,
-        Document         attr,
-        boolean          visible);
-
-
-    protected abstract YAxisWalker getYAxisWalker();
-
-
-    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();
-
-
-    /**
-     * 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 called to retrieve the default label for an Y axis at
-     * position <i>pos</i>.
-     *
-     * @param pos The position of an Y axis.
-     *
-     * @return the default Y axis label at position <i>pos</i>.
-     */
-    protected abstract String getDefaultYAxisLabel(int pos);
-
-
-    /**
-     * 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));
-        }
-    }
-
-
-    /**
-     * Register annotations like MainValues for later plotting
-     *
-     * @param annotations list of annotations (data of facet).
-     * @param aandf   Artifact and the facet.
-     * @param theme   Theme document for given annotations.
-     * @param visible The visibility of the annotations.
-     */
-    protected void doAnnotations(
-        FLYSAnnotation annotations,
-        ArtifactAndFacet aandf,
-        Document theme,
-        boolean visible
-    ){
-        logger.debug("doAnnotations");
-
-        // Add all annotations to our annotation pool.
-        annotations.setTheme(theme);
-        if (aandf != null) {
-            annotations.setLabel(aandf.getFacetDescription());
-        }
-        else {
-            logger.error(
-                "Art/Facet for Annotations is null. " +
-                "This should never happen!");
-        }
-
-        if (visible) {
-            addAnnotations(annotations);
-        }
-    }
-
-
-    /**
-     * Generate chart.
-     */
-    @Override
-    public void generate()
-    throws IOException
-    {
-        logger.debug("ChartGenerator.generate");
-
-        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(Document request, OutputStream out, CallContext context) {
-        logger.debug("ChartGenerator.init");
-
-        this.request = request;
-        this.out     = out;
-        this.context = context;
-    }
-
-
-    /** Sets the master artifact. */
-    @Override
-    public void setMasterArtifact(Artifact master) {
-        this.master = master;
-    }
-
-
-    /** Sets the collection. */
-    @Override
-    public void setCollection(FLYSArtifactCollection 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;
-    }
-
-
-    /**
-     * Creates a list of Section for the chart's Y axes. This method makes use
-     * of <i>getYAxisWalker</i> to be able to access all Y axes defined in
-     * subclasses.
-     *
-     * @return a list of Y axis sections.
-     */
-    protected List<AxisSection> buildYAxisSections() {
-        List<AxisSection> axisSections = new ArrayList<AxisSection>();
-
-        YAxisWalker walker = getYAxisWalker();
-        for (int i = 0, n = walker.length(); i < n; i++) {
-            AxisSection ySection = new AxisSection();
-            ySection.setIdentifier(walker.getId(i));
-            ySection.setLabel(getYAxisLabel(i));
-            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 <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;
-    }
-
-
-    /**
-     * 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 pos) {
-        ChartSettings chartSettings = getChartSettings();
-        if (chartSettings == null) {
-            return DEFAULT_FONT_SIZE;
-        }
-
-        YAxisWalker walker = getYAxisWalker();
-
-        AxisSection   as = chartSettings.getAxisSection(walker.getId(pos));
-        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 };
-    }
-
-
-    /**
-     * 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(int pos) {
-        ChartSettings chartSettings = getChartSettings();
-        if (chartSettings == null) {
-            return getDefaultYAxisLabel(pos);
-        }
-
-        YAxisWalker walker = getYAxisWalker();
-        AxisSection     as = chartSettings.getAxisSection(walker.getId(pos));
-        if (as != null) {
-            String label = as.getLabel();
-
-            if (label != null) {
-                return label;
-            }
-        }
-
-        return getDefaultYAxisLabel(pos);
-    }
-
-
-    /**
-     * 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.
-     */
-    protected 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.
-     */
-    protected String msg(String key) {
-        return Resources.getMsg(context.getMeta(), key, key);
-    }
-
-    protected String msg(String key, String def, Object[] args) {
-        return Resources.getMsg(context.getMeta(), key, def, args);
-    }
-
-
-    protected String getRiverName() {
-        FLYSArtifact flys = (FLYSArtifact) master;
-
-        River river = FLYSUtils.getRiver(flys);
-        return (river != null) ? river.getName() : "";
-    }
-
-
-    protected double[] getRange() {
-        FLYSArtifact flys = (FLYSArtifact) master;
-
-        RangeAccess rangeAccess = new RangeAccess(flys, null);
-        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) {
-        YAxisWalker walker = getYAxisWalker();
-
-        return new IdentifiableNumberAxis(walker.getId(idx), label);
-    }
-
-
-    /**
-     * Create Y (range) axis for given index.
-     * Shall be overriden by subclasses.
-     */
-    protected NumberAxis createYAxis(int index) {
-        YAxisWalker walker = getYAxisWalker();
-
-        Font labelFont = new Font(
-            DEFAULT_FONT_NAME,
-            Font.BOLD,
-            getYAxisFontSize(index));
-
-        IdentifiableNumberAxis axis = new IdentifiableNumberAxis(
-            walker.getId(index),
-            getYAxisLabel(index));
-
-        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(Document theme, String name) {
-        // OPTIMIZE Pass font, parsed Theme items.
-        ThemeAccess themeAccess = new ThemeAccess(theme);
-
-        Color      color       = themeAccess.parseLineColorField();
-        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;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/ChartHelper.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,200 +0,0 @@
-package de.intevation.flys.exports;
-
-import org.jfree.data.Range;
-import org.jfree.data.xy.XYDataset;
-import org.jfree.data.xy.XYSeriesCollection;
-import org.jfree.data.time.RegularTimePeriod;
-import org.jfree.data.time.TimeSeriesCollection;
-import org.jfree.data.time.TimeSeries;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.flys.jfree.Bounds;
-import de.intevation.flys.jfree.DoubleBounds;
-import de.intevation.flys.jfree.TimeBounds;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class ChartHelper {
-
-    private static final Logger logger = Logger.getLogger(ChartHelper.class);
-
-
-    /**
-     * This method returns the ranges of the XYDataset <i>dataset</i> as array
-     * with [xrange, yrange].
-     *
-     * @param dataset The dataset which should be evaluated.
-     *
-     * @return an array with x and y ranges.
-     */
-    public static Bounds[] getBounds(XYSeriesCollection dataset) {
-        int seriesCount = dataset != null ? dataset.getSeriesCount() : 0;
-
-        if (seriesCount == 0) {
-            logger.warn("Dataset is empty or has no Series set.");
-            return null;
-        }
-
-        boolean foundValue = false;
-
-        double minX = Double.MAX_VALUE;
-        double maxX = -Double.MAX_VALUE;
-        double minY = Double.MAX_VALUE;
-        double maxY = -Double.MAX_VALUE;
-
-        for (int i = 0, m = seriesCount; i < m; i++) {
-            for (int j = 0, n = dataset.getItemCount(i); j < n; j++) {
-                double x = dataset.getXValue(i, j);
-                double y = dataset.getYValue(i, j);
-
-                if (Double.isNaN(x) || Double.isNaN(y)) {
-                    logger.warn("Item " + j + " in Series " + i + " is broken");
-                    continue;
-                }
-
-                foundValue = true;
-
-                if (x < minX) {
-                    minX = x;
-                }
-
-                if (x > maxX) {
-                    maxX = x;
-                }
-
-                if (y < minY) {
-                    minY = y;
-                }
-
-                if (y > maxY) {
-                    maxY = y;
-                }
-            }
-        }
-
-        return foundValue
-            ? new Bounds[] {
-                    new DoubleBounds(minX, maxX),
-                    new DoubleBounds(minY, maxY) }
-            : null;
-    }
-
-
-    public static Bounds[] getBounds(XYDataset dataset) {
-        if (dataset instanceof XYSeriesCollection) {
-            return getBounds((XYSeriesCollection) dataset);
-        }
-        else if(dataset instanceof TimeSeriesCollection) {
-            return getBounds((TimeSeriesCollection) dataset);
-        }
-        else {
-            logger.warn("Unknown XYDataset instance: " + dataset.getClass());
-            return null;
-        }
-    }
-
-
-    public static Bounds[] getBounds(TimeSeriesCollection collection) {
-        int seriesCount = collection != null ? collection.getSeriesCount() : 0;
-
-        if (seriesCount == 0) {
-            logger.warn("TimeSeriesCollection is empty or has no Series set.");
-            return null;
-        }
-
-        boolean foundValue = false;
-
-        long lowerX = Long.MAX_VALUE;
-        long upperX = -Long.MAX_VALUE;
-
-        double lowerY = Double.MAX_VALUE;
-        double upperY = -Double.MAX_VALUE;
-
-        for (int i = 0, m = seriesCount; i < m; i++) {
-            TimeSeries series = collection.getSeries(i);
-
-            for (int j = 0, n = collection.getItemCount(i); j < n; j++) {
-                RegularTimePeriod rtp = series.getTimePeriod(j);
-
-                if (rtp == null) {
-                    continue;
-                }
-
-                foundValue = true;
-
-                long start = rtp.getFirstMillisecond();
-                long end   = rtp.getLastMillisecond();
-
-                if (start < lowerX) {
-                    lowerX = start;
-                }
-
-                if (end > upperX) {
-                    upperX = end;
-                }
-
-                double y = series.getValue(j).doubleValue();
-
-                lowerY = Math.min(lowerY, y);
-                upperY = Math.max(upperY, y);
-            }
-        }
-
-        if (foundValue) {
-            return new Bounds[] {
-                new TimeBounds(lowerX, upperX),
-                new DoubleBounds(lowerY, upperY)
-            };
-        }
-
-        return null;
-    }
-
-
-    /**
-     * Expand bounds by percent.
-     *
-     * @param bounds The bounds to expand.
-     * @param percent The percentage to expand.
-     *
-     * @return a new, expanded bounds.
-     */
-    public static Bounds expandBounds(Bounds bounds, double percent) {
-        if (bounds == null) {
-            return null;
-        }
-
-        double value  = (Double) bounds.getLower();
-        double expand = Math.abs(value / 100 * percent);
-
-        return expand != 0
-            ? new DoubleBounds(value-expand, value+expand)
-            : new DoubleBounds(-0.01 * percent, 0.01 * percent);
-    }
-
-
-    /**
-     * Expand range by percent.
-     *
-     * @param range The range to expand.
-     * @param percent The percentage to expand.
-     *
-     * @return a new, expanded range.
-     */
-    public static Range expandRange(Range range, double percent) {
-        if (range == null) {
-            return null;
-        }
-
-        double value  = range.getLowerBound();
-        double expand = Math.abs(value / 100 * percent);
-
-        return expand != 0
-            ? new Range(value-expand, value+expand)
-            : new Range(-0.01 * percent, 0.01 * percent);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/ChartInfoGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,189 +0,0 @@
-package de.intevation.flys.exports;
-
-import de.intevation.flys.collections.FLYSArtifactCollection;
-import de.intevation.flys.java2d.NOPGraphics2D;
-
-import java.io.IOException;
-import java.io.OutputStream;
-
-import java.awt.Transparency;
-import java.awt.Graphics2D;
-
-import java.awt.geom.Rectangle2D;
-
-import java.awt.image.BufferedImage;
-
-import org.w3c.dom.Document;
-
-import org.apache.log4j.Logger;
-
-import org.jfree.chart.ChartRenderingInfo;
-import org.jfree.chart.JFreeChart;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.artifactdatabase.state.ArtifactAndFacet;
-import de.intevation.artifactdatabase.state.Settings;
-
-import de.intevation.artifacts.common.utils.XMLUtils;
-
-
-/**
- * An OutGenerator that generates meta information for charts. A concrete
- * ChartInfoGenerator need to instantiate a concrete ChartGenerator and dispatch
- * the methods to that instance. The only thing this ChartInfoGenerator needs
- * to, is to overwrite the generate() method which doesn't write the chart image
- * to the OutputStream but a Document that contains some meta information of the
- * created chart.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public abstract class ChartInfoGenerator implements OutGenerator {
-
-    public static final boolean USE_NOP_GRAPHICS =
-        Boolean.getBoolean("info.rendering.nop.graphics");
-
-    /** The logger used in this generator.*/
-    private static Logger logger =
-        Logger.getLogger(ChartInfoGenerator.class);
-
-
-    /** The OutGenerator that creates the charts.*/
-    protected ChartGenerator generator;
-
-    protected OutputStream out;
-
-
-
-    public ChartInfoGenerator(ChartGenerator generator) {
-        this.generator = generator;
-    }
-
-
-    /**
-     * Dispatches the operation to the instantiated generator.
-     *
-     * @param request
-     * @param out
-     * @param context
-     */
-    public void init(Document request, OutputStream out, CallContext context) {
-        this.out = out;
-
-        generator.init(request, out, context);
-    }
-
-
-    /**
-     * Dispatches the operation to the instantiated generator.
-     *
-     * @param master The master artifact
-     */
-    public void setMasterArtifact(Artifact master) {
-        generator.setMasterArtifact(master);
-    }
-
-
-    /**
-     * Dispatches the operation to the instantiated generator.
-     *
-     * @param collection The collection.
-     */
-    public void setCollection(FLYSArtifactCollection collection) {
-        generator.setCollection(collection);
-    }
-
-
-    /**
-     * Dispatches the operation to the instantiated generator.
-     */
-    public void doOut(
-        ArtifactAndFacet artifactFacet,
-        Document         attr,
-        boolean          visible
-    ) {
-        generator.doOut(artifactFacet, attr, visible);
-    }
-
-
-    /**
-     * This method generates the chart using a concrete ChartGenerator but
-     * doesn't write the chart itself to the OutputStream but a Document that
-     * contains meta information of the created chart.
-     */
-    @Override
-    public void generate()
-    throws IOException
-    {
-        logger.debug("ChartInfoGenerator.generate");
-
-        JFreeChart chart = generator.generateChart();
-
-        int[] size = generator.getSize();
-        if (size == null) {
-            size = generator.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");
-        }
-
-
-        InfoGeneratorHelper helper = new InfoGeneratorHelper(generator);
-        Document doc = helper.createInfoDocument(chart, info);
-
-        XMLUtils.toStream(doc, out);
-    }
-
-
-    /**
-     * A proxy method which calls <i>generator</i>.getSettings() and returns its
-     * return value.
-     *
-     * @return a Settings object provided by <i>generator</i>.
-     */
-    @Override
-    public Settings getSettings() {
-        return generator.getSettings();
-    }
-
-
-    /**
-     * A proxy method which calls <i>generator</i>.setSettings().
-     *
-     * @param settings A settings object for the <i>generator</i>.
-     */
-    @Override
-    public void setSettings(Settings settings) {
-        generator.setSettings(settings);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/ChartSection.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,92 +0,0 @@
-package de.intevation.flys.exports;
-
-import org.apache.log4j.Logger;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class ChartSection extends TypeSection {
-
-    private static Logger logger = Logger.getLogger(ChartSection.class);
-
-    public static final String TITLE_ATTR         = "title";
-    public static final String SUBTITLE_ATTR      = "subtitle";
-    public static final String DISPLAYGRID_ATTR   = "display-grid";
-    public static final String DISPLAYLOGO_ATTR   = "display-logo";
-    public static final String LOGOPLACEMENTH_ATTR = "logo-placeh";
-    public static final String LOGOPLACEMENTV_ATTR = "logo-placev";
-
-
-    public ChartSection() {
-        super("chart");
-    }
-
-
-    public void setTitle(String title) {
-        setStringValue(TITLE_ATTR, title);
-    }
-
-
-    public String getTitle() {
-        return getStringValue(TITLE_ATTR);
-    }
-
-
-    public void setSubtitle(String subtitle) {
-        setStringValue(SUBTITLE_ATTR, subtitle);
-    }
-
-
-    public String getSubtitle() {
-        return getStringValue(SUBTITLE_ATTR);
-    }
-
-
-    /** Get Property-value for display-logo property. */
-    public String getDisplayLogo() {
-        return getStringValue(DISPLAYLOGO_ATTR);
-    }
-
-
-    /** Set Property-value for display-logo property. */
-    public void setDisplayLogo(String logo) {
-        logger.debug("Setting Display logo string.");
-        setChoiceStringValue(DISPLAYLOGO_ATTR, logo, "logo");
-    }
-
-
-    /** Get Property-value for horizontal logo-placement property. */
-    public String getLogoHPlacement() {
-        return getStringValue(LOGOPLACEMENTH_ATTR);
-    }
-
-
-    /** Set Property-value for horizontal logo-placement property. */
-    public void setLogoHPlacement(String place) {
-        setChoiceStringValue(LOGOPLACEMENTH_ATTR, place, "placeh");
-    }
-
-
-    /** Get Property-value for vertical logo-placement property. */
-    public String getLogoVPlacement() {
-        return getStringValue(LOGOPLACEMENTV_ATTR);
-    }
-
-
-    /** Set Property-value for vertical logo-placement property. */
-    public void setLogoVPlacement(String place) {
-        setChoiceStringValue(LOGOPLACEMENTV_ATTR, place, "placev");
-    }
-
-
-    public void setDisplayGrid(boolean displayGrid) {
-        setBooleanValue(DISPLAYGRID_ATTR, displayGrid);
-    }
-
-
-    public Boolean getDisplayGrid() {
-        return getBooleanValue(DISPLAYGRID_ATTR);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/ChartSettings.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,299 +0,0 @@
-package de.intevation.flys.exports;
-
-import javax.xml.xpath.XPathConstants;
-
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.common.utils.XMLUtils;
-
-import de.intevation.artifactdatabase.state.DefaultSection;
-import de.intevation.artifactdatabase.state.DefaultSettings;
-import de.intevation.artifactdatabase.state.Section;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class ChartSettings extends DefaultSettings {
-
-    private static final Logger logger = Logger.getLogger(ChartSettings.class);
-
-    protected ChartSection  chartSection;
-    protected LegendSection legendSection;
-    protected ExportSection exportSection;
-    protected Section       axesSection;
-
-
-    public ChartSettings() {
-        super();
-
-        axesSection = new DefaultSection("axes");
-        addSection(axesSection);
-    }
-
-
-    /**
-     * Sets the chart section. Old chart sections are removed.
-     *
-     * @param chartSection A new Section that stores chart specific attributes.
-     */
-    public void setChartSection(ChartSection chartSection) {
-        ChartSection oldChartSection = getChartSection();
-
-        if (oldChartSection != null) {
-            removeSection(oldChartSection);
-        }
-
-        this.chartSection = chartSection;
-        addSection(chartSection);
-    }
-
-
-    /**
-     * Returns the Section that stores chart specific attributes.
-     *
-     * @return the Section that stores chart specific attributes.
-     */
-    public ChartSection getChartSection() {
-        return chartSection;
-    }
-
-
-    /**
-     * Sets the legend section. Old legend sections are removed.
-     *
-     * @param legendSection A new Section that stores legend specific
-     * attributes.
-     */
-    public void setLegendSection(LegendSection legendSection) {
-        LegendSection oldLegendSection = getLegendSection();
-
-        if (oldLegendSection != null) {
-            removeSection(oldLegendSection);
-        }
-
-        this.legendSection = legendSection;
-        addSection(legendSection);
-    }
-
-
-    /**
-     * Returns the Section that stores legend specific attributes.
-     *
-     * @return the Section that stores legend specific attributes.
-     */
-    public LegendSection getLegendSection() {
-        return legendSection;
-    }
-
-
-    /**
-     * Sets the export section. Old export sections are removed.
-     *
-     * @param exportSection A new Section that stores export specific
-     * attributes.
-     */
-    public void setExportSection(ExportSection exportSection) {
-        ExportSection oldExportSection = getExportSection();
-
-        if (oldExportSection != null) {
-            removeSection(oldExportSection);
-        }
-
-        this.exportSection = exportSection;
-        addSection(exportSection);
-    }
-
-
-    /**
-     * Returns the Section that stores export specific attributes.
-     *
-     * @return the Section that stores export specific attributes.
-     */
-    public ExportSection getExportSection() {
-        return exportSection;
-    }
-
-
-    /**
-     * Adds a Section for a new axis of the chart.
-     *
-     * @param axisSection The Section specific for a chart axis.
-     */
-    public void addAxisSection(AxisSection axisSection) {
-        if (axisSection != null) {
-            axesSection.addSubsection(axisSection);
-        }
-    }
-
-
-    /**
-     * This method returns an AxisSection specified by <i>axisId</i> or null if
-     * no AxisSection is existing with identifier <i>axisId</i>.
-     *
-     * @param axisId The identifier of the wanted AxisSection.
-     *
-     * @return the AxisSection specified by <i>axisId</i> or null.
-     */
-    public AxisSection getAxisSection(String axisId) {
-        for (int i = 0, n = axesSection.getSubsectionCount(); i < n; i++) {
-            AxisSection as = (AxisSection) axesSection.getSubsection(i);
-            String      id = as.getIdentifier();
-
-            if (id != null && id.equals(axisId)) {
-                return as;
-            }
-        }
-
-        return null;
-    }
-
-
-    /**
-     * Parses the settings from <i>settings</i>. The result is a new
-     * ChartSettings instance.
-     *
-     * @param settings A <i>settings</i> node.
-     *
-     * @return a new <i>ChartSettings</i> instance.
-     */
-    public static ChartSettings parse(Node settings) {
-        if (settings == null) {
-            logger.warn("Tried to parse ChartSettings from empty Node!");
-            return null;
-        }
-
-        ChartSettings chartSettings = new ChartSettings();
-
-        parseAxes(chartSettings, settings);
-        parseChart(chartSettings, settings);
-        parseLegend(chartSettings, settings);
-        parseExport(chartSettings, settings);
-
-        return chartSettings;
-    }
-
-
-    protected static void parseAxes(ChartSettings target, Node settings) {
-        NodeList axesList = (NodeList) XMLUtils.xpath(
-            settings, "axes/axis", XPathConstants.NODESET, null);
-
-        int num = axesList != null ? axesList.getLength() : 0;
-
-        if (num <= 0) {
-            logger.debug("No axis sections found.");
-            return;
-        }
-
-        for (int i = 0; i < num; i++) {
-            parseAxis(target, axesList.item(i));
-        }
-    }
-
-
-    protected static void parseAxis(ChartSettings target, Node axis) {
-        AxisSection section = new AxisSection();
-
-        String id       = XMLUtils.xpathString(axis, "id", null);
-        String label    = XMLUtils.xpathString(axis, "label", null);
-        String fSize    = XMLUtils.xpathString(axis, "font-size", null);
-        String fixation = XMLUtils.xpathString(axis, "fixation", null);
-        String low      = XMLUtils.xpathString(axis, "lower", null);
-        String up       = XMLUtils.xpathString(axis, "upper", null);
-
-        if (logger.isDebugEnabled()) {
-            logger.debug("Fount axis id:        '" + id + "'");
-            logger.debug("Fount axis label:     '" + label + "'");
-            logger.debug("Fount axis font size: '" + fSize + "'");
-            logger.debug("Fount axis fixation:  '" + fixation + "'");
-            logger.debug("Fount axis lower:     '" + low + "'");
-            logger.debug("Fount axis upper:     '" + up + "'");
-        }
-
-        section.setIdentifier(id);
-        section.setLabel(label);
-        section.setFontSize(Integer.parseInt(fSize.length() > 0 ? fSize : "-1"));
-        section.setFixed(Boolean.valueOf(fixation));
-        section.setLowerRange(Double.parseDouble(low.length() > 0 ? low : "0"));
-        section.setUpperRange(Double.parseDouble(up.length() > 0 ? up : "0"));
-
-        target.addAxisSection(section);
-    }
-
-
-    /**
-     * From document chart create ChartSection and populate it with attributes.
-     * Give this object to target as chartsection.
-     */
-    protected static void parseChart(ChartSettings target, Node chart) {
-        ChartSection chartSection = new ChartSection();
-
-        String title = XMLUtils.xpathString(chart, "chart/title", null);
-        String sub   = XMLUtils.xpathString(chart, "chart/subtitle", null);
-        String grid  = XMLUtils.xpathString(chart, "chart/display-grid", null);
-        String logo  = XMLUtils.xpathString(chart, "chart/display-logo", null);
-        String placeh = XMLUtils.xpathString(chart, "chart/logo-placeh", null);
-        String placev = XMLUtils.xpathString(chart, "chart/logo-placev", null);
-
-        if (logger.isDebugEnabled()) {
-            logger.debug("Found chart title:    '" + title + "'");
-            logger.debug("Found chart subtitle: '" + sub + "'");
-            logger.debug("Found chart grid:     '" + grid + "'");
-            logger.debug("Found chart logo:     '" + logo + "'");
-            logger.debug("Found chart logo placeh: '" + placeh + "'");
-            logger.debug("Found chart logo placev: '" + placev + "'");
-        }
-
-        chartSection.setTitle(title);
-        chartSection.setSubtitle(sub);
-        chartSection.setDisplayGrid(Boolean.valueOf(grid));
-        chartSection.setDisplayLogo(logo);
-        chartSection.setLogoHPlacement(placeh);
-        chartSection.setLogoVPlacement(placev);
-
-        target.setChartSection(chartSection);
-    }
-
-
-    protected static void parseLegend(ChartSettings target, Node legend) {
-        LegendSection section = new LegendSection();
-
-        String vis   = XMLUtils.xpathString(legend, "legend/visibility", null);
-        String fSize = XMLUtils.xpathString(legend, "legend/font-size", null);
-        String lthre = XMLUtils.xpathString(legend, "legend/aggregation-threshold", null);
-
-        if (logger.isDebugEnabled()) {
-            logger.debug("Found legend visibility: '" + vis + "'");
-            logger.debug("Found legend font size : '" + fSize + "'");
-            logger.debug("Found legend aggregation threshold : '" + lthre + "'");
-        }
-
-        section.setVisibility(Boolean.valueOf(vis));
-        section.setFontSize(Integer.valueOf(fSize.length() > 0 ? fSize : "-1"));
-        section.setAggregationThreshold(Integer.valueOf(lthre.length() >0 ? lthre : "-1"));
-
-        target.setLegendSection(section);
-    }
-
-
-    protected static void parseExport(ChartSettings target, Node export) {
-        ExportSection section = new ExportSection();
-
-        String width  = XMLUtils.xpathString(export, "export/width", null);
-        String height = XMLUtils.xpathString(export, "export/height", null);
-
-        if (logger.isDebugEnabled()) {
-            logger.debug("Found export width : '" + width + "'");
-            logger.debug("Found export height: '" + height + "'");
-        }
-
-        section.setWidth(Integer.valueOf(width.length() > 0 ? width : "-1"));
-        section.setHeight(Integer.valueOf(height.length() > 0 ? height : "-1"));
-
-        target.setExportSection(section);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/ChoiceStringAttribute.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-package de.intevation.flys.exports;
-
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class ChoiceStringAttribute extends StringAttribute {
-
-    /** Indicator which type of choice is dealt with. */
-    protected String choiceType;
-
-
-    public ChoiceStringAttribute(String name,
-                           String value,
-                           boolean visible,
-                           String choiceType) {
-        super(name, value, visible);
-        this.choiceType = choiceType;
-    }
-
-
-    /**
-     * Calls VisibleAttribute.toXML() and appends afterwards an attribute
-     * <i>type</i> with value <i>string</i>.
-     *
-     * @param parent The parent Node.
-     *
-     * @return the new Node that represents this Attribute.
-     */
-    @Override
-    public Node toXML(Node parent) {
-        Element ele = (Element) super.toXML(parent);
-        ele.setAttribute("type", "string");
-        ele.setAttribute("choice", choiceType);
-
-        return ele;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/ComputedDischargeCurveExporter.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,229 +0,0 @@
-package de.intevation.flys.exports;
-
-import java.io.OutputStream;
-import java.text.NumberFormat;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-import java.util.HashMap;
-import java.util.Date;
-import java.text.DateFormat;
-import java.util.Locale;
-
-import org.w3c.dom.Document;
-
-import org.apache.log4j.Logger;
-
-import au.com.bytecode.opencsv.CSVWriter;
-
-import net.sf.jasperreports.engine.JasperExportManager;
-import net.sf.jasperreports.engine.JasperFillManager;
-import net.sf.jasperreports.engine.JasperPrint;
-import net.sf.jasperreports.engine.JRException;
-
-import de.intevation.artifacts.common.utils.Config;
-
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.flys.artifacts.access.RangeAccess;
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.WQKms;
-import de.intevation.flys.artifacts.model.WKmsJRDataSource;
-import de.intevation.flys.artifacts.resources.Resources;
-
-import de.intevation.flys.utils.FLYSUtils;
-import de.intevation.flys.utils.Formatter;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class ComputedDischargeCurveExporter extends AbstractExporter {
-
-    /** The logger used in this exporter.*/
-    private static Logger logger =
-        Logger.getLogger(ComputedDischargeCurveExporter.class);
-
-    public static final String CSV_W_HEADER =
-        "export.computed.discharge.curve.csv.header.w";
-
-    public static final String CSV_Q_HEADER =
-        "export.computed.discharge.curve.csv.header.q";
-
-    public static final String DEFAULT_CSV_W_HEADER  = "W [NN + m]";
-    public static final String DEFAULT_CSV_Q_HEADER  = "Q [m\u00b3/s]";
-
-    public static final String PDF_HEADER_MODE = "export.computed.discharge.pdf.mode";
-    public static final String JASPER_FILE = "export.computed.discharge.pdf.file";
-
-    protected List<WQKms> data;
-
-
-    public void init(Document request, OutputStream out, CallContext context) {
-        logger.debug("ComputedDischargeCurveExporter.init");
-
-        super.init(request, out, context);
-
-        this.data = new ArrayList<WQKms>();
-    }
-
-
-    @Override
-    protected void addData(Object d) {
-        if (d instanceof CalculationResult) {
-            d = ((CalculationResult)d).getData();
-        }
-        if (d instanceof WQKms[]){
-            data.addAll(Arrays.asList((WQKms [])d));
-            return;
-        }
-        else if (d instanceof WQKms) {
-            data.add((WQKms)d);
-            return;
-        }
-        logger.warn("Can't add data for csv export. Unkown data type " +
-                d.getClass().getName());
-    }
-
-
-    protected void writeCSVData(CSVWriter writer) {
-        logger.info("ComputedDischargeCurveExporter.writeData");
-
-        writeCSVHeader(writer);
-
-        NumberFormat wf  = getWFormatter();
-        NumberFormat qf  = getQFormatter();
-
-        double[] res = new double[3];
-
-        for (WQKms wqkms: data) {
-            int size = wqkms.size();
-
-            for (int i = 0; i < size; i++) {
-                res = wqkms.get(i, res);
-
-                writer.writeNext(new String[] {
-                    wf.format(res[0]),
-                    qf.format(res[1])
-                });
-            }
-        }
-    }
-
-
-    protected void writeCSVHeader(CSVWriter writer) {
-        logger.debug("ComputedDischargeCurveExporter.writeCSVHeader");
-
-        String unit = FLYSUtils.getRiver((FLYSArtifact) master).getWstUnit().getName();
-
-        writer.writeNext(new String[] {
-            msg(CSV_W_HEADER, DEFAULT_CSV_W_HEADER, new Object[] { unit }),
-            msg(CSV_Q_HEADER, DEFAULT_CSV_Q_HEADER)
-        });
-    }
-
-
-    /**
-     * Returns the number formatter for W values.
-     *
-     * @return the number formatter for W values.
-     */
-    protected NumberFormat getWFormatter() {
-        return Formatter.getComputedDischargeW(context);
-    }
-
-
-    /**
-     * Returns the number formatter for Q values.
-     *
-     * @return the number formatter for Q values.
-     */
-    protected NumberFormat getQFormatter() {
-        return Formatter.getComputedDischargeQ(context);
-    }
-
-
-    @Override
-    protected void writePDF(OutputStream out) {
-        WKmsJRDataSource source = createJRData();
-
-        String jasperFile = Resources.getMsg(
-                                context.getMeta(),
-                                JASPER_FILE,
-                                "/jasper/computed-discharge_en.jasper");
-        String confPath = Config.getConfigDirectory().toString();
-
-        Map parameters = new HashMap();
-        parameters.put("ReportTitle", "Exported Data");
-        try {
-            JasperPrint print = JasperFillManager.fillReport(
-                confPath + jasperFile,
-                parameters,
-                source);
-            JasperExportManager.exportReportToPdfStream(print, out);
-        }
-        catch(JRException je) {
-            logger.warn("Error generating PDF Report!");
-            je.printStackTrace();
-        }
-    }
-
-
-    protected WKmsJRDataSource createJRData() {
-        WKmsJRDataSource source = new WKmsJRDataSource();
-
-        addMetaData(source);
-        addWQData(source);
-
-        return source;
-    }
-
-
-    protected void addMetaData(WKmsJRDataSource source) {
-        CallMeta meta = context.getMeta();
-
-        FLYSArtifact flys = (FLYSArtifact) master;
-
-        source.addMetaData ("river", FLYSUtils.getRivername(flys));
-
-        Locale locale = Resources.getLocale(meta);
-        DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, locale);
-
-        source.addMetaData("date", df.format(new Date()));
-
-        RangeAccess rangeAccess = new RangeAccess(flys, null);
-        double[] kms = rangeAccess.getKmRange();
-        source.addMetaData("range", String.valueOf(kms[0]));
-
-        source.addMetaData("calculation", Resources.getMsg(
-                                            locale,
-                                            PDF_HEADER_MODE,
-                                            "Computed Discharge"));
-    }
-
-    protected void addWQData(WKmsJRDataSource source) {
-        NumberFormat wf  = getWFormatter();
-        NumberFormat qf  = getQFormatter();
-
-        double[] res = new double[3];
-
-        for (WQKms wqkms: data) {
-            int size = wqkms.size();
-
-            for (int i = 0; i < size; i++) {
-                res = wqkms.get(i, res);
-
-                source.addData(new String[] {
-                    "",   // Empty, the WKmsJRDtasource stores km here.
-                    wf.format(res[0]),
-                    qf.format(res[1])
-                });
-            }
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,269 +0,0 @@
-package de.intevation.flys.exports;
-
-import de.intevation.artifactdatabase.state.ArtifactAndFacet;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.StaticWKmsArtifact;
-import de.intevation.flys.artifacts.WINFOArtifact;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.WKms;
-import de.intevation.flys.artifacts.model.WQKms;
-import de.intevation.flys.jfree.FLYSAnnotation;
-import de.intevation.flys.jfree.StickyAxisAnnotation;
-import de.intevation.flys.jfree.StyledXYSeries;
-
-import de.intevation.flys.utils.FLYSUtils;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-import org.jfree.data.xy.XYSeries;
-import org.w3c.dom.Document;
-
-
-/**
- * An OutGenerator that generates discharge curves.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class ComputedDischargeCurveGenerator
-extends      DischargeCurveGenerator
-implements   FacetTypes
-{
-    /** The logger used in this generator. */
-    private static Logger logger =
-        Logger.getLogger(ComputedDischargeCurveGenerator.class);
-
-    public static final String I18N_CHART_TITLE =
-        "chart.computed.discharge.curve.title";
-
-    public static final String I18N_CHART_SUBTITLE =
-        "chart.computed.discharge.curve.subtitle";
-
-    public static final String I18N_YAXIS_LABEL =
-        "chart.computed.discharge.curve.yaxis.label";
-
-    public static final String I18N_CHART_TITLE_DEFAULT = "Abflusskurve";
-    public static final String I18N_YAXIS_LABEL_DEFAULT = "W [NN + m]";
-    public static final String I18N_MAINVALUES_Q_LABEL = "Q (Haupt- und Extremwerte)";
-    public static final String I18N_MAINVALUES_W_LABEL = "W (Haupt- und Extremwerte)";
-
-
-    /** Trivial Constructor. */
-    public ComputedDischargeCurveGenerator () {
-        super();
-    }
-
-
-    @Override
-    protected String getDefaultChartTitle() {
-        return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT);
-    }
-
-
-    @Override
-    protected String getDefaultChartSubtitle() {
-        double[] dist = getRange();
-
-        Object[] args = new Object[] {
-            getRiverName(),
-            dist[0]
-        };
-
-        return msg(I18N_CHART_SUBTITLE, "", args);
-    }
-
-
-    @Override
-    protected String getDefaultYAxisLabel(int pos) {
-        FLYSArtifact flys = (FLYSArtifact) master;
-
-        String unit = FLYSUtils.getRiver(flys).getWstUnit().getName();
-
-        return msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL_DEFAULT, new Object[] { unit });
-    }
-
-
-    /**
-     * Process data, build up plot.
-     */
-    @Override
-    public void doOut(
-        ArtifactAndFacet artifactFacet,
-        Document         attr,
-        boolean          visible
-    ) {
-        String name = artifactFacet.getFacetName();
-
-        logger.debug("ComputedDischargeCurveGenerator.doOut: " + name);
-
-        if (name == null) {
-            logger.warn("Broken facet in computed discharge out generation.");
-            return;
-        }
-
-        //XXX DEAD CODE // Facet facet = artifactFacet.getFacet();
-
-        if (name.equals(COMPUTED_DISCHARGE_Q)) {
-            doQOut((WQKms) artifactFacet.getData(context), artifactFacet, attr, visible);
-        }
-        else if (name.equals(STATIC_WQ)) {
-            doWQOut(artifactFacet.getData(context), artifactFacet, attr, visible);
-        }
-        else if (name.equals(STATIC_WQ_ANNOTATIONS)) {
-            doWQAnnotations(
-                artifactFacet.getData(context),
-                artifactFacet,
-                attr,
-                visible);
-        }
-        else if (name.equals(COMPUTED_DISCHARGE_MAINVALUES_Q)
-                || name.equals(MAINVALUES_Q)
-                || name.equals(COMPUTED_DISCHARGE_MAINVALUES_W)
-                || name.equals(MAINVALUES_W)
-        ) {
-            doAnnotations((FLYSAnnotation)
-                artifactFacet.getData(context), artifactFacet, attr, visible);
-        }
-        else if (name.equals(STATIC_WKMS_INTERPOL) || name.equals(HEIGHTMARKS_POINTS)) {
-            doWAnnotations(
-                artifactFacet.getData(context),
-                artifactFacet,
-                attr,
-                visible);
-        }
-        else if (name.equals(STATIC_WKMS)) {
-            doWAnnotations(
-                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(DISCHARGE_CURVE)) {
-            doDischargeOut(
-                (WINFOArtifact) artifactFacet.getArtifact(),
-                artifactFacet.getData(context),
-                artifactFacet.getFacetDescription(),
-                attr,
-                visible);
-        }
-        else {
-            logger.warn("Unknown facet type for computed discharge: " + name);
-            return;
-        }
-    }
-
-
-    /**
-     * Add WQ Data to plot.
-     * @param wqkms data as double[][]
-     */
-    protected void doWQOut(
-        Object           wqkms,
-        ArtifactAndFacet aaf,
-        Document         theme,
-        boolean          visible
-    ) {
-        logger.debug("ComputedDischargeCurveGenerator: doWQOut");
-        double [][] data = (double [][]) wqkms;
-
-        XYSeries series = new StyledXYSeries(aaf.getFacetDescription(), theme);
-        StyledSeriesBuilder.addPoints(series, data, true);
-
-        addAxisSeries(series, YAXIS.W.idx, visible);
-    }
-
-
-    /**
-     * Add Q-Series to plot.
-     * @param wqkms actual data
-     * @param theme theme to use.
-     */
-    protected void doQOut(
-        WQKms            wqkms,
-        ArtifactAndFacet aaf,
-        Document         theme,
-        boolean          visible
-    ) {
-        logger.debug("ComputedDischargeCurveGenerator: doWQOut");
-        XYSeries series = new StyledXYSeries(aaf.getFacetDescription(), theme);
-        StyledSeriesBuilder.addPointsQW(series, wqkms);
-
-        addAxisSeries(series, YAXIS.W.idx, visible);
-    }
-
-
-    /**
-     * Add WQ-Annotations to plot.
-     * @param wqkms actual data
-     * @param theme theme to use.
-     */
-    protected void doWQAnnotations(
-        Object   wqkms,
-        ArtifactAndFacet aandf,
-        Document theme,
-        boolean  visible
-    ) {
-        List<StickyAxisAnnotation> xy = new ArrayList<StickyAxisAnnotation>();
-        double [][] data = (double [][]) wqkms;
-        for (int i = 0; i< data[0].length; i++) {
-            // TODO we need linear interpolation?
-            xy.add(new StickyAxisAnnotation(aandf.getFacetDescription(),
-                (float) data[0][i], StickyAxisAnnotation.SimpleAxis.X_AXIS));
-            xy.add(new StickyAxisAnnotation(aandf.getFacetDescription(),
-                (float) data[1][i], StickyAxisAnnotation.SimpleAxis.Y_AXIS));
-        }
-
-        doAnnotations(new FLYSAnnotation(aandf.getFacetDescription(), xy),
-            aandf, theme, visible);
-    }
-
-
-    /**
-     * Add W-Annotations to plot.
-     * @param wqkms actual data (double[][]).
-     * @param theme theme to use.
-     */
-    protected void doWAnnotations(
-        Object   wqkms,
-        ArtifactAndFacet aandf,
-        Document theme,
-        boolean  visible
-    ) {
-        Facet facet = aandf.getFacet();
-
-        List<StickyAxisAnnotation> xy = new ArrayList<StickyAxisAnnotation>();
-        // Try to find them as WKms as well...
-        if (wqkms instanceof double[][]) {
-            logger.debug("its double[][] time, baby");
-            double [][] data = (double [][]) wqkms;
-            // TODO Do we need interpolation?
-            for (int i = 0; i< data[0].length; i++) {
-                xy.add(new StickyAxisAnnotation(aandf.getFacetDescription(),
-                    (float) data[1][i], StickyAxisAnnotation.SimpleAxis.Y_AXIS));
-            }
-
-            doAnnotations(new FLYSAnnotation(facet.getDescription(), xy),
-                aandf, theme, visible);
-        }
-        else {
-            logger.debug("its wkms time, baby");
-            WKms data = (WKms) wqkms;
-            // Assume its WKms.
-            // XXX DEAD CODE // double location = getRange()[0];
-            double w = StaticWKmsArtifact.getWAtKmLin(data, getRange()[0]);
-            xy.add(new StickyAxisAnnotation(aandf.getFacetDescription(),
-              (float) w, StickyAxisAnnotation.SimpleAxis.Y_AXIS));
-
-            doAnnotations(new FLYSAnnotation(facet.getDescription(), xy),
-                aandf, theme, visible);
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/ComputedDischargeCurveInfoGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,16 +0,0 @@
-package de.intevation.flys.exports;
-
-
-/**
- * A ChartInfoGenerator that generates meta information for specific computed
- * discharge curves.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class ComputedDischargeCurveInfoGenerator extends ChartInfoGenerator {
-
-    public ComputedDischargeCurveInfoGenerator() {
-        super(new ComputedDischargeCurveGenerator());
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/CrossSectionGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,445 +0,0 @@
-package de.intevation.flys.exports;
-
-import java.awt.BasicStroke;
-import java.awt.Color;
-import java.awt.Paint;
-import java.awt.Stroke;
-import java.text.NumberFormat;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-import org.jfree.chart.LegendItemCollection;
-import org.jfree.chart.annotations.XYBoxAnnotation;
-import org.jfree.chart.annotations.XYTextAnnotation;
-import org.jfree.chart.plot.XYPlot;
-import org.jfree.data.xy.XYSeries;
-import org.w3c.dom.Document;
-
-import de.intevation.artifactdatabase.state.ArtifactAndFacet;
-import de.intevation.artifacts.DataProvider;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.geom.Lines;
-import de.intevation.flys.artifacts.model.CrossSectionFacet;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.HYKFactory;
-import de.intevation.flys.artifacts.resources.Resources;
-import de.intevation.flys.jfree.FLYSAnnotation;
-import de.intevation.flys.jfree.StyledXYSeries;
-import de.intevation.flys.model.FastCrossSectionLine;
-import de.intevation.flys.themes.LineStyle;
-import de.intevation.flys.themes.TextStyle;
-import de.intevation.flys.themes.ThemeAccess;
-import de.intevation.flys.utils.FLYSUtils;
-import de.intevation.flys.utils.Formatter;
-import de.intevation.flys.utils.ThemeUtil;
-
-
-/**
- * An OutGenerator that generates cross section graphs.
- */
-public class CrossSectionGenerator
-extends      LongitudinalSectionGenerator
-implements   FacetTypes
-{
-    /** The logger that is used in this generator. */
-    private static Logger logger =
-            Logger.getLogger(CrossSectionGenerator.class);
-
-    public static final String I18N_CHART_TITLE =
-            "chart.cross_section.title";
-
-    public static final String I18N_CHART_SUBTITLE =
-            "chart.cross_section.subtitle";
-
-    public static final String I18N_XAXIS_LABEL =
-            "chart.cross_section.xaxis.label";
-
-    public static final String I18N_YAXIS_LABEL =
-            "chart.cross_section.yaxis.label";
-
-    public static final String I18N_CHART_TITLE_DEFAULT = "Querprofildiagramm";
-    public static final String I18N_XAXIS_LABEL_DEFAULT = "Abstand [m]";
-    public static final String I18N_YAXIS_LABEL_DEFAULT = "W [NN + m]";
-
-
-    /** Trivial Constructor. */
-    public CrossSectionGenerator() {
-        super();
-    }
-
-
-    @Override
-    protected YAxisWalker getYAxisWalker() {
-        return new YAxisWalker() {
-            @Override
-            public int length() {
-                return 1;
-            }
-
-            /** Get identifier for this index. */
-            @Override
-            public String getId(int idx) {
-                return "W";
-            }
-        };
-    }
-
-
-    /**
-     * Get localized chart title.
-     */
-    @Override
-    public String getDefaultChartTitle() {
-        Object[] i18n_msg_args = new Object[] {
-                getRiverName()
-        };
-        return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT, i18n_msg_args);
-    }
-
-
-    /** Always return default subtitle. */
-    @Override
-    protected String getChartSubtitle() {
-        // XXX NOTE: overriding this method disables ChartSettings subtitle!
-        // The default implementation of this method in ChartGenerator returns
-        // the subtitle changed via the chart settings dialog. This method
-        // always returns the subtitle containing river and km, NEVER the
-        // ChartSettings subtitle!
-        return getDefaultChartSubtitle();
-    }
-
-
-    /** Get Charts default subtitle. */
-    @Override
-    protected String getDefaultChartSubtitle() {
-        List<DataProvider> providers =
-                context.getDataProvider(CrossSectionFacet.BLACKBOARD_CS_MASTER_DATA);
-        double km = 0d;
-        if (providers.size() > 0) {
-            FastCrossSectionLine csl = (FastCrossSectionLine) providers.get(0).
-                    provideData(CrossSectionFacet.BLACKBOARD_CS_MASTER_DATA,
-                            null, context);
-            km = csl == null ? -1 : csl.getKm();
-        }
-
-        Object[] args = new Object[] {
-                getRiverName(),
-                km
-        };
-
-        logger.debug("Locale: " + Resources.getLocale(context.getMeta()));
-
-        return msg(I18N_CHART_SUBTITLE, "", args);
-    }
-
-
-    /** Get color for hyk zones by their type (which is the name). */
-    protected Paint colorForHYKZone(String zoneName) {
-        if (zoneName.startsWith("R")) {
-            // Brownish.
-            return new Color(153, 60, 0);
-        }
-        else if (zoneName.startsWith("V")) {
-            // Greenish.
-            return new Color(0, 255, 0);
-        }
-        else if (zoneName.startsWith("B")) {
-            // Grayish.
-            return new Color(128, 128, 128);
-        }
-        else if (zoneName.startsWith("H")) {
-            // Blueish.
-            return new Color(0, 0, 255);
-        }
-        else {
-            // Default.
-            logger.debug("Unknown zone type found.");
-            return new Color(255, 0, 0);
-        }
-    }
-
-    @Override
-    protected void addAnnotationsToRenderer(XYPlot plot) {
-        super.addAnnotationsToRenderer(plot);
-
-        // Paints for the boxes/lines.
-        Stroke basicStroke = new BasicStroke(1.0f);
-
-        // XXX: DEAD CODE // Paint linePaint = new Color(255,  0,0,60);
-        Paint fillPaint = new Color(0,  255,0,60);
-        Paint tranPaint = new Color(0,    0,0, 0);
-
-        // OPTMIMIZE: Pre-calculate positions
-        ChartArea area = new ChartArea(
-                plot.getDomainAxis(0).getRange(),
-                plot.getRangeAxis().getRange());
-
-        for(FLYSAnnotation fa : this.annotations) {
-
-            // Access text styling, if any.
-            Document 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();
-                // XXX: DEAD CODE // lineStyle = themeAccess.parseLineStyle();
-                if (fa.getLabel() != null) {
-                    LegendItemCollection lic = new LegendItemCollection();
-                    LegendItemCollection old = plot.getFixedLegendItems();
-                    lic.add(createLegendItem(theme, fa.getLabel()));
-                    // (Re-)Add prior legend entries.
-                    if (old != null) {
-                        old.addAll(lic);
-                    }
-                    else {
-                        old = lic;
-                    }
-                    plot.setFixedLegendItems(old);
-                }
-            }
-
-            // Hyks.
-            for (HYKFactory.Zone zone: fa.getBoxes()) {
-                // For each zone, create a box to fill with color, a box to draw
-                // the lines and a text to display the type.
-                fillPaint = colorForHYKZone(zone.getName());
-
-                XYBoxAnnotation boxA = new XYBoxAnnotation(zone.getFrom(), area.atGround(),
-                        zone.getTo(), area.ofGround(0.03f), basicStroke, tranPaint, fillPaint);
-                XYBoxAnnotation boxB = new XYBoxAnnotation(zone.getFrom(), area.atGround(),
-                        zone.getTo(), area.atTop(), basicStroke, fillPaint, tranPaint);
-
-                XYTextAnnotation tex = new XYTextAnnotation(zone.getName(),
-                        zone.getFrom() + (zone.getTo() - zone.getFrom()) / 2.0d,
-                        area.ofGround(0.015f));
-                if (textStyle != null) {
-                    textStyle.apply(tex);
-                }
-
-                plot.getRenderer().addAnnotation(boxA, org.jfree.ui.Layer.BACKGROUND);
-                plot.getRenderer().addAnnotation(boxB, org.jfree.ui.Layer.BACKGROUND);
-                plot.getRenderer().addAnnotation(tex,  org.jfree.ui.Layer.BACKGROUND);
-            }
-        }
-    }
-
-    @Override
-    protected String getDefaultXAxisLabel() {
-        return msg(I18N_XAXIS_LABEL, I18N_XAXIS_LABEL_DEFAULT);
-    }
-
-
-    @Override
-    protected String getDefaultYAxisLabel(int pos) {
-        FLYSArtifact flys = (FLYSArtifact) master;
-
-        String unit = FLYSUtils.getRiver(flys).getWstUnit().getName();
-
-        return msg(I18N_YAXIS_LABEL,
-                   I18N_YAXIS_LABEL_DEFAULT,
-                   new Object[] { unit });
-    }
-
-
-    /**
-     * Let one facet do its job.
-     */
-    @Override
-    public void doOut(
-            ArtifactAndFacet artifactFacet,
-            Document         attr,
-            boolean          visible
-            ) {
-        String name = artifactFacet.getFacetName();
-
-        logger.debug("CrossSectionGenerator.doOut: " + name);
-
-        if (name == null) {
-            logger.error("No facet name for doOut(). No output generated!");
-            return;
-        }
-
-        if (name.equals(CROSS_SECTION)) {
-            doCrossSectionOut(
-                    artifactFacet.getData(context),
-                    artifactFacet.getFacetDescription(),
-                    attr,
-                    visible);
-        }
-        else if (name.equals(CROSS_SECTION_WATER_LINE)) {
-            doCrossSectionWaterLineOut(
-                    artifactFacet.getData(context),
-                    artifactFacet.getFacetDescription(),
-                    attr,
-                    visible);
-        }
-        else if (FacetTypes.IS.AREA(name)) {
-            doArea(artifactFacet.getData(context),
-                    artifactFacet,
-                    attr,
-                    visible);
-        }
-        else if (name.equals(HYK)) {
-            doHyk(artifactFacet.getData(context),
-                    artifactFacet.getFacetDescription(),
-                    attr,
-                    visible);
-        }
-        else if (FacetTypes.IS.MANUALLINE(name)) {
-            doCrossSectionWaterLineOut(
-                    artifactFacet.getData(context),
-                    artifactFacet.getFacetDescription(),
-                    attr,
-                    visible);
-        }
-        else if (FacetTypes.IS.MANUALPOINTS(name)) {
-            doPoints(artifactFacet.getData(context),
-                    artifactFacet,
-                    attr, visible, YAXIS.W.idx);
-        }
-        else {
-            logger.warn("CrossSection.doOut: Unknown facet name: " + name);
-            return;
-        }
-    }
-
-
-    /** Look up the axis identifier for a given facet type. */
-    @Override
-    public int axisIdxForFacet(String facetName) {
-        // TODO Where to add thid axis too.
-        return 0;
-    }
-
-
-    /**
-     * Do cross sections waterline out.
-     *
-     * @param seriesName name of the data (line) to display in legend.
-     * @param theme Theme for the data series.
-     */
-    protected void doCrossSectionWaterLineOut(
-            Object   o,
-            String   seriesName,
-            Document theme,
-            boolean  visible
-            ) {
-        logger.debug("CrossSectionGenerator.doCrossSectionWaterLineOut");
-
-        Lines.LineData lines = (Lines.LineData) o;
-        // DO NOT SORT DATA! This destroys the gaps indicated by NaNs.
-        StyledXYSeries series = new StyledXYSeries(seriesName, false, theme);
-
-        if (!ThemeUtil.parseShowLineLabel(theme)) {
-            series.setLabel("");
-        }
-        if (ThemeUtil.parseShowWidth(theme)) {
-            NumberFormat nf = Formatter.getMeterFormat(this.context);
-            String labelAdd = "b=" + nf.format(lines.width) + "m";
-            if (series.getLabel().length() == 0) {
-                series.setLabel(labelAdd);
-            }
-            else {
-                series.setLabel(series.getLabel() + ", " + labelAdd);
-            }
-        }
-        if (ThemeUtil.parseShowLevel(theme) && lines.points.length > 1
-                && lines.points[1].length > 0) {
-            NumberFormat nf = Formatter.getMeterFormat(this.context);
-            FLYSArtifact flys = (FLYSArtifact) master;
-
-            String unit = FLYSUtils.getRiver(flys).getWstUnit().getName();
-
-            String labelAdd = "W=" + nf.format(lines.points[1][0]) + unit;
-            if (series.getLabel().length() == 0) {
-                series.setLabel(labelAdd);
-            }
-            else {
-                series.setLabel(series.getLabel() + ", " + labelAdd);
-            }
-        }
-        if (ThemeUtil.parseShowMiddleHeight(theme) && lines.width != 0) {
-            NumberFormat nf = Formatter.getMeterFormat(this.context);
-            String labelAdd = "T=" + nf.format(lines.area / lines.width) + "m";
-            // : " + lines.area + "/" + lines.width);
-            if (series.getLabel().length() == 0) {
-                series.setLabel(labelAdd);
-            }
-            else {
-                series.setLabel(series.getLabel() + ", " + labelAdd);
-            }
-        }
-
-        StyledSeriesBuilder.addPoints(series, lines.points, false);
-
-        addAxisSeries(series, 0, visible);
-    }
-
-
-    /** Add HYK-Annotations (colorize and label some areas, draw lines. */
-    protected void doHyk(
-            Object   o,
-            String   seriesName,
-            Document theme,
-            boolean  visible
-            ) {
-        logger.debug("CrossSectionGenerator.doHyk");
-
-        List<HYKFactory.Zone> zones = (List<HYKFactory.Zone>) o;
-
-        if (zones == null || zones.isEmpty()) {
-            logger.warn("CrossSectionGenerator.doHYK: empty zone list received.");
-            return;
-        }
-
-        // Actual Styling is done in XYChartGenerator.
-        if (visible) {
-            addAnnotations(new FLYSAnnotation(seriesName, null, zones, theme));
-        }
-    }
-
-
-    /**
-     * Do cross sections out.
-     *
-     * @param seriesName name of the data (line) to display in legend.
-     * @param theme Theme for the data series.
-     */
-    protected void doCrossSectionOut(
-            Object   o,
-            String   seriesName,
-            Document theme,
-            boolean  visible
-            ) {
-        logger.debug("CrossSectionGenerator.doCrossSectionOut");
-
-        XYSeries series = new StyledXYSeries(seriesName, theme);
-
-        StyledSeriesBuilder.addPoints(series, (double [][]) o, false);
-
-        addAxisSeries(series, 0, visible);
-    }
-
-
-    /**
-     * Creates a new <i>ChartSection</i>.
-     *
-     * Overridden to prevent inclusion of subtitle.
-     *
-     * @return a new <i>ChartSection</i>.
-     */
-    @Override
-    protected ChartSection buildChartSection() {
-        ChartSection chartSection = new ChartSection();
-        chartSection.setTitle(getChartTitle());
-        chartSection.setDisplayGrid(isGridVisible());
-        chartSection.setDisplayLogo(showLogo());
-        chartSection.setLogoVPlacement(logoVPlace());
-        chartSection.setLogoHPlacement(logoHPlace());
-        return chartSection;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/CrossSectionInfoGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,14 +0,0 @@
-package de.intevation.flys.exports;
-
-
-/**
- * A ChartInfoGenerator that generates meta information for specific cross
- * sections.
- */
-public class CrossSectionInfoGenerator extends ChartInfoGenerator {
-
-    public CrossSectionInfoGenerator() {
-        super(new CrossSectionGenerator());
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,188 +0,0 @@
-package de.intevation.flys.exports;
-
-import de.intevation.artifactdatabase.state.ArtifactAndFacet;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.WQKms;
-import de.intevation.flys.jfree.Bounds;
-import de.intevation.flys.jfree.FLYSAnnotation;
-import de.intevation.flys.jfree.StyledXYSeries;
-import de.intevation.flys.model.Gauge;
-import de.intevation.flys.model.River;
-import de.intevation.flys.utils.FLYSUtils;
-
-import org.apache.log4j.Logger;
-import org.jfree.chart.axis.ValueAxis;
-import org.jfree.chart.plot.XYPlot;
-import org.jfree.data.Range;
-import org.jfree.data.xy.XYSeries;
-import org.w3c.dom.Document;
-
-
-/**
- * An OutGenerator that generates discharge curves.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class DischargeCurveGenerator
-extends      XYChartGenerator
-implements   FacetTypes {
-
-    public static enum YAXIS {
-        W(0);
-        protected int idx;
-        private YAXIS(int c) {
-            idx = c;
-        }
-    }
-
-    /** The logger used in this generator. */
-    private static Logger logger =
-        Logger.getLogger(DischargeCurveGenerator.class);
-
-    public static final String I18N_CHART_TITLE =
-        "chart.discharge.curve.title";
-
-    public static final String I18N_CHART_SUBTITLE =
-        "chart.discharge.curve.subtitle";
-
-    public static final String I18N_XAXIS_LABEL =
-        "chart.discharge.curve.xaxis.label";
-
-    public static final String I18N_YAXIS_LABEL =
-        "chart.discharge.curve.yaxis.label";
-
-    public static final String I18N_CHART_TITLE_DEFAULT  = "Abflusskurven";
-    public static final String I18N_XAXIS_LABEL_DEFAULT  = "Q [m\u00b3/s]";
-    public static final String I18N_YAXIS_LABEL_DEFAULT  = "W [cm]";
-
-
-    public DischargeCurveGenerator() {
-        super();
-    }
-
-
-    @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 always null to suppress subtitles.
-     */
-    @Override
-    protected String getDefaultChartTitle() {
-        return null;
-    }
-
-
-    @Override
-    protected String getDefaultXAxisLabel() {
-        return msg(I18N_XAXIS_LABEL, I18N_XAXIS_LABEL_DEFAULT);
-    }
-
-    @Override
-    protected String getDefaultYAxisLabel(int pos) {
-        return msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL_DEFAULT);
-    }
-
-
-    /* TODO is this one really needed? */
-    @Override
-    protected boolean zoomX(XYPlot plot, ValueAxis axis, Bounds bounds, Range x) {
-        boolean zoomin = super.zoom(plot, axis, bounds, x);
-
-        if (!zoomin) {
-            axis.setLowerBound(0d);
-        }
-
-        return zoomin;
-    }
-
-
-    @Override
-    public void doOut(
-        ArtifactAndFacet artifactFacet,
-        Document         theme,
-        boolean          visible
-    ) {
-        String name = artifactFacet.getFacetName();
-        logger.debug("DischargeCurveGenerator.doOut: " + name);
-
-        if (name.equals(DISCHARGE_CURVE)
-                || name.equals(GAUGE_DISCHARGE_CURVE)) {
-            doDischargeOut(
-                (FLYSArtifact)artifactFacet.getArtifact(),
-                artifactFacet.getData(context),
-                artifactFacet.getFacetDescription(),
-                theme,
-                visible);
-        }
-        else if (name.equals(COMPUTED_DISCHARGE_MAINVALUES_Q)
-                || name.equals(MAINVALUES_Q)
-                || name.equals(COMPUTED_DISCHARGE_MAINVALUES_W)
-                || name.equals(MAINVALUES_W))
-        {
-            doAnnotations((FLYSAnnotation) artifactFacet.getData(context),
-                artifactFacet, theme, visible);
-        }
-        else if (FacetTypes.IS.MANUALPOINTS(name)) {
-            doPoints(artifactFacet.getData(context),
-                artifactFacet,
-                theme, visible, YAXIS.W.idx);
-        }
-        else {
-           logger.warn("DischargeCurveGenerator.doOut: Unknown facet name: " + name);
-           return;
-        }
-    }
-
-
-    /**
-     * Add series with discharge curve to diagram.
-     */
-    protected void doDischargeOut(
-        FLYSArtifact artifact,
-        Object        o,
-        String        description,
-        Document      theme,
-        boolean       visible)
-    {
-        WQKms wqkms = (WQKms) o;
-
-        String gaugeName = wqkms.getName();
-
-        River river = FLYSUtils.getRiver(artifact);
-
-        if (river == null) {
-            logger.debug("no river found");
-            return;
-        }
-
-        Gauge gauge = river.determineGaugeByName(gaugeName);
-
-        if (gauge == null) {
-            logger.debug("no gauge found");
-            return;
-        }
-
-        XYSeries series = new StyledXYSeries(description, theme);
-
-        StyledSeriesBuilder.addPointsQW(series, wqkms);
-
-        addAxisSeries(series, YAXIS.W.idx, visible);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/DischargeCurveInfoGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,16 +0,0 @@
-package de.intevation.flys.exports;
-
-
-/**
- * A ChartInfoGenerator that generates meta information for specific discharge
- * curves.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class DischargeCurveInfoGenerator extends ChartInfoGenerator {
-
-    public DischargeCurveInfoGenerator() {
-        super(new DischargeCurveGenerator());
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionExporter.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,133 +0,0 @@
-package de.intevation.flys.exports;
-
-import au.com.bytecode.opencsv.CSVWriter;
-
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.WQCKms;
-import de.intevation.flys.artifacts.model.WQKms;
-
-import java.text.NumberFormat;
-
-import org.apache.log4j.Logger;
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class DischargeLongitudinalSectionExporter extends WaterlevelExporter {
-
-    /** The logger used in this exporter.*/
-    private static Logger logger =
-        Logger.getLogger(DischargeLongitudinalSectionExporter.class);
-
-
-    public static final String CSV_KM_HEADER =
-        "export.discharge.longitudinal.section.csv.header.km";
-
-    public static final String CSV_W_HEADER =
-        "export.discharge.longitudinal.section.csv.header.w";
-
-    public static final String CSV_CW_HEADER =
-        "export.discharge.longitudinal.section.csv.header.cw";
-
-    public static final String CSV_Q_HEADER =
-        "export.discharge.longitudinal.section.csv.header.q";
-
-    public static final String DEFAULT_CSV_KM_HEADER = "Fluss-Km";
-    public static final String DEFAULT_CSV_W_HEADER  = "W [NN + m]";
-    public static final String DEFAULT_CSV_CW_HEADER = "W korr.";
-    public static final String DEFAULT_CSV_Q_HEADER  = "Q [m\u00b3/s]";
-
-
-    @Override
-    protected void addData(Object d) {
-        if (d instanceof CalculationResult) {
-            d = ((CalculationResult)d).getData();
-            if (d instanceof WQKms []) {
-                data.add((WQKms [])d);
-            }
-        }
-    }
-
-
-    @Override
-    protected void writeCSVHeader(
-        CSVWriter writer,
-        boolean   atGauge,
-        boolean   isQ
-    ) {
-        logger.info("WaterlevelExporter.writeCSVHeader");
-
-        writer.writeNext(new String[] {
-            msg(CSV_KM_HEADER, DEFAULT_CSV_KM_HEADER),
-            msg(CSV_W_HEADER, DEFAULT_CSV_W_HEADER),
-            msg(CSV_CW_HEADER, DEFAULT_CSV_CW_HEADER),
-            msg(CSV_Q_HEADER, DEFAULT_CSV_Q_HEADER),
-            msg(CSV_Q_DESC_HEADER, DEFAULT_CSV_Q_DESC_HEADER)
-        });
-    }
-
-
-    protected void wQKms2CSV(
-        CSVWriter writer,
-        WQKms     wqkms,
-        boolean   atGauge,
-        boolean   isQ
-    ) {
-        logger.debug("WaterlevelExporter.wQKms2CSV");
-
-        int      size   = wqkms.size();
-        double[] result = new double[4];
-
-        NumberFormat kmf = getKmFormatter();
-        NumberFormat wf  = getWFormatter();
-        NumberFormat qf  = getQFormatter();
-
-        for (int i = 0; i < size; i ++) {
-            result = wqkms.get(i, result);
-
-            String name = wqkms.getName();
-            String wc = "";
-            if (wqkms instanceof WQCKms) {
-                wc = wf.format(result[3]);
-            }
-
-            writer.writeNext(new String[] {
-                kmf.format(result[2]),
-                wf.format(result[0]),
-                wc,
-                qf.format(result[1]),
-                name
-            });
-        }
-    }
-
-
-    @Override
-    protected void addWSTColumn(WstWriter writer, WQKms wqkms) {
-        String name = wqkms.getName();
-
-        // is it a W or a Q mode?
-        int wIdx = name.indexOf("W");
-        int qIdx = name.indexOf("Q");
-
-        String wq = null;
-        if (wIdx >= 0) {
-            wq = "W";
-        }
-        else if (qIdx >= 0) {
-            wq = "Q";
-        }
-
-        // we just want to display the first W or Q value in the WST
-        int start = name.indexOf("(");
-        int end   = name.indexOf(")");
-
-        String   tmp    = name.substring(start+1, end);
-        String[] values = tmp.split(";");
-
-        String column = wq + "=" + values[0];
-
-        writer.addColumn(column);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,125 +0,0 @@
-package de.intevation.flys.exports;
-
-import org.apache.log4j.Logger;
-
-import org.jfree.data.xy.XYSeries;
-
-import org.w3c.dom.Document;
-
-import de.intevation.artifactdatabase.state.ArtifactAndFacet;
-import de.intevation.artifactdatabase.state.Facet;
-
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.WQCKms;
-import de.intevation.flys.artifacts.model.WQKms;
-import de.intevation.flys.exports.process.Processor;
-import de.intevation.flys.exports.process.WOutProcessor;
-
-import de.intevation.flys.jfree.FLYSAnnotation;
-import de.intevation.flys.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((FLYSAnnotation) artifactFacet.getData(context),
-                 artifactFacet, attr, visible);
-        }
-        else if (FacetTypes.IS.MANUALPOINTS(name)) {
-            doPoints(artifactFacet.getData(context),
-                artifactFacet,
-                attr, visible, YAXIS.W.idx);
-        }
-        else {
-            Processor processor = new WOutProcessor();
-            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/flys-artifacts/src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionInfoGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-package de.intevation.flys.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/flys-artifacts/src/main/java/de/intevation/flys/exports/DoubleAttribute.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,34 +0,0 @@
-package de.intevation.flys.exports;
-
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class DoubleAttribute extends VisibleAttribute {
-
-
-    public DoubleAttribute(String name, double value, boolean visible) {
-        super(name, value, visible);
-    }
-
-
-    /**
-     * Calls VisibleAttribute.toXML() and appends afterwards an attribute
-     * <i>type</i> with value <i>double</i>.
-     *
-     * @param parent The parent Node.
-     *
-     * @return the new Node that represents this Attribute.
-     */
-    @Override
-    public Node toXML(Node parent) {
-        Element ele = (Element) super.toXML(parent);
-        ele.setAttribute("type", "double");
-
-        return ele;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/DurationCurveExporter.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,263 +0,0 @@
-package de.intevation.flys.exports;
-
-import java.io.OutputStream;
-import java.text.NumberFormat;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.HashMap;
-import java.util.Date;
-import java.text.DateFormat;
-import java.util.Locale;
-
-import org.w3c.dom.Document;
-
-import org.apache.log4j.Logger;
-
-import au.com.bytecode.opencsv.CSVWriter;
-
-import net.sf.jasperreports.engine.JasperExportManager;
-import net.sf.jasperreports.engine.JasperFillManager;
-import net.sf.jasperreports.engine.JasperPrint;
-import net.sf.jasperreports.engine.JRException;
-
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.common.utils.Config;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.flys.artifacts.access.RangeAccess;
-import de.intevation.flys.artifacts.model.WQDay;
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.WKmsJRDataSource;
-import de.intevation.flys.artifacts.resources.Resources;
-
-import de.intevation.flys.utils.FLYSUtils;
-import de.intevation.flys.utils.Formatter;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class DurationCurveExporter extends AbstractExporter {
-
-    /** The logger used in this exporter. */
-    private static Logger logger = Logger.getLogger(DurationCurveExporter.class);
-
-
-    public static final String CSV_DURATION_HEADER =
-        "export.duration.curve.csv.header.duration";
-
-    public static final String CSV_W_HEADER =
-        "export.duration.curve.csv.header.w";
-
-    public static final String CSV_Q_HEADER =
-        "export.duration.curve.csv.header.q";
-
-    public static final String DEFAULT_CSV_DURATION_HEADER = "D [Tagen]";
-    public static final String DEFAULT_CSV_W_HEADER  = "W [NN + m]";
-    public static final String DEFAULT_CSV_Q_HEADER  = "Q [m\u00b3/s]";
-
-    public static final String PDF_HEADER_MODE = "export.duration.pdf.mode";
-    public static final String JASPER_FILE = "export.duration.pdf.file";
-
-    /** 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>();
-    }
-
-
-    @Override
-    protected void addData(Object d) {
-        if (d instanceof CalculationResult) {
-            d = ((CalculationResult)d).getData();
-            if (d instanceof WQDay) {
-                data.add((WQDay)d);
-            }
-        }
-    }
-
-
-    protected void writeCSVData(CSVWriter writer) {
-        logger.info("DurationCurveExporter.writeData");
-
-        writeCSVHeader(writer);
-
-        for (WQDay wqday: data) {
-            wQDay2CSV(writer, wqday);
-        }
-    }
-
-
-    protected void writeCSVHeader(CSVWriter writer) {
-        logger.info("DurationCurveExporter.writeCSVHeader");
-
-        writer.writeNext(new String[] {
-            msg(CSV_W_HEADER, DEFAULT_CSV_W_HEADER),
-            msg(CSV_Q_HEADER, DEFAULT_CSV_Q_HEADER),
-            msg(CSV_DURATION_HEADER, DEFAULT_CSV_DURATION_HEADER)
-        });
-    }
-
-
-    protected void wQDay2CSV(CSVWriter writer, WQDay wqday) {
-        logger.debug("DurationCurveExporter.wQDay2CSV");
-
-        int size = wqday.size();
-
-        NumberFormat wf  = getWFormatter();
-        NumberFormat qf  = getQFormatter();
-        NumberFormat df  = getDFormatter();
-
-        if (wqday.isIncreasing()) {
-            for (int i = size-1; i >= 0; i --) {
-                writer.writeNext(new String[] {
-                    wf.format(wqday.getW(i)),
-                    qf.format(wqday.getQ(i)),
-                    df.format(wqday.getDay(i))
-                });
-            }
-        }
-        else {
-            for (int i = 0; i < size; i ++) {
-                writer.writeNext(new String[] {
-                    wf.format(wqday.getW(i)),
-                    qf.format(wqday.getQ(i)),
-                    df.format(wqday.getDay(i))
-                });
-            }
-        }
-    }
-
-
-    /**
-     * Returns the number formatter for W values.
-     *
-     * @return the number formatter for W values.
-     */
-    @Override
-    protected NumberFormat getWFormatter() {
-        return Formatter.getDurationW(context);
-    }
-
-
-    /**
-     * Returns the number formatter for Q values.
-     *
-     * @return the number formatter for Q values.
-     */
-    @Override
-    protected NumberFormat getQFormatter() {
-        return Formatter.getDurationQ(context);
-    }
-
-
-    /**
-     * Returns the number formatter for duration values.
-     *
-     * @return the number formatter for duration values.
-     */
-    protected NumberFormat getDFormatter() {
-        return Formatter.getDurationD(context);
-    }
-
-
-    @Override
-    protected void writePDF(OutputStream out) {
-        WKmsJRDataSource source = createJRData();
-
-        String jasperFile = Resources.getMsg(
-                                context.getMeta(),
-                                JASPER_FILE,
-                                "/jasper/duration_en.jasper");
-        String confPath = Config.getConfigDirectory().toString();
-
-        Map parameters = new HashMap();
-        parameters.put("ReportTitle", "Exported Data");
-        try {
-            JasperPrint print = JasperFillManager.fillReport(
-                confPath + jasperFile,
-                parameters,
-                source);
-            JasperExportManager.exportReportToPdfStream(print, out);
-        }
-        catch(JRException je) {
-            logger.warn("Error generating PDF Report!");
-            je.printStackTrace();
-        }
-    }
-
-    protected WKmsJRDataSource createJRData() {
-        WKmsJRDataSource source = new WKmsJRDataSource();
-
-        addMetaData(source);
-        for (WQDay wqday: data) {
-            addWQDayData(source, wqday);
-        }
-
-        return source;
-    }
-
-
-    protected void addMetaData(WKmsJRDataSource source) {
-        CallMeta meta = context.getMeta();
-
-        FLYSArtifact flys = (FLYSArtifact) master;
-
-        source.addMetaData ("river", FLYSUtils.getRivername(flys));
-
-        Locale locale = Resources.getLocale(meta);
-        DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, locale);
-
-        source.addMetaData("date", df.format(new Date()));
-
-        RangeAccess rangeAccess = new RangeAccess(flys, null);
-        double[] kms = rangeAccess.getKmRange();
-        source.addMetaData("range", String.valueOf(kms[0]));
-
-        source.addMetaData("calculation", Resources.getMsg(
-                                            locale,
-                                            PDF_HEADER_MODE,
-                                            "Duration"));
-    }
-
-    protected void addWQDayData(WKmsJRDataSource source, WQDay wqday) {
-        int size = wqday.size();
-
-        NumberFormat wf  = getWFormatter();
-        NumberFormat qf  = getQFormatter();
-        NumberFormat df  = getDFormatter();
-
-        if (wqday.isIncreasing()) {
-            for (int i = size-1; i >= 0; i --) {
-                source.addData(new String[] {
-                    "",
-                    wf.format(wqday.getW(i)),
-                    qf.format(wqday.getQ(i)),
-                    "", "", "",
-                    df.format(wqday.getDay(i))
-                });
-            }
-        }
-        else {
-            for (int i = 0; i < size; i ++) {
-                source.addData(new String[] {
-                    "",
-                    wf.format(wqday.getW(i)),
-                    qf.format(wqday.getQ(i)),
-                    "", "", "",
-                    df.format(wqday.getDay(i))
-                });
-            }
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/DurationCurveGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,313 +0,0 @@
-package de.intevation.flys.exports;
-
-import de.intevation.artifactdatabase.state.ArtifactAndFacet;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.WQDay;
-import de.intevation.flys.jfree.Bounds;
-import de.intevation.flys.jfree.FLYSAnnotation;
-import de.intevation.flys.jfree.StyledXYSeries;
-
-import java.awt.Font;
-import java.awt.geom.Point2D;
-
-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.Range;
-import org.jfree.data.xy.XYSeries;
-import org.w3c.dom.Document;
-
-
-/**
- * An OutGenerator that generates duration curves.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class DurationCurveGenerator
-extends      XYChartGenerator
-implements   FacetTypes
-{
-    public static enum YAXIS {
-        W(0),
-        Q(1);
-        public int idx;
-        private YAXIS(int c) {
-           idx = c;
-        }
-    }
-
-    /** Local logger. */
-    private static Logger logger =
-        Logger.getLogger(DurationCurveGenerator.class);
-
-    public static final String I18N_CHART_TITLE =
-        "chart.duration.curve.title";
-
-    public static final String I18N_CHART_SUBTITLE =
-        "chart.duration.curve.subtitle";
-
-    public static final String I18N_XAXIS_LABEL =
-        "chart.duration.curve.xaxis.label";
-
-    public static final String I18N_YAXIS_LABEL =
-        "chart.duration.curve.yaxis.label";
-
-    public static final String I18N_CHART_TITLE_DEFAULT  =
-        "Dauerlinie";
-
-    public static final String I18N_XAXIS_LABEL_DEFAULT  =
-        "Unterschreitungsdauer [Tage]";
-
-    public static final String I18N_YAXIS_LABEL_DEFAULT  =
-        "W [NN + m]";
-
-
-    public DurationCurveGenerator() {
-        super();
-    }
-
-
-    /**
-     * Create Axis for given index.
-     * @return axis with according internationalized label.
-     */
-    @Override
-    protected NumberAxis createYAxis(int index) {
-        Font labelFont = new Font("Tahoma", Font.BOLD, 14);
-        String label   = getYAxisLabel(index);
-
-        NumberAxis axis = createNumberAxis(index, label);
-        if (index == YAXIS.W.idx) {
-            axis.setAutoRangeIncludesZero(false);
-        }
-        axis.setLabelFont(labelFont);
-        return axis;
-    }
-
-
-    @Override
-    protected String getDefaultChartTitle() {
-        return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT);
-    }
-
-
-    @Override
-    protected String getDefaultChartSubtitle() {
-        double[] dist  = getRange();
-
-        Object[] args = new Object[] {
-            getRiverName(),
-            dist[0]
-        };
-
-        return msg(I18N_CHART_SUBTITLE, "", args);
-    }
-
-
-    @Override
-    protected String getDefaultXAxisLabel() {
-        return msg(I18N_XAXIS_LABEL, I18N_XAXIS_LABEL_DEFAULT);
-    }
-
-
-    @Override
-    protected String getDefaultYAxisLabel(int index) {
-        String label = "default";
-        if (index == YAXIS.W.idx) {
-            label = msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL_DEFAULT);
-        }
-        else if (index == YAXIS.Q.idx) {
-            // TODO i18n for this label
-            label = "Q [m\u00b3/s]";
-            //label = msg(get2YAxisLabelKey(), get2YAxisDefaultLabel());
-        }
-
-        return label;
-    }
-
-
-    @Override
-    protected boolean zoomX(XYPlot plot, ValueAxis axis, Bounds bounds, Range x) {
-        boolean zoomin = super.zoom(plot, axis, bounds, x);
-
-        if (!zoomin) {
-            axis.setLowerBound(0d);
-        }
-
-        axis.setUpperBound(364);
-
-        return zoomin;
-    }
-
-
-    /**
-     * This method overrides the method in the parent class to set the lower
-     * bounds of the Q axis to 0. This axis should never display negative
-     * values on its own.
-     */
-    @Override
-    protected boolean zoomY(XYPlot plot, ValueAxis axis, Bounds bounds, Range x) {
-        boolean zoomin = super.zoom(plot, axis, bounds, x);
-
-        if (!zoomin && axis instanceof IdentifiableNumberAxis) {
-            String id = ((IdentifiableNumberAxis) axis).getId();
-
-            if (YAXIS.Q.toString().equals(id)) {
-                axis.setLowerBound(0d);
-            }
-        }
-
-        return zoomin;
-    }
-
-
-    @Override
-    public void doOut(
-        ArtifactAndFacet artifactFacet,
-        Document         attr,
-        boolean          visible
-    ) {
-        String name = artifactFacet.getFacetName();
-
-        logger.debug("DurationCurveGenerator.doOut: " + name);
-
-        if (name == null || name.length() == 0) {
-            logger.error("No facet given. Cannot create dataset.");
-            return;
-        }
-
-        if (name.equals(DURATION_W)) {
-            doWOut(
-                (WQDay) artifactFacet.getData(context),
-                artifactFacet,
-                attr,
-                visible);
-        }
-        else if (name.equals(DURATION_Q)) {
-            doQOut(
-                (WQDay) artifactFacet.getData(context),
-                artifactFacet,
-                attr,
-                visible);
-        }
-        else if (name.equals(DURATION_MAINVALUES_Q)
-                || name.equals(MAINVALUES_Q)
-                || name.equals(COMPUTED_DISCHARGE_MAINVALUES_W)
-                || name.equals(MAINVALUES_W)
-        ) {
-            doAnnotations(
-                (FLYSAnnotation) artifactFacet.getData(context),
-                artifactFacet,
-                attr,
-                visible);
-        }
-        else if (name.equals(RELATIVE_POINT)) {
-            doPointOut((Point2D) artifactFacet.getData(context),
-                artifactFacet,
-                attr,
-                visible);
-        }
-        else if (FacetTypes.IS.MANUALPOINTS(name)) {
-            doPoints(
-                artifactFacet.getData(context),
-                artifactFacet,
-                attr, visible, YAXIS.W.idx);
-        }
-        else {
-            logger.warn("Unknown facet name: " + name);
-            return;
-        }
-    }
-
-
-    /**
-     * Creates the series for a duration curve's W facet.
-     *
-     * @param wqdays The WQDay store that contains the Ws.
-     * @param theme
-     */
-    protected void doWOut(
-        WQDay            wqdays,
-        ArtifactAndFacet aaf,
-        Document         theme,
-        boolean          visible
-    ) {
-        logger.debug("DurationCurveGenerator.doWOut");
-
-        XYSeries series = new StyledXYSeries(aaf.getFacetDescription(), theme);
-
-        int size = wqdays.size();
-        for (int i = 0; i < size; i++) {
-            int  day = wqdays.getDay(i);
-            double w = wqdays.getW(i);
-
-            series.add(day, w);
-        }
-
-        addAxisSeries(series, YAXIS.W.idx, visible);
-    }
-
-    protected void doPointOut(
-        Point2D point,
-        ArtifactAndFacet aandf,
-        Document theme,
-        boolean visible
-    ){
-        logger.debug("DurationCurveGenerator.doPointOut");
-
-        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
-
-        series.add(point.getX(), point.getY());
-
-        addAxisSeries(series, YAXIS.W.idx, visible);
-    }
-
-
-    /**
-     * Creates the series for a duration curve's Q facet.
-     *
-     * @param wqdays The WQDay store that contains the Qs.
-     * @param theme
-     */
-    protected void doQOut(
-        WQDay            wqdays,
-        ArtifactAndFacet aaf,
-        Document         theme,
-        boolean          visible
-    ) {
-        logger.debug("DurationCurveGenerator.doQOut");
-
-        XYSeries series = new StyledXYSeries(aaf.getFacetDescription(), theme);
-
-        int size = wqdays.size();
-        for (int i = 0; i < size; i++) {
-            int  day = wqdays.getDay(i);
-            double q = wqdays.getQ(i);
-
-            series.add(day, q);
-        }
-
-        addAxisSeries(series, YAXIS.Q.idx, visible);
-    }
-
-
-    @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();
-            }
-        };
-    }
-
-    // MainValue-Annotations should be visualized by a line that goes to the curve itself.
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/DurationCurveInfoGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-package de.intevation.flys.exports;
-
-
-/**
- * A ChartInfoGenerator that generates meta information for specific duration
- * curves.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class DurationCurveInfoGenerator
-extends      ChartInfoGenerator
-{
-    public DurationCurveInfoGenerator() {
-        super(new DurationCurveGenerator());
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/EmptySettings.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,78 +0,0 @@
-package de.intevation.flys.exports;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Node;
-
-import de.intevation.artifactdatabase.state.Settings;
-import de.intevation.artifactdatabase.state.Section;
-
-
-/**
- * An implementation of <i>Settings</i> that doesn't take new <i>Section</i>s
- * and that always creates an empty <b>settings</b> DOM node in its
- * <i>toXML()</i> operation.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class EmptySettings implements Settings {
-
-    public EmptySettings() {
-    }
-
-
-    /**
-     * This method has no function. It is not implemented!
-     *
-     * @param section A Section.
-     */
-    @Override
-    public void addSection(Section section) {
-        // do nothing
-    }
-
-
-    /**
-     * Always returns 0.
-     *
-     * @return 0.
-     */
-    @Override
-    public int getSectionCount() {
-        return 0;
-    }
-
-
-    /**
-     * This method always returns null. It is not implemented!
-     *
-     * @param pos A position.
-     *
-     * @return null.
-     */
-    @Override
-    public Section getSection(int pos) {
-        return null;
-    }
-
-
-    /**
-     * This method has no function. It is not implemented!
-     */
-    @Override
-    public void removeSection(Section section) {
-        // do nothing
-    }
-
-
-    /**
-     * This method creates an empty <i>settings</i> DOM node.
-     *
-     * @param parent A parent DOM node.
-     */
-    @Override
-    public void toXML(Node parent) {
-        Document owner = parent.getOwnerDocument();
-        parent.appendChild(owner.createElement("settings"));
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/ExportSection.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-package de.intevation.flys.exports;
-
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class ExportSection extends TypeSection {
-
-    public static final String WIDTH_ATTR  = "width";
-    public static final String HEIGHT_ATTR = "height";
-
-
-    public ExportSection() {
-        super("export");
-    }
-
-
-    public void setWidth(int width) {
-        if (width <= 0) {
-            return;
-        }
-
-        setIntegerValue(WIDTH_ATTR, width);
-    }
-
-
-    public Integer getWidth() {
-        return getIntegerValue(WIDTH_ATTR);
-    }
-
-
-    public void setHeight(int height) {
-        if (height <= 0) {
-            return;
-        }
-
-        setIntegerValue(HEIGHT_ATTR, height);
-    }
-
-
-    public Integer getHeight() {
-        return getIntegerValue(HEIGHT_ATTR);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/FlowVelocityExporter.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,127 +0,0 @@
-package de.intevation.flys.exports;
-
-import java.io.OutputStream;
-import java.text.NumberFormat;
-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 de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.FlowVelocityData;
-import de.intevation.flys.utils.FLYSUtils;
-import de.intevation.flys.utils.Formatter;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class FlowVelocityExporter extends AbstractExporter {
-
-    private static final Logger logger =
-        Logger.getLogger(FlowVelocityExporter.class);
-
-
-    public static final String CSV_KM =
-        "export.flow_velocity.csv.header.km";
-
-    public static final String CSV_V_TOTAL =
-        "export.flow_velocity.csv.header.v_total";
-
-    public static final String CSV_V_MAIN =
-        "export.flow_velocity.csv.header.v_main";
-
-    public static final String CSV_TAU_MAIN =
-        "export.flow_velocity.csv.header.tau_main";
-
-    public static final String CSV_Q =
-        "export.flow_velocity.csv.header.q";
-
-    public static final String CSV_LOCATIONS =
-        "export.flow_velocity.csv.header.locations";
-
-
-    protected List<FlowVelocityData[]> data;
-
-
-    public void init(Document request, OutputStream out, CallContext cc) {
-        super.init(request, out, cc);
-        data = new ArrayList<FlowVelocityData[]>();
-    }
-
-
-    @Override
-    protected void addData(Object d) {
-        if (d instanceof CalculationResult) {
-            d = ((CalculationResult) d).getData();
-
-            if (d instanceof FlowVelocityData[]) {
-                logger.debug("Add new data of type FlowVelocityData");
-                data.add((FlowVelocityData[]) d);
-            }
-        }
-    }
-
-
-    @Override
-    protected void writeCSVData(CSVWriter writer) {
-        logger.info("FlowVelocityExporter.writeCSVData");
-        logger.debug("CSV gets " + data.size() + " FlowVelocityData objects.");
-
-        writeCSVHeader(writer);
-
-        for (FlowVelocityData[] d: data) {
-            data2CSV(writer, d);
-        }
-    }
-
-
-    protected void writeCSVHeader(CSVWriter writer) {
-        writer.writeNext(new String[] {
-            msg(CSV_KM, CSV_KM),
-            msg(CSV_V_TOTAL, CSV_V_TOTAL),
-            msg(CSV_V_MAIN, CSV_V_MAIN),
-            msg(CSV_TAU_MAIN, CSV_TAU_MAIN),
-            msg(CSV_Q, CSV_Q),
-            msg(CSV_LOCATIONS, CSV_LOCATIONS)
-        });
-    }
-
-
-    protected void data2CSV(CSVWriter writer, FlowVelocityData[] fData) {
-        logger.debug("Add next FlowVelocityData to CSV");
-
-        FLYSArtifact flys = (FLYSArtifact) master;
-
-        for (FlowVelocityData data: fData) {
-            for (int i = 0, n = data.size(); i < n; i++) {
-                NumberFormat kmF  = Formatter.getFlowVelocityKM(context);
-                NumberFormat valF = Formatter.getFlowVelocityValues(context);
-                NumberFormat qF   = Formatter.getFlowVelocityQ(context);
-
-                writer.writeNext(new String[] {
-                    kmF.format(data.getKM(i)),
-                    valF.format(data.getVMain(i)),
-                    valF.format(data.getVTotal(i)),
-                    valF.format(data.getTauMain(i)),
-                    qF.format(data.getQ(i)) + "=" + data.getZone(),
-                    FLYSUtils.getLocationDescription(flys, data.getKM(i)),
-                });
-            }
-        }
-    }
-
-
-    @Override
-    protected void writePDF(OutputStream out) {
-        logger.error("TODO: Implement FlowVelocityExporter.writePDF");
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/FlowVelocityGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,547 +0,0 @@
-package de.intevation.flys.exports;
-
-import java.util.Arrays;
-
-import org.apache.log4j.Logger;
-
-import org.jfree.data.xy.XYSeries;
-
-import org.w3c.dom.Document;
-
-import de.intevation.artifactdatabase.state.ArtifactAndFacet;
-import de.intevation.artifactdatabase.state.Facet;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.flys.artifacts.access.FlowVelocityAccess;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.FlowVelocityData;
-import de.intevation.flys.artifacts.model.minfo.BedDiameterResult;
-import de.intevation.flys.artifacts.model.minfo.BedloadDiameterResult;
-import de.intevation.flys.model.FlowVelocityMeasurementValue;
-
-import de.intevation.flys.jfree.Bounds;
-import de.intevation.flys.jfree.DoubleBounds;
-import de.intevation.flys.jfree.FLYSAnnotation;
-import de.intevation.flys.jfree.StyledXYSeries;
-
-import de.intevation.flys.utils.FLYSUtils;
-
-
-/**
- * An OutGenerator that generates flow velocity curves.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class FlowVelocityGenerator
-extends      XYChartGenerator
-implements   FacetTypes
-{
-    public enum YAXIS {
-        V(0),
-        T(1),
-        Q(2),
-        D(3);
-        /* TODO Q and Density will come as 4th and 3rd axis. */
-        protected int idx;
-        private YAXIS(int c) {
-           idx = c;
-        }
-    }
-
-    /** The logger that is used in this generator. */
-    private static Logger logger = Logger.getLogger(FlowVelocityGenerator.class);
-
-    /** Key to look up internationalized String for annotations label. */
-    public static final String I18N_ANNOTATIONS_LABEL =
-        "chart.flow_velocity.annotations.label";
-
-    /**
-     * Key to look up internationalized String for LongitudinalSection diagrams
-     * titles.
-     */
-    public static final String I18N_CHART_TITLE =
-        "chart.flow_velocity.section.title";
-
-    /**
-     * Key to look up internationalized String for LongitudinalSection diagrams
-     * subtitles.
-     */
-    public static final String I18N_CHART_SUBTITLE =
-        "chart.flow_velocity.section.subtitle";
-
-    /**
-     * Key to look up internationalized String for LongitudinalSection diagrams
-     * short subtitles.
-     */
-    public static final String I18N_CHART_SHORT_SUBTITLE =
-        "chart.flow_velocity.section.shortsubtitle";
-
-    public static final String I18N_XAXIS_LABEL =
-        "chart.flow_velocity.section.xaxis.label";
-
-    public static final String I18N_YAXIS_LABEL =
-        "chart.flow_velocity.section.yaxis.label";
-
-    public static final String I18N_2YAXIS_LABEL =
-        "chart.flow_velocity.section.yaxis.second.label";
-
-    public static final String I18N_3YAXIS_LABEL =
-        "chart.flow_velocity.section.yaxis.third.label";
-    public static final String I18N_4YAXIS_LABEL = "chart.bedquality.yaxis.label.diameter";
-
-    public static final String I18N_CHART_TITLE_DEFAULT  = "Geschwindigkeit- und Schubspannung";
-    public static final String I18N_XAXIS_LABEL_DEFAULT  = "km";
-    public static final String I18N_YAXIS_LABEL_DEFAULT  = "Geschwindigkeit v [m/s]";
-    public static final String I18N_2YAXIS_LABEL_DEFAULT = "Schubspannung Tau [N]";
-    public static final String I18N_3YAXIS_LABEL_DEFAULT = "Q [m³/s]";
-    public static final String I18N_4YAXIS_LABEL_DEFAULT = "Durchmesser [mm]";
-
-    @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() {
-        FLYSArtifact flys = (FLYSArtifact) master;
-
-        return msg(
-            I18N_XAXIS_LABEL,
-            I18N_XAXIS_LABEL_DEFAULT,
-            new Object[] { FLYSUtils.getRiver(flys).getName() });
-    }
-
-
-    @Override
-    protected String getDefaultYAxisLabel(int index) {
-        String label = "default";
-
-        if (index == YAXIS.V.idx) {
-            label = getVAxisLabel();
-        }
-        else if (index == YAXIS.T.idx) {
-            label = getTAxisLabel();
-        }
-        else if (index == YAXIS.Q.idx) {
-            label = getQAxisLabel();
-        }
-        else if (index == YAXIS.D.idx) {
-            label = getDAxisLabel();
-        }
-
-        return label;
-    }
-
-
-    /**
-     * Get internationalized label for the y axis.
-     */
-    protected String getVAxisLabel() {
-        return msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL_DEFAULT);
-    }
-
-
-    /**
-     * Get internationalized label for the y axis.
-     */
-    protected String getQAxisLabel() {
-        return msg(I18N_3YAXIS_LABEL, I18N_3YAXIS_LABEL_DEFAULT);
-    }
-
-    /**
-     * Get internationalized label for the y axis.
-     */
-    protected String getTAxisLabel() {
-        return msg(I18N_2YAXIS_LABEL, I18N_2YAXIS_LABEL_DEFAULT);
-    }
-
-    /**
-     * Get internationalized label for the y axis.
-     */
-    protected String getDAxisLabel() {
-        return msg(I18N_4YAXIS_LABEL, I18N_4YAXIS_LABEL_DEFAULT);
-    }
-
-    /**
-     * Produce output.
-     * @param artifactAndFacet current facet.
-     * @param attr  theme for facet
-     * @param visible Whether this facets data is actually visible or not.
-     */
-    public void doOut(
-        ArtifactAndFacet artifactAndFacet,
-        Document         attr,
-        boolean          visible
-    ) {
-        String name = artifactAndFacet.getFacetName();
-
-        logger.debug("FlowVelocityGenerator.doOut: " + name);
-
-        if (name == null) {
-            logger.error("No facet name for doOut(). No output generated!");
-            return;
-        }
-
-        Facet facet = artifactAndFacet.getFacet();
-
-        if (facet == null) {
-            return;
-        }
-
-        if (getXBounds(0) != null && getDomainAxisRange() != null) {
-            logger.debug(Arrays.toString(getDomainAxisRangeFromRequest()));
-            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) {
-            FLYSArtifact artifact = (FLYSArtifact)artifactAndFacet.getArtifact();
-            FlowVelocityAccess access = new FlowVelocityAccess(artifact, context);
-            context.putContextValue("startkm", access.getLowerKM());
-            context.putContextValue("endkm", access.getUpperKM());
-        }
-        else if (getXBounds(0) == null && getDomainAxisRange() != null){
-            FLYSArtifact artifact = (FLYSArtifact)artifactAndFacet.getArtifact();
-            FlowVelocityAccess access = new FlowVelocityAccess(artifact, context);
-            Bounds b = new DoubleBounds(access.getLowerKM(), access.getUpperKM());
-            Bounds bounds =
-                calculateZoom(b, getDomainAxisRange());
-            context.putContextValue("startkm", bounds.getLower());
-            context.putContextValue("endkm", bounds.getUpper());
-        }
-        if (name.equals(FLOW_VELOCITY_MAINCHANNEL)) {
-            doMainChannelOut(
-                (FlowVelocityData) artifactAndFacet.getData(context),
-                artifactAndFacet,
-                attr,
-                visible);
-        }
-        else if (name.equals(FLOW_VELOCITY_TOTALCHANNEL)) {
-            doTotalChannelOut(
-                (FlowVelocityData) artifactAndFacet.getData(context),
-                artifactAndFacet,
-                attr,
-                visible);
-        }
-        else if (name.equals(FLOW_VELOCITY_MAINCHANNEL_FILTERED)) {
-            doMainChannelOut(
-                (FlowVelocityData) artifactAndFacet.getData(context),
-                artifactAndFacet,
-                attr,
-                visible);
-        }
-        else if (name.equals(FLOW_VELOCITY_TOTALCHANNEL_FILTERED)) {
-            doTotalChannelOut(
-                (FlowVelocityData) artifactAndFacet.getData(context),
-                artifactAndFacet,
-                attr,
-                visible);
-        }
-        else if (name.equals(FLOW_VELOCITY_DISCHARGE)) {
-            doQOut(
-                (FlowVelocityData) artifactAndFacet.getData(context),
-                artifactAndFacet,
-                attr,
-                visible);
-        }
-        else if (name.equals(FLOW_VELOCITY_TAU)) {
-            doTauOut(
-                (FlowVelocityData) artifactAndFacet.getData(context),
-                artifactAndFacet,
-                attr,
-                visible);
-        }
-        else if (name.equals(FLOW_VELOCITY_TAU_FILTERED)) {
-            doTauOut(
-                (FlowVelocityData) artifactAndFacet.getData(context),
-                artifactAndFacet,
-                attr,
-                visible);
-        }
-
-        else if (name.equals(FLOW_VELOCITY_ANNOTATION)) {
-            doAnnotations(
-                (FLYSAnnotation) 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.V.idx);
-        }
-        else if (name.equals(LONGITUDINAL_ANNOTATION)) {
-            doAnnotations(
-                (FLYSAnnotation) artifactAndFacet.getData(context),
-                 artifactAndFacet,
-                 attr,
-                 visible);
-        }
-        else if (name.equals(FLOW_VELOCITY_MEASUREMENT)) {
-            doVPointOut(
-                 artifactAndFacet.getData(context),
-                 artifactAndFacet,
-                 attr,
-                 visible);
-        }
-        else if (name.equals(BED_QUALITY_BED_DIAMETER_SUBLAYER)) {
-            doBedQualitySubLayerOut(
-                (BedDiameterResult)artifactAndFacet.getData(context),
-                artifactAndFacet,
-                attr,
-                visible);
-        }
-        else if (name.equals(BED_QUALITY_BED_DIAMETER_TOPLAYER)) {
-            doBedQualityTopLayerOut(
-                (BedDiameterResult)artifactAndFacet.getData(context),
-                artifactAndFacet,
-                attr,
-                visible);
-        }
-        else if (name.equals(BED_QUALITY_BEDLOAD_DIAMETER)) {
-            doBedQualityLoadDiameter(
-                (BedloadDiameterResult)artifactAndFacet.getData(context),
-                artifactAndFacet,
-                attr,
-                visible);
-        }
-        else {
-            logger.warn("Unknown facet name: " + name);
-            return;
-        }
-    }
-
-
-    private void doBedQualityLoadDiameter(
-        BedloadDiameterResult data,
-        ArtifactAndFacet aandf,
-        Document attr,
-        boolean visible) {
-        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), attr);
-        StyledSeriesBuilder.addPoints(series, data.getDiameterData(), true);
-
-        addAxisSeries(series, YAXIS.D.idx, visible);
-    }
-
-
-    private void doBedQualityTopLayerOut(
-        BedDiameterResult data,
-        ArtifactAndFacet aandf,
-        Document attr,
-        boolean visible) {
-        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), attr);
-        StyledSeriesBuilder.addPoints(series, data.getDiameterSubData(), true);
-        addAxisSeries(series, YAXIS.D.idx, visible);
-    }
-
-
-    private void doBedQualitySubLayerOut(
-        BedDiameterResult data,
-        ArtifactAndFacet aandf,
-        Document attr,
-        boolean visible
-    ) {
-        logger.debug("Do beddiametersubout");
-        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), attr);
-        StyledSeriesBuilder.addPoints(series, data.getDiameterSubData(), true);
-        addAxisSeries(series, YAXIS.D.idx, visible);
-    }
-
-
-    /**
-     * Process the output for W facets in a longitudinal section curve.
-     *
-     * @param data A FlowVelocityData object
-     * @param aandf The facet. This facet does NOT support any data objects. Use
-     * FLYSArtifact.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 doMainChannelOut(
-        FlowVelocityData data,
-        ArtifactAndFacet aandf,
-        Document         theme,
-        boolean          visible
-    ) {
-        logger.debug("FlowVelocityGenerator.doMainChannelOut");
-
-        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
-
-        StyledSeriesBuilder.addPoints(series, data.getMainChannelPoints(), true);
-
-        addAxisSeries(series, YAXIS.V.idx, visible);
-    }
-
-
-    /** Handle VWQKms. */
-    protected void doVPointOut (
-        Object data,
-        ArtifactAndFacet aandf,
-        Document         theme,
-        boolean          visible
-    ) {
-        logger.debug("FlowVelocityGenerator.doVPointOut");
-
-        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
-
-        FlowVelocityMeasurementValue.FastFlowVelocityMeasurementValue
-            value = (FlowVelocityMeasurementValue.FastFlowVelocityMeasurementValue)
-                data;
-
-        StyledSeriesBuilder.addPoints(series, new double[][] {{value.getStation()},{value.getV()}}, true);
-
-        addAxisSeries(series, YAXIS.V.idx, visible);
-    }
-
-
-    /**
-     * Add items to dataseries which describes the differences.
-     */
-    protected void doTotalChannelOut(
-        FlowVelocityData data,
-        ArtifactAndFacet aandf,
-        Document         theme,
-        boolean          visible
-    ) {
-        logger.debug("FlowVelocityGenerator.doTotalChannelOut");
-
-        if (data == null) {
-            logger.warn("No data to add to FlowVelocity chart.");
-            return;
-         }
-
-        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
-
-        StyledSeriesBuilder.addPoints(series, data.getTotalChannelPoints(), true);
-
-        addAxisSeries(series, YAXIS.V.idx, visible);
-    }
-
-
-
-    /**
-     * @param data A FlowVelocityData object
-     * @param aandf The facet. This facet does NOT support any data objects. Use
-     * FLYSArtifact.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(
-        FlowVelocityData data,
-        ArtifactAndFacet aandf,
-        Document         theme,
-        boolean          visible
-    ) {
-        logger.debug("FlowVelocityGenerator.doTauOut");
-
-        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
-
-        StyledSeriesBuilder.addPoints(series, data.getQPoints(), true);
-
-        addAxisSeries(series, YAXIS.Q.idx, visible);
-    }
-
-    /**
-     * @param data A FlowVelocityData object
-     * @param aandf The facet. This facet does NOT support any data objects. Use
-     * FLYSArtifact.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 doTauOut(
-        FlowVelocityData data,
-        ArtifactAndFacet aandf,
-        Document         theme,
-        boolean          visible
-    ) {
-        logger.debug("FlowVelocityGenerator.doTauOut");
-
-        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
-
-        StyledSeriesBuilder.addPoints(series, data.getTauPoints(), true);
-
-        addAxisSeries(series, YAXIS.T.idx, visible);
-    }
-
-
-    /** Look up the axis identifier for a given facet type. */
-    public int axisIdxForFacet(String facetName) {
-        if (FacetTypes.IS.V(facetName)) {
-            return YAXIS.V.idx;
-        }
-        else if (FacetTypes.IS.T(facetName)) {
-            return YAXIS.T.idx;
-        }
-        else {
-            logger.warn("Could not find axis for facet " + facetName);
-            return YAXIS.V.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/flys-artifacts/src/main/java/de/intevation/flys/exports/FlowVelocityInfoGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-package de.intevation.flys.exports;
-
-
-/**
- * A ChartInfoGenerator that generates meta information for specific
- * flow velocity curves.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class FlowVelocityInfoGenerator
-extends      ChartInfoGenerator
-{
-    public FlowVelocityInfoGenerator() {
-        super(new FlowVelocityGenerator());
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/HistoricalDischargeCurveExporter.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,250 +0,0 @@
-package de.intevation.flys.exports;
-
-import java.io.OutputStream;
-import java.text.NumberFormat;
-import java.text.DateFormat;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-import java.util.HashMap;
-import java.util.Locale;
-
-import org.w3c.dom.Document;
-
-import org.apache.log4j.Logger;
-
-import au.com.bytecode.opencsv.CSVWriter;
-
-import net.sf.jasperreports.engine.JasperExportManager;
-import net.sf.jasperreports.engine.JasperFillManager;
-import net.sf.jasperreports.engine.JasperPrint;
-import net.sf.jasperreports.engine.JRException;
-
-import de.intevation.artifacts.common.utils.Config;
-
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.HistoricalDischargeData;
-import de.intevation.flys.artifacts.model.Timerange;
-import de.intevation.flys.artifacts.model.WQTimerange;
-import de.intevation.flys.artifacts.model.WQTJRDataSource;
-import de.intevation.flys.artifacts.resources.Resources;
-import de.intevation.flys.utils.FLYSUtils;
-import de.intevation.flys.utils.Formatter;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class HistoricalDischargeCurveExporter extends AbstractExporter {
-
-    private static final Logger logger =
-        Logger.getLogger(HistoricalDischargeCurveExporter.class);
-
-
-    public static final String CSV_TIMERANGE_HEADER =
-        "export.historical.discharge.csv.header.timerange";
-
-    public static final String CSV_WATERLEVEL_HEADER =
-        "export.historical.discharge.csv.header.waterlevel";
-
-    public static final String CSV_DISCHARGE_HEADER =
-        "export.historical.discharge.csv.header.discharge";
-
-    public static final String CSV_DIFF_HEADER =
-        "export.historical.discharge.csv.header.diff";
-
-    public static final String CSV_GAUGENAME_HEADER =
-        "export.historical.discharge.csv.header.gaugename";
-
-    public static final String PDF_HEADER_MODE =
-        "export.historical.discharge.pdf.mode";
-
-    public static final String JASPER_FILE =
-        "export.historical.discharge.pdf.file";
-
-    protected List<WQTimerange[]> data;
-
-
-    public void init(Document request, OutputStream out, CallContext cc) {
-        super.init(request, out, cc);
-
-        data = new ArrayList<WQTimerange[]>();
-    }
-
-
-    @Override
-    protected void addData(Object d) {
-        logger.debug("Add data of class: " + d.getClass());
-
-        if (d instanceof CalculationResult) {
-            d = ((CalculationResult) d).getData();
-
-            logger.debug("Internal data of CalculationResult: " + d.getClass());
-
-            if (d instanceof HistoricalDischargeData) {
-                d = (WQTimerange[]) ((HistoricalDischargeData) d).getWQTimeranges();
-
-                if (d instanceof WQTimerange[]) {
-                    logger.debug("Add new data of type WQTimerange");
-                    data.add((WQTimerange[]) d);
-                }
-            }
-        }
-    }
-
-
-    @Override
-    protected void writeCSVData(CSVWriter writer) {
-        logger.info("HistoricalDischargeCurveExporter.writeCSVData");
-        logger.debug("CSV gets " + data.size() + " WQTimerange[] objects.");
-
-        writeCSVHeader(writer);
-
-        for (WQTimerange[] arr: data) {
-            for (WQTimerange wqt: arr) {
-                wqt2CSV(writer, wqt);
-            }
-        }
-    }
-
-
-    @Override
-    protected void writePDF(OutputStream out) {
-        WQTJRDataSource source = createJRData();
-
-        String jasperFile = Resources.getMsg(
-                                context.getMeta(),
-                                JASPER_FILE,
-                                "/jasper/historical-discharge_en.jasper");
-        String confPath = Config.getConfigDirectory().toString();
-
-        Map parameters = new HashMap();
-        parameters.put("ReportTitle", "Exported Data");
-        try {
-            JasperPrint print = JasperFillManager.fillReport(
-                confPath + jasperFile,
-                parameters,
-                source);
-            JasperExportManager.exportReportToPdfStream(print, out);
-        }
-        catch(JRException je) {
-            logger.warn("Error generating PDF Report!");
-            je.printStackTrace();
-        }
-    }
-
-
-    protected void writeCSVHeader(CSVWriter writer) {
-        writer.writeNext(new String[] {
-            msg(CSV_TIMERANGE_HEADER, CSV_TIMERANGE_HEADER),
-            msg(CSV_WATERLEVEL_HEADER, CSV_WATERLEVEL_HEADER),
-            msg(CSV_DISCHARGE_HEADER, CSV_DISCHARGE_HEADER),
-            msg(CSV_DIFF_HEADER, CSV_DIFF_HEADER),
-            msg(CSV_GAUGENAME_HEADER, CSV_GAUGENAME_HEADER)
-        });
-    }
-
-
-    protected void wqt2CSV(CSVWriter writer, WQTimerange wqt) {
-        logger.debug("Add next WQTimerange to CSV");
-
-        DateFormat   df = Formatter.getMediumDateFormat(context);
-        NumberFormat wf = Formatter.getHistoricalDischargeW(context);
-        NumberFormat qf = Formatter.getHistoricalDischargeQ(context);
-
-        double[] wq = new double[3];
-
-        String gaugeName = getReferenceGaugename();
-
-        List<WQTimerange.TimerangeItem> sorted = wqt.sort();
-
-        for (int i = 0, n = sorted.size(); i < n; i++) {
-            WQTimerange.TimerangeItem item = sorted.get(i);
-
-            Timerange tr = item.timerange;
-            Date   start = new Date(tr.getStart());
-            Date     end = new Date(tr.getEnd());
-
-            item.get(wq);
-
-            writer.writeNext(new String[] {
-                df.format(start) + " - " + df.format(end),
-                wf.format(wq[0]),
-                qf.format(wq[1]),
-                qf.format(wq[2]),
-                gaugeName
-            });
-        }
-    }
-
-
-    protected WQTJRDataSource createJRData() {
-        WQTJRDataSource source = new WQTJRDataSource();
-
-        addMetaData(source);
-        for (WQTimerange[] arr: data) {
-            for (WQTimerange wqt: arr) {
-                addWQTData(source, wqt);
-            }
-        }
-
-        return source;
-    }
-
-
-    protected void addMetaData(WQTJRDataSource source) {
-        CallMeta meta = context.getMeta();
-
-        FLYSArtifact flys = (FLYSArtifact) master;
-
-        source.addMetaData ("river", FLYSUtils.getRivername(flys));
-
-        Locale locale = Resources.getLocale(meta);
-        DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, locale);
-
-        source.addMetaData("date", df.format(new Date()));
-
-        source.addMetaData("calculation", Resources.getMsg(
-                                            locale,
-                                            PDF_HEADER_MODE,
-                                            "Historical Discharge"));
-    }
-
-
-    protected void addWQTData(WQTJRDataSource source, WQTimerange wqt) {
-        DateFormat   df = Formatter.getShortDateFormat(context);
-        NumberFormat wf = Formatter.getHistoricalDischargeW(context);
-        NumberFormat qf = Formatter.getHistoricalDischargeQ(context);
-
-        double[] wq = new double[3];
-
-        String gaugeName = getReferenceGaugename();
-
-        for (int i = 0, n = wqt.size(); i < n; i++) {
-            Timerange tr = wqt.getTimerange(i);
-            Date   start = new Date(tr.getStart());
-            Date     end = new Date(tr.getEnd());
-
-            wqt.get(i, wq);
-
-            source.addData(new String[] {
-                df.format(start) + " - " + df.format(end),
-                wf.format(wq[0]),
-                qf.format(wq[1]),
-                qf.format(wq[2]),
-                gaugeName
-            });
-        }
-    }
-
-
-    public String getReferenceGaugename() {
-        return FLYSUtils.getReferenceGaugeName((FLYSArtifact) master);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/HistoricalDischargeCurveGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,258 +0,0 @@
-package de.intevation.flys.exports;
-
-import java.util.Date;
-
-import org.apache.log4j.Logger;
-import org.jfree.chart.plot.XYPlot;
-import org.jfree.data.general.SeriesException;
-import org.jfree.data.time.Day;
-import org.jfree.data.time.RegularTimePeriod;
-import org.jfree.data.time.TimeSeries;
-import org.jfree.data.time.TimeSeriesCollection;
-import org.w3c.dom.Document;
-
-import de.intevation.artifactdatabase.state.ArtifactAndFacet;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.access.HistoricalDischargeAccess;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.HistoricalWQTimerange;
-import de.intevation.flys.artifacts.model.Timerange;
-import de.intevation.flys.artifacts.model.WQTimerange;
-import de.intevation.flys.jfree.StyledTimeSeries;
-import de.intevation.flys.utils.FLYSUtils;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class HistoricalDischargeCurveGenerator extends TimeseriesChartGenerator
-    implements FacetTypes {
-
-    private static Logger logger = Logger
-        .getLogger(HistoricalDischargeCurveGenerator.class);
-
-    public static final String I18N_CHART_TITLE = "chart.historical.discharge.title";
-
-    public static final String I18N_CHART_SUBTITLE = "chart.historical.discharge.subtitle";
-
-    public static final String I18N_XAXIS_LABEL = "chart.historical.discharge.xaxis.label";
-
-    public static final String I18N_YAXIS_LABEL = "chart.historical.discharge.yaxis.label";
-
-    public static final String I18N_YAXIS_SECOND_LABEL = "chart.historical.discharge.yaxis.second.label";
-
-    public static enum YAXIS {
-        W(0), Q(1);
-
-        protected int idx;
-
-        private YAXIS(int c) {
-            idx = c;
-        }
-    }
-
-    @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
-    protected String getDefaultChartTitle() {
-        return msg(I18N_CHART_TITLE, I18N_CHART_TITLE);
-    }
-
-    @Override
-    protected String getDefaultChartSubtitle() {
-        FLYSArtifact flys = (FLYSArtifact) master;
-        Timerange evalTime = new HistoricalDischargeAccess(flys)
-            .getEvaluationTimerange();
-
-        Object[] args = new Object[] { FLYSUtils.getReferenceGaugeName(flys),
-            evalTime.getStart(), evalTime.getEnd() };
-
-        return msg(I18N_CHART_SUBTITLE, "", args);
-    }
-
-    @Override
-    protected String getDefaultXAxisLabel() {
-        return msg(I18N_XAXIS_LABEL, I18N_XAXIS_LABEL);
-    }
-
-    @Override
-    protected String getDefaultYAxisLabel(int pos) {
-        if (pos == 0) {
-            return msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL);
-        }
-        else if (pos == 1) {
-            return msg(I18N_YAXIS_SECOND_LABEL, I18N_YAXIS_SECOND_LABEL);
-        }
-        else {
-            return "NO TITLE FOR Y AXIS: " + pos;
-        }
-    }
-
-    @Override
-    protected void adjustPlot(XYPlot plot) {
-        super.adjustPlot(plot);
-        plot.setRangeZeroBaselineVisible(true);
-    }
-
-    @Override
-    public void doOut(ArtifactAndFacet artifactFacet, Document theme,
-        boolean visible) {
-        String name = artifactFacet.getFacetName();
-        logger.debug("HistoricalDischargeCurveGenerator.doOut: " + name);
-        logger.debug("Theme description is: "
-            + artifactFacet.getFacetDescription());
-
-        if (name.equals(HISTORICAL_DISCHARGE_Q)) {
-            doHistoricalDischargeOutQ(
-                (FLYSArtifact) artifactFacet.getArtifact(),
-                artifactFacet.getData(context),
-                artifactFacet.getFacetDescription(), theme, visible);
-        }
-        else if (name.equals(HISTORICAL_DISCHARGE_W)) {
-            doHistoricalDischargeOutW(
-                (FLYSArtifact) artifactFacet.getArtifact(),
-                artifactFacet.getData(context),
-                artifactFacet.getFacetDescription(), theme, visible);
-        }
-        else if (name.equals(HISTORICAL_DISCHARGE_Q_DIFF)) {
-            doHistoricalDischargeDifferenceOutQ(
-                (FLYSArtifact) artifactFacet.getArtifact(),
-                artifactFacet.getData(context),
-                artifactFacet.getFacetDescription(), theme, visible);
-        }
-        else if (name.equals(HISTORICAL_DISCHARGE_W_DIFF)) {
-            doHistoricalDischargeDifferenceOutW(
-                (FLYSArtifact) artifactFacet.getArtifact(),
-                artifactFacet.getData(context),
-                artifactFacet.getFacetDescription(), theme, visible);
-        }
-        else if (FacetTypes.IS.MANUALPOINTS(name)) {
-            doPoints(artifactFacet.getData(context), artifactFacet, theme,
-                visible, YAXIS.Q.idx);
-        }
-        else {
-            logger.warn("doOut(): unknown facet name: " + name);
-            return;
-        }
-    }
-
-    protected void doHistoricalDischargeOutQ(FLYSArtifact artifact,
-        Object data, String desc, Document theme, boolean visible) {
-        logger.debug("doHistoricalDischargeOut(): description = " + desc);
-
-        WQTimerange wqt = (WQTimerange) data;
-
-        TimeSeriesCollection tsc = newTimeSeriesCollection(wqt.getTimeranges(),
-            wqt.getQs(), theme, desc);
-
-        addAxisDataset(tsc, YAXIS.Q.idx, visible);
-    }
-
-    protected void doHistoricalDischargeOutW(FLYSArtifact artifact,
-        Object data, String desc, Document theme, boolean visible) {
-        logger.debug("doHistoricalDischargeOut(): description = " + desc);
-
-        WQTimerange wqt = (WQTimerange) data;
-
-        TimeSeriesCollection tsc = newTimeSeriesCollection(wqt.getTimeranges(),
-            wqt.getWs(), theme, desc);
-
-        addAxisDataset(tsc, YAXIS.W.idx, visible);
-    }
-
-    protected void doHistoricalDischargeDifferenceOutQ(FLYSArtifact artifact,
-        Object data, String desc, Document theme, boolean visible) {
-        logger.debug("doHistoricalDischargeDifferenceOut: desc = " + desc);
-
-        HistoricalWQTimerange wqt = (HistoricalWQTimerange) data;
-
-        TimeSeriesCollection tsc = newTimeSeriesCollection(wqt.getTimeranges(),
-            wqt.getDiffs(), theme, desc);
-
-        addAxisDataset(tsc, YAXIS.Q.idx, visible);
-    }
-
-    protected void doHistoricalDischargeDifferenceOutW(FLYSArtifact artifact,
-        Object data, String desc, Document theme, boolean visible) {
-        logger.debug("doHistoricalDischargeDifferenceOut: desc = " + desc);
-
-        HistoricalWQTimerange wqt = (HistoricalWQTimerange) data;
-
-        TimeSeriesCollection tsc = newTimeSeriesCollection(wqt.getTimeranges(),
-            wqt.getDiffs(), theme, desc);
-
-        addAxisDataset(tsc, YAXIS.W.idx, visible);
-    }
-
-    /**
-     * Creates a new TimeSeriesCollection with a single TimeSeries. The
-     * TimeSeries will consist of two RegularTimePeriods for each W/Q value
-     * provided by <i>wqt</i>. This has the effect, that the line in the chart
-     * looks like a "step chart".
-     */
-    protected TimeSeriesCollection newTimeSeriesCollection(
-        Timerange[] timeranges, double[] values, Document theme, String desc) {
-        logger.debug("Create new TimeSeriesCollection for: " + desc);
-
-        TimeSeriesCollection tsc = new TimeSeriesCollection();
-        TimeSeries series = new StyledTimeSeries(desc, theme);
-
-        for (int i = 0, n = timeranges.length; i < n; i++) {
-            RegularTimePeriod[] rtp = newRegularTimePeriod(timeranges[i]);
-
-            try {
-                if (Double.isNaN(values[i])) {
-                    logger.warn("Skip TimePeriod because value is NaN.");
-                    continue;
-                }
-
-                series.add(rtp[0], values[i]);
-                series.add(rtp[1], values[i]);
-
-                if (logger.isDebugEnabled()) {
-                    logger.debug("added Item to TimeSeries:");
-                    logger.debug("   TimePeriod: " + rtp[0] + " - " + rtp[1]);
-                    logger.debug("   Value:      " + values[i]);
-                }
-            }
-            catch (SeriesException se) {
-                logger.warn("Error while adding TimePeriod: " + se);
-            }
-        }
-
-        tsc.addSeries(series);
-
-        return tsc;
-    }
-
-    /**
-     * Creates an array that consists of two <i>Minute</i> periods [start, end].
-     *
-     * @param timerange
-     *            Supports start and end time.
-     *
-     * @return an array with two <i>Minute</i> periods [start, end].
-     */
-    protected RegularTimePeriod[] newRegularTimePeriod(Timerange timerange) {
-        Date start = new Date(timerange.getStart());
-        Date end = new Date(timerange.getEnd() - 1000 * 60 * 60 * 24);
-
-        return new RegularTimePeriod[] { new Day(start), new Day(end) };
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/HistoricalDischargeCurveInfoGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-package de.intevation.flys.exports;
-
-
-/**
- * A ChartInfoGenerator that generates meta information for specific
- * historical discharge curves.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class HistoricalDischargeCurveInfoGenerator
-extends      ChartInfoGenerator
-{
-    public HistoricalDischargeCurveInfoGenerator() {
-        super(new HistoricalDischargeCurveGenerator());
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/HistoricalDischargeWQCurveGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,163 +0,0 @@
-package de.intevation.flys.exports;
-
-import org.apache.log4j.Logger;
-import org.jfree.data.xy.XYSeries;
-import org.w3c.dom.Document;
-
-import de.intevation.artifactdatabase.state.ArtifactAndFacet;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.access.HistoricalDischargeAccess;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.Timerange;
-import de.intevation.flys.artifacts.model.WQKms;
-
-import de.intevation.flys.jfree.FLYSAnnotation;
-import de.intevation.flys.jfree.StyledValueMarker;
-import de.intevation.flys.jfree.StyledXYSeries;
-import de.intevation.flys.utils.FLYSUtils;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class HistoricalDischargeWQCurveGenerator
-extends      XYChartGenerator
-implements   FacetTypes {
-
-    /** Private logger. */
-    private static Logger logger = Logger
-        .getLogger(HistoricalDischargeWQCurveGenerator.class);
-
-    public static final String I18N_CHART_TITLE = "chart.historical.discharge.wq.title";
-
-    public static final String I18N_CHART_SUBTITLE = "chart.historical.discharge.wq.subtitle";
-
-    public static final String I18N_XAXIS_LABEL = "chart.historical.discharge.wq.xaxis.label";
-
-    public static final String I18N_YAXIS_LABEL = "chart.historical.discharge.wq.yaxis.label";
-
-    /** One Y-Axis only, in this chart. */
-    public static enum YAXIS {
-        W(0);
-
-        protected int idx;
-
-        private YAXIS(int c) {
-            idx = c;
-        }
-    }
-
-    @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
-    protected String getDefaultChartTitle() {
-        return msg(I18N_CHART_TITLE, I18N_CHART_TITLE);
-    }
-
-    @Override
-    protected String getDefaultChartSubtitle() {
-        FLYSArtifact flys = (FLYSArtifact) master;
-        Timerange evalTime = new HistoricalDischargeAccess(flys)
-            .getEvaluationTimerange();
-
-        Object[] args = new Object[] { FLYSUtils.getReferenceGaugeName(flys),
-            evalTime.getStart(), evalTime.getEnd() };
-
-        return msg(I18N_CHART_SUBTITLE, "", args);
-    }
-
-    @Override
-    protected String getDefaultXAxisLabel() {
-        return msg(I18N_XAXIS_LABEL, I18N_XAXIS_LABEL);
-    }
-
-    @Override
-    protected String getDefaultYAxisLabel(int pos) {
-        if (pos == 0) {
-            return msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL);
-        }
-        else {
-            return "NO TITLE FOR Y AXIS: " + pos;
-        }
-    }
-
-    @Override
-    public void doOut(ArtifactAndFacet artifactFacet, Document theme,
-        boolean visible) {
-        String name = artifactFacet.getFacetName();
-        logger.debug("HistoricalDischargeWQCurveGenerator.doOut: " + name);
-        logger.debug("Theme description is: "
-            + artifactFacet.getFacetDescription());
-
-        if (name.equals(HISTORICAL_DISCHARGE_WQ_Q)) {
-            doHistoricalDischargeOutQ(
-                (FLYSArtifact) artifactFacet.getArtifact(),
-                artifactFacet.getData(context),
-                artifactFacet.getFacetDescription(), theme, visible);
-        }
-        else if (name.equals(HISTORICAL_DISCHARGE_WQ_W)) {
-            doHistoricalDischargeOutW(
-                (FLYSArtifact) artifactFacet.getArtifact(),
-                artifactFacet.getData(context),
-                artifactFacet.getFacetDescription(), theme, visible);
-        }
-        else if (name.equals(HISTORICAL_DISCHARGE_WQ_CURVE)) {
-            doHistoricalDischargeCurveOut(
-                (FLYSArtifact) artifactFacet.getArtifact(),
-                artifactFacet.getData(context),
-                artifactFacet.getFacetDescription(), theme, visible);
-        }
-        else if (FacetTypes.IS.MANUALPOINTS(name)) {
-            doPoints(artifactFacet.getData(context), artifactFacet, theme,
-                visible, YAXIS.W.idx);
-        }
-        else if (HISTORICAL_DISCHARGE_MAINVALUES_Q.equals(name)) {
-            doAnnotations((FLYSAnnotation)
-                artifactFacet.getData(context), artifactFacet, theme, visible);
-        }
-        else if (HISTORICAL_DISCHARGE_MAINVALUES_W.equals(name)) {
-            doAnnotations((FLYSAnnotation)
-                artifactFacet.getData(context), artifactFacet, theme, visible);
-        }
-        else {
-            logger.warn("doOut(): unknown facet name: " + name);
-            return;
-        }
-    }
-
-    protected void doHistoricalDischargeOutQ(FLYSArtifact artifact,
-        Object data, String desc, Document theme, boolean visible) {
-        double value = Double.valueOf(data.toString());
-        addDomainMarker(new StyledValueMarker(value, theme), visible);
-    }
-
-    protected void doHistoricalDischargeOutW(FLYSArtifact artifact,
-        Object data, String desc, Document theme, boolean visible) {
-        double value = Double.valueOf(data.toString());
-        addValueMarker(new StyledValueMarker(value, theme), visible);
-    }
-
-    protected void doHistoricalDischargeCurveOut(FLYSArtifact artifact,
-        Object data, String desc, Document theme, boolean visible) {
-        XYSeries series = new StyledXYSeries(desc, theme);
-        StyledSeriesBuilder.addPointsQW(series, (WQKms) data);
-
-        addAxisSeries(series, YAXIS.W.idx, visible);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/HistoricalDischargeWQCurveInfoGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-package de.intevation.flys.exports;
-
-
-/**
- * A ChartInfoGenerator that generates meta information for specific
- * historical discharge curves.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class HistoricalDischargeWQCurveInfoGenerator
-extends      ChartInfoGenerator
-{
-    public HistoricalDischargeWQCurveInfoGenerator() {
-        super(new HistoricalDischargeWQCurveGenerator());
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/IdentifiableNumberAxis.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,22 +0,0 @@
-package de.intevation.flys.exports;
-
-import org.jfree.chart.axis.NumberAxis;
-
-
-public class IdentifiableNumberAxis extends NumberAxis {
-
-
-    protected String key;
-
-
-    protected IdentifiableNumberAxis(String key, String label) {
-        super(label);
-        this.key = key;
-    }
-
-
-    public String getId() {
-        return key;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/InfoGeneratorHelper.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,405 +0,0 @@
-package de.intevation.flys.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 de.intevation.artifacts.common.ArtifactNamespaceContext;
-import de.intevation.artifacts.common.utils.XMLUtils;
-import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
-
-import de.intevation.flys.jfree.Bounds;
-
-
-/**
- * This class helps generating chart info documents.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class InfoGeneratorHelper {
-
-    /** Private logging instance. */
-    private static final Logger logger =
-        Logger.getLogger(InfoGeneratorHelper.class);
-
-    protected ChartGenerator generator;
-
-
-    public InfoGeneratorHelper(ChartGenerator generator) {
-        this.generator = generator;
-    }
-
-
-    /**
-     * Triggers the creation of the chart info document.
-     *
-     * @param chart The JFreeChart chart.
-     * @param info An info object that has been created while chart creation.
-     *
-     * @return the info document.
-     */
-    public Document createInfoDocument(
-        JFreeChart         chart,
-        ChartRenderingInfo info)
-    {
-        logger.debug("InfoGeneratorHelper.createInfoDocument");
-
-        Document doc = XMLUtils.newDocument();
-
-        ElementCreator cr = new ElementCreator(
-            doc,
-            ArtifactNamespaceContext.NAMESPACE_URI,
-            ArtifactNamespaceContext.NAMESPACE_PREFIX);
-
-        Element chartinfo = cr.create("chartinfo");
-
-        chartinfo.appendChild(createAxesElements(cr, chart));
-        chartinfo.appendChild(createTransformationElements(cr, chart, info));
-
-        doc.appendChild(chartinfo);
-
-        return doc;
-    }
-
-
-    /**
-     * This method create a axes element that contains all domain and range
-     * axes of the given chart.
-     *
-     * @param cr The ElementCreator.
-     * @param chart The chart that provides range information of its axes.
-     *
-     * @return an element with axes information.
-     */
-    protected Element createAxesElements(
-        ElementCreator     cr,
-        JFreeChart         chart)
-    {
-        logger.debug("InfoGeneratorHelper.createRangeElements");
-
-        Element axes = cr.create("axes");
-
-        XYPlot plot = (XYPlot) chart.getPlot();
-
-        int dAxisCount = plot.getDomainAxisCount();
-        for (int i = 0; i < dAxisCount; i++) {
-            ValueAxis axis = plot.getDomainAxis(i);
-            XYDataset data = plot.getDataset(i);
-
-            if (axis != null) {
-                Element e = createAxisElement(cr, axis, data, "domain", i);
-                axes.appendChild(e);
-            }
-        }
-
-        int rAxisCount = plot.getRangeAxisCount();
-        for (int i = 0; i < rAxisCount; i++) {
-            ValueAxis axis = plot.getRangeAxis(i);
-            XYDataset data = plot.getDataset(i);
-
-            if (axis == null || data == null) {
-                logger.warn("Axis or dataset is empty at pos: " + i);
-                continue;
-            }
-
-            Element e = createAxisElement(cr, axis, data, "range", i);
-            axes.appendChild(e);
-        }
-
-        return axes;
-    }
-
-
-    /**
-     * This method create a axis element for a given <i>axis</i> and
-     * <i>type</i>. Type can be one of 'domain' or 'range'.
-     *
-     * @param cr The ElementCreator
-     * @param axis The axis that provides range information.
-     * @param dataset The dataset for min/max determination.
-     * @param type The axis type ('domain' or 'range').
-     * @param pos The position in the chart.
-     *
-     * @return An element that contains range information of a given axis.
-     */
-    protected Element createAxisElement(
-        ElementCreator cr,
-        ValueAxis      axis,
-        XYDataset      dataset,
-        String         type,
-        int            pos)
-    {
-        logger.debug("createAxisElement " + pos);
-        logger.debug("Axis is from type: " + axis.getClass());
-
-        Element e = cr.create(type);
-        cr.addAttr(e, "pos",  String.valueOf(pos), true);
-
-        if (axis instanceof DateAxis) {
-            prepareDateAxisElement(
-                e, cr, (DateAxis) axis, dataset, type, pos);
-        }
-        else {
-            prepareNumberAxisElement(
-                e, cr, (NumberAxis) axis, dataset, type, pos);
-        }
-
-        return e;
-    }
-
-
-    protected Element prepareNumberAxisElement(
-        Element        e,
-        ElementCreator cr,
-        NumberAxis     axis,
-        XYDataset      dataset,
-        String         type,
-        int            pos
-    ) {
-        Range range = axis.getRange();
-
-        cr.addAttr(e, "from", String.valueOf(range.getLowerBound()), true);
-        cr.addAttr(e, "to",   String.valueOf(range.getUpperBound()), true);
-        cr.addAttr(e, "axistype", "number", true);
-
-        Range[] rs = generator.getRangesForAxis(pos);
-        Range   r  = null;
-
-        if (type.equals("range")) {
-            r = rs[1];
-        }
-        else {
-            r = rs[0];
-        }
-
-        cr.addAttr(e, "min", String.valueOf(r.getLowerBound()), true);
-        cr.addAttr(e, "max", String.valueOf(r.getUpperBound()), true);
-
-        return e;
-    }
-
-
-    protected Element prepareDateAxisElement(
-        Element        e,
-        ElementCreator cr,
-        DateAxis       axis,
-        XYDataset      dataset,
-        String         type,
-        int            pos
-    ) {
-        Date from = axis.getMinimumDate();
-        Date to   = axis.getMaximumDate();
-
-        Bounds bounds = null;
-        if (type.equals("range")) {
-            bounds = generator.getYBounds(pos);
-        }
-        else {
-            bounds = generator.getXBounds(pos);
-        }
-
-        cr.addAttr(e, "axistype", "date", true);
-        cr.addAttr(e, "from", String.valueOf(from.getTime()), true);
-        cr.addAttr(e, "to", String.valueOf(to.getTime()), true);
-
-        cr.addAttr(e, "min", bounds.getLower().toString(), true);
-        cr.addAttr(e, "max", bounds.getUpper().toString(), true);
-
-        return e;
-    }
-
-
-    /**
-     * This method appends the values of a transformation matrix to transform
-     * image pixel coordinates into chart coordinates.
-     *
-     * @param cr The ElementCreator.
-     * @param chart The chart object.
-     * @param info The ChartRenderingInfo that is filled while chart creation.
-     *
-     * @return an element that contains one or more transformation matrix.
-     */
-    protected Element createTransformationElements(
-        ElementCreator     cr,
-        JFreeChart         chart,
-        ChartRenderingInfo info)
-    {
-        logger.debug("InfoGeneratorHelper.createTransformationElements");
-
-        Element tf = cr.create("transformation-matrix");
-
-        Rectangle2D dataArea = info.getPlotInfo().getDataArea();
-
-        XYPlot    plot  = (XYPlot) chart.getPlot();
-        ValueAxis xAxis = plot.getDomainAxis();
-
-        if (xAxis == null) {
-            logger.error("There is no x axis in the chart!");
-            return null;
-        }
-
-        for (int i  = 0, num = plot.getRangeAxisCount(); i < num; i++) {
-            ValueAxis yAxis = plot.getRangeAxis(i);
-
-            if (yAxis == null) {
-                logger.warn("No y axis at pos " + i + " existing.");
-                continue;
-            }
-
-            Element matrix = createTransformationElement(
-                cr, xAxis, yAxis, dataArea, i);
-
-            tf.appendChild(matrix);
-        }
-
-        return tf;
-    }
-
-
-    /**
-     * Creates an element that contains values used to transform coordinates
-     * of a coordinate system A into a coordinate system B.
-     *
-     * @param cr The ElementCreator.
-     * @param xAxis The x axis of the target coordinate system.
-     * @param yAxis The y axis of the target coordinate system.
-     * @param dataArea The pixel coordinates of the chart image.
-     * @param pos The dataset position.
-     *
-     * @return an element that contains transformation matrix values.
-     */
-    protected Element createTransformationElement(
-        ElementCreator cr,
-        ValueAxis      xAxis,
-        ValueAxis      yAxis,
-        Rectangle2D    dataArea,
-        int            pos)
-    {
-        double[] tm = createTransformationMatrix(dataArea, xAxis, yAxis);
-
-        Element matrix = cr.create("matrix");
-
-        cr.addAttr(matrix, "pos", String.valueOf(pos), true);
-        cr.addAttr(matrix, "sx", String.valueOf(tm[0]), true);
-        cr.addAttr(matrix, "sy", String.valueOf(tm[1]), true);
-        cr.addAttr(matrix, "tx", String.valueOf(tm[2]), true);
-        cr.addAttr(matrix, "ty", String.valueOf(tm[3]), true);
-
-        if (xAxis instanceof DateAxis) {
-            cr.addAttr(matrix, "xtype", "date", true);
-        }
-        else {
-            cr.addAttr(matrix, "xtype", "number", true);
-        }
-
-        if (yAxis instanceof DateAxis) {
-            cr.addAttr(matrix, "ytype", "date", true);
-        }
-        else {
-            cr.addAttr(matrix, "ytype", "number", true);
-        }
-
-        return matrix;
-    }
-
-
-    /**
-     * This method determines a transformation matrix to transform pixel
-     * coordinates of the chart image into chart coordinates.
-     *
-     * @param dataArea The rectangle that contains the data points of the chart.
-     * @param xAxis The x axis.
-     * @param yAxis The y axis.
-     *
-     * @return a double array as follows: [sx, sy, tx, ty].
-     */
-    protected static double[] createTransformationMatrix(
-        Rectangle2D dataArea,
-        ValueAxis   xAxis,
-        ValueAxis   yAxis)
-    {
-        logger.debug("InfoGeneratorHelper.createTransformationMatrix");
-
-        double offsetX = dataArea.getX();
-        double width   = dataArea.getWidth();
-        double offsetY = dataArea.getY();
-        double height  = dataArea.getHeight();
-
-        Range xRange = getRangeFromAxis(xAxis);
-        Range yRange = getRangeFromAxis(yAxis);
-
-        double lowerX  = xRange.getLowerBound();
-        double upperX  = xRange.getUpperBound();
-        double lowerY  = yRange.getLowerBound();
-        double upperY  = yRange.getUpperBound();
-
-        if (xAxis.isInverted()) {
-            logger.info("X-Axis is inverted!");
-
-            double tmp = upperX;
-            upperX = lowerX;
-            lowerX = tmp;
-        }
-
-        double dMoveX = upperX - lowerX;
-        double fMoveX = width * lowerX;
-        double dMoveY = lowerY - upperY;
-        double fMoveY = height * upperY;
-
-        AffineTransform t1 = AffineTransform.getTranslateInstance(
-                offsetX - ( fMoveX / dMoveX ),
-                offsetY - ( fMoveY / dMoveY ) );
-
-        AffineTransform t2 = AffineTransform.getScaleInstance(
-                width / (upperX - lowerX),
-                height / (lowerY - upperY));
-
-        t1.concatenate(t2);
-
-        try {
-            t1.invert();
-
-            double[] c = new double[6];
-            t1.getMatrix(c);
-
-            return new double[] { c[0], c[3], c[4], c[5] };
-        }
-        catch (NoninvertibleTransformException e) {
-            // do nothing
-            logger.warn("Matrix is not invertible.");
-        }
-
-        return new double[] { 1d, 1d, 0d, 0d };
-    }
-
-
-    protected static Range getRangeFromAxis(ValueAxis axis) {
-        if  (axis instanceof DateAxis) {
-            DateAxis dAxis = (DateAxis) axis;
-            Date     min   = dAxis.getMinimumDate();
-            Date     max   = dAxis.getMaximumDate();
-
-            return new Range(min.getTime(), max.getTime());
-        }
-        else {
-            return axis.getRange();
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/IntegerAttribute.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,34 +0,0 @@
-package de.intevation.flys.exports;
-
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class IntegerAttribute extends VisibleAttribute {
-
-
-    public IntegerAttribute(String name, int value, boolean visible) {
-        super(name, value, visible);
-    }
-
-
-    /**
-     * Calls VisibleAttribute.toXML() and appends afterwards an attribute
-     * <i>type</i> with value <i>integer</i>.
-     *
-     * @param parent The parent Node.
-     *
-     * @return the new Node that represents this Attribute.
-     */
-    @Override
-    public Node toXML(Node parent) {
-        Element ele = (Element) super.toXML(parent);
-        ele.setAttribute("type", "integer");
-
-        return ele;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/LegendProcessor.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,143 +0,0 @@
-package de.intevation.flys.exports;
-
-import java.awt.geom.Line2D;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.List;
-
-import org.jfree.chart.LegendItem;
-import org.jfree.chart.LegendItemCollection;
-import org.jfree.chart.plot.XYPlot;
-
-
-/** Class to process Plots legends. */
-public abstract class LegendProcessor {
-
-    /** (Empty) shape for aggregated Legend Items. */
-    private static final Line2D.Double SPACE = new Line2D.Double(0,0,0,0);
-
-
-    /** Prevent instantiations. */
-    private LegendProcessor() {
-    }
-
-
-    /**
-     * 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.
-     */
-    protected static String legendItemHash(LegendItem li) {
-        // TODO Do proper implementation. Ensure that only mergable sets are created.
-        // getFillPaint()
-        // getFillPaintTransformer()
-        // getLabel()
-        // getLine()
-        // getLinePaint()
-        // getLineStroke()
-        // getOutminePaint()
-        // getOutlineStroke()
-        // Shape getShape()
-        // String getToolTipText()
-        // String getURLText()
-        // boolean isLineVisible()
-        // boolean isShapeFilled()
-        // boolean isShapeOutlineVisible()
-        // boolean isShapeVisible()
-        String hash = li.getLinePaint().toString();
-        // XXX: DEAD CODE // 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;
-    }
-
-
-    /**
-     * Create new legend entries, dependent on settings.
-     * @param plot The plot for which to modify the legend.
-     * @param threshold How many items are needed for aggregation to
-     *                  be triggered?
-     */
-    public static void aggregateLegendEntries(XYPlot plot, int threshold) {
-        LegendItemCollection old = plot.getLegendItems();
-        // Find "similar" entries if aggregation is enabled.
-
-        int maxListSize = 0;
-        int AGGR_THRESHOLD = threshold;
-
-        if (AGGR_THRESHOLD > old.getItemCount() || AGGR_THRESHOLD <= 0){
-            return;
-        }
-
-        HashMap<String, List<LegendItem>> entries = new LinkedHashMap<String, List<LegendItem>>();
-        for (Iterator<LegendItem> i = old.iterator(); i.hasNext();) {
-            LegendItem item = i.next();
-            String hash = legendItemHash(item);
-            List<LegendItem> itemList = entries.get(hash);
-            if (itemList == null) {
-                itemList = new ArrayList<LegendItem>();
-                entries.put(hash, itemList);
-            }
-            itemList.add(item);
-
-            if (itemList.size() > maxListSize) {
-                maxListSize = itemList.size();
-            }
-        }
-
-        if (maxListSize < AGGR_THRESHOLD) {
-            // No need to do anything.
-            return;
-        }
-
-        // Run over collected entries, merge their names and create new
-        // entry if needed.
-        LegendItemCollection newLegend = new LegendItemCollection();
-        for (List<LegendItem> itemList: entries.values()) {
-            if (itemList.size() >= AGGR_THRESHOLD) {
-                // Now do merging.
-                // XXX: DEAD CODE // LegendItem item = itemList.get(0);
-                // Unfortunately we cannot clone and just setDescription, as this
-                // method was added in JFreeChart 1.0.14 (we are at .13).
-
-                // Remove the shapes of all but the first items,
-                // to prevent "overfill" of legenditemblock.
-                for (int i = 0; i < itemList.size(); i++) {
-                    if (i != 0) {
-                        LegendItem litem = itemList.get(i);
-
-                        // Make shape and line really small.
-                        LegendItem merged = new LegendItem(
-                            "," + litem.getLabel(), litem.getDescription(), litem.getToolTipText(),
-                            litem.getURLText(), false, SPACE,
-                            false, litem.getFillPaint(), false,
-                            litem.getOutlinePaint(), litem.getOutlineStroke(), false,
-                            SPACE, litem.getLineStroke(), litem.getLinePaint());
-                        newLegend.add(merged);
-                    }
-                    else {
-                        newLegend.add(itemList.get(i));
-                    }
-                }
-            }
-            else {
-                // Do not merge entries.
-                for (LegendItem li: itemList) {
-                    newLegend.add(li);
-                }
-            }
-        }
-
-        plot.setFixedLegendItems (newLegend);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/LegendSection.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-package de.intevation.flys.exports;
-
-
-/**
- * Settings regarding legend of chart.
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class LegendSection extends TypeSection {
-
-    public static final String VISIBILITY_ATTR  = "visibility";
-    public static final String FONTSIZE_ATTR    = "font-size";
-    public static final String AGGREGATION_ATTR = "aggregation-threshold";
-
-
-    public LegendSection() {
-        super("legend");
-    }
-
-
-    /** Register font size attribute and value. */
-    public void setFontSize(int fontSize) {
-        if (fontSize <= 0) {
-            return;
-        }
-
-        setIntegerValue(FONTSIZE_ATTR, fontSize);
-    }
-
-
-    public Integer getFontSize() {
-        return getIntegerValue(FONTSIZE_ATTR);
-    }
-
-
-    public Integer getAggregationThreshold() {
-        return getIntegerValue(AGGREGATION_ATTR);
-    }
-
-
-    public void setAggregationThreshold(int aggregationThreshold) {
-        setIntegerValue(AGGREGATION_ATTR, Math.abs(aggregationThreshold));
-    }
-
-
-    public void setVisibility(boolean visibility) {
-        setBooleanValue(VISIBILITY_ATTR, visibility);
-    }
-
-
-    public Boolean getVisibility() {
-        return getBooleanValue(VISIBILITY_ATTR);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,604 +0,0 @@
-package de.intevation.flys.exports;
-
-import de.intevation.artifactdatabase.state.ArtifactAndFacet;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.geom.Lines;
-import de.intevation.flys.artifacts.model.AreaFacet;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.WKms;
-import de.intevation.flys.artifacts.model.WQKms;
-import de.intevation.flys.exports.process.WOutProcessor;
-import de.intevation.flys.jfree.FLYSAnnotation;
-import de.intevation.flys.jfree.StyledAreaSeriesCollection;
-import de.intevation.flys.jfree.StyledXYSeries;
-import de.intevation.flys.utils.DataUtil;
-import de.intevation.flys.utils.FLYSUtils;
-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;
-
-
-/**
- * An OutGenerator that generates longitudinal section curves.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class LongitudinalSectionGenerator
-extends      XYChartGenerator
-implements   FacetTypes
-{
-    public enum YAXIS {
-        W(0),
-        D(1),
-        Q(2);
-        protected int idx;
-        private YAXIS(int c) {
-            idx = c;
-        }
-    }
-
-    /** The logger that is used in this generator. */
-    private static Logger logger =
-        Logger.getLogger(LongitudinalSectionGenerator.class);
-
-    /** Key to look up internationalized String for annotations label. */
-    public static final String I18N_ANNOTATIONS_LABEL =
-        "chart.longitudinal.annotations.label";
-
-    /**
-     * Key to look up internationalized String for LongitudinalSection diagrams
-     * titles.
-     */
-    public static final String I18N_CHART_TITLE =
-        "chart.longitudinal.section.title";
-
-    /**
-     * Key to look up internationalized String for LongitudinalSection diagrams
-     * subtitles.
-     */
-    public static final String I18N_CHART_SUBTITLE =
-        "chart.longitudinal.section.subtitle";
-
-    /**
-     * Key to look up internationalized String for LongitudinalSection diagrams
-     * short subtitles.
-     */
-    public static final String I18N_CHART_SHORT_SUBTITLE =
-        "chart.longitudinal.section.shortsubtitle";
-
-    public static final String I18N_XAXIS_LABEL =
-        "chart.longitudinal.section.xaxis.label";
-
-    public static final String I18N_YAXIS_LABEL =
-        "chart.longitudinal.section.yaxis.label";
-
-    public static final String I18N_2YAXIS_LABEL =
-        "chart.longitudinal.section.yaxis.second.label";
-
-    public static final String I18N_CHART_TITLE_DEFAULT  = "W-L\u00e4ngsschnitt";
-    public static final String I18N_XAXIS_LABEL_DEFAULT  = "km";
-    public static final String I18N_YAXIS_LABEL_DEFAULT  = "W [NN + m]";
-    public static final String I18N_2YAXIS_LABEL_DEFAULT = "Q [m\u00b3/s]";
-
-    public final static String I18N_WDIFF_YAXIS_LABEL =
-        "chart.w_differences.yaxis.label";
-
-    public final static String I18N_WDIFF_YAXIS_LABEL_DEFAULT = "m";
-
-    public LongitudinalSectionGenerator() {
-        super();
-    }
-
-
-    @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();
-            }
-        };
-    }
-
-
-    /**
-     * Return left most data points x value (on first axis).
-     * Overridden because axis could be inverted.
-     */
-    @Override
-    protected double getLeftX() {
-        if (isInverted()) {
-            return (Double)getXBounds(0).getUpper();
-        }
-        return (Double)getXBounds(0).getLower();
-    }
-
-
-    /**
-     * Return right most data points x value (on first axis).
-     * Overridden because axis could be inverted.
-     */
-    @Override
-    protected double getRightX() {
-        if (isInverted()) {
-            return (Double)getXBounds(0).getLower();
-        }
-        return (Double)getXBounds(0).getUpper();
-    }
-
-
-    /**
-     * 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);
-    }
-
-
-    /**
-     * Returns the default subtitle for this chart.
-     *
-     * @return the default subtitle for this chart.
-     */
-    @Override
-    protected String getDefaultChartSubtitle() {
-        double[] dist = getRange();
-
-        Object[] args = null;
-        if (dist == null) {
-            args = new Object[] {getRiverName()};
-            return msg(getChartShortSubtitleKey(), "", args);
-        }
-        args = new Object[] {
-            getRiverName(),
-            dist[0],
-            dist[1]
-        };
-        return msg(getChartSubtitleKey(), "", args);
-    }
-
-
-    /**
-     * Gets key to look up internationalized String for the charts subtitle.
-     * @return key to look up translated subtitle.
-     */
-    protected String getChartSubtitleKey() {
-        return I18N_CHART_SUBTITLE;
-    }
-
-
-    /**
-     * Gets key to look up internationalized String for the charts short
-     * subtitle.
-     * @return key to look up translated subtitle.
-     */
-    protected String getChartShortSubtitleKey() {
-        return I18N_CHART_SHORT_SUBTITLE;
-    }
-
-
-    /**
-     * Get internationalized label for the x axis.
-     */
-    @Override
-    protected String getDefaultXAxisLabel() {
-        FLYSArtifact flys = (FLYSArtifact) master;
-
-        return msg(
-            I18N_XAXIS_LABEL,
-            I18N_XAXIS_LABEL_DEFAULT,
-            new Object[] { FLYSUtils.getRiver(flys).getName() });
-    }
-
-
-    @Override
-    protected String getDefaultYAxisLabel(int index) {
-        String label = "default";
-
-        if (index == YAXIS.W.idx) {
-            label = getWAxisLabel();
-        }
-        else if (index == YAXIS.Q.idx) {
-            label = msg(getQAxisLabelKey(), getQAxisDefaultLabel());
-        }
-        else if (index == YAXIS.D.idx) {
-            label = msg(I18N_WDIFF_YAXIS_LABEL, I18N_WDIFF_YAXIS_LABEL_DEFAULT);
-        }
-
-        return label;
-    }
-
-
-    /**
-     * Get internationalized label for the y axis.
-     */
-    protected String getWAxisLabel() {
-        FLYSArtifact flys = (FLYSArtifact) master;
-
-        String unit = FLYSUtils.getRiver(flys).getWstUnit().getName();
-
-        return msg(
-            I18N_YAXIS_LABEL,
-            I18N_YAXIS_LABEL_DEFAULT,
-            new Object[] { unit });
-    }
-
-
-    /**
-     * Create Axis for given index.
-     * @return axis with according internationalized label.
-     */
-    @Override
-    protected NumberAxis createYAxis(int index) {
-        NumberAxis axis = super.createYAxis(index);
-
-        // "Q" Axis shall include 0.
-        if (index == YAXIS.Q.idx) {
-            axis.setAutoRangeIncludesZero(true);
-        }
-        else {
-            axis.setAutoRangeIncludesZero(false);
-        }
-
-        return axis;
-    }
-
-
-    /**
-     * Get default value for the second Y-Axis' label (if no translation was
-     * found).
-     */
-    protected String getQAxisDefaultLabel() {
-        return I18N_2YAXIS_LABEL_DEFAULT;
-    }
-
-
-    /**
-     * Get key for internationalization of the second Y-Axis' label.
-     */
-    protected String getQAxisLabelKey() {
-        return I18N_2YAXIS_LABEL;
-    }
-
-
-    /**
-     * Trigger inversion.
-     */
-    @Override
-    protected void adjustAxes(XYPlot plot) {
-        super.adjustAxes(plot);
-        invertXAxis(plot.getDomainAxis());
-    }
-
-
-    /**
-     * This method inverts the x-axis based on the kilometer information of the
-     * selected river. If the head of the river is at kilometer 0, the axis is
-     * not inverted, otherwise it is.
-     *
-     * @param xaxis The domain axis.
-     */
-    protected void invertXAxis(ValueAxis xaxis) {
-        if (isInverted()) {
-            logger.debug("X-Axis.setInverted(true)");
-            xaxis.setInverted(true);
-        }
-    }
-
-
-    /**
-     * Produce output.
-     * @param artifactAndFacet current facet and artifact.
-     * @param attr  theme for facet
-     */
-    @Override
-    public void doOut(
-        ArtifactAndFacet artifactAndFacet,
-        Document         attr,
-        boolean          visible
-    ) {
-        String name = artifactAndFacet.getFacetName();
-
-        logger.debug("LongitudinalSectionGenerator.doOut: " + name);
-
-        if (name == null) {
-            logger.error("No facet name for doOut(). No output generated!");
-            return;
-        }
-
-        Facet facet = artifactAndFacet.getFacet();
-
-        if (facet == null) {
-            return;
-        }
-
-        WOutProcessor processor = new WOutProcessor();
-        if (processor.canHandle(name)) {
-            processor.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(
-                (FLYSAnnotation) artifactAndFacet.getData(context),
-                 artifactAndFacet,
-                 attr,
-                 visible);
-        }
-        else if (name.equals(STATIC_WQKMS_Q)) {
-            doQOut(
-                (WQKms) artifactAndFacet.getData(context),
-                artifactAndFacet,
-                attr,
-                visible);
-        }
-        else if (name.equals(W_DIFFERENCES)) {
-            doWDifferencesOut(
-                (WKms) 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.W.idx);
-        }
-        else {
-            logger.warn("Unknown facet name: " + name);
-            return;
-        }
-    }
-
-    /**
-     * Add items to dataseries which describes the differences.
-     */
-    protected void doWDifferencesOut(
-        WKms       wkms,
-        ArtifactAndFacet aandf,
-        Document   theme,
-        boolean    visible
-    ) {
-        logger.debug("WDifferencesCurveGenerator.doWDifferencesOut");
-        if (wkms == null) {
-            logger.warn("No data to add to WDifferencesChart.");
-            return;
-         }
-
-        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
-
-        if (logger.isDebugEnabled()) {
-            if (wkms.size() > 0) {
-                logger.debug("Generate series: " + series.getKey());
-                logger.debug("Start km: " + wkms.getKm(0));
-                logger.debug("End   km: " + wkms.getKm(wkms.size() - 1));
-                logger.debug("Values  : " + wkms.size());
-            }
-        }
-
-        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
-     * FLYSArtifact.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;
-    }
-
-
-    /**
-     * Get name of series (displayed in legend).
-     * @return name of the series.
-     */
-    protected String getSeriesName(WQKms 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;
-    }
-
-
-    /** Look up the axis identifier for a given facet type. */
-    public int axisIdxForFacet(String facetName) {
-        if (FacetTypes.IS.W(facetName)) {
-            return YAXIS.W.idx;
-        }
-        else if (FacetTypes.IS.Q(facetName)) {
-            return YAXIS.Q.idx;
-        }
-        else {
-            logger.warn("Could not find axis for facet " + facetName);
-            return YAXIS.W.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("LongitudinalSectionGenerator.doArea");
-        StyledAreaSeriesCollection area = new StyledAreaSeriesCollection(theme);
-
-        String seriesName = aandf.getFacetDescription();
-
-        AreaFacet.Data data = (AreaFacet.Data) o;
-
-        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);
-        }
-        // Add area to the respective axis.
-        addAreaSeries(area, axisIdxForFacet(data.getRootFacetName()), visible);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/LongitudinalSectionInfoGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-package de.intevation.flys.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/flys-artifacts/src/main/java/de/intevation/flys/exports/MapGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,344 +0,0 @@
-package de.intevation.flys.exports;
-
-import com.vividsolutions.jts.geom.Envelope;
-
-import de.intevation.artifactdatabase.data.StateData;
-import de.intevation.artifactdatabase.state.ArtifactAndFacet;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.Settings;
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.common.ArtifactNamespaceContext;
-import de.intevation.artifacts.common.utils.XMLUtils;
-import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.map.WMSDBLayerFacet;
-import de.intevation.flys.artifacts.model.map.WMSLayerFacet;
-import de.intevation.flys.artifacts.model.map.WSPLGENLayerFacet;
-import de.intevation.flys.artifacts.states.WaterlevelGroundDifferences;
-import de.intevation.flys.collections.FLYSArtifactCollection;
-import de.intevation.flys.utils.ArtifactMapfileGenerator;
-import de.intevation.flys.utils.GeometryUtils;
-import de.intevation.flys.utils.ThemeUtil;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-
-public class MapGenerator implements OutGenerator, FacetTypes {
-
-    private static Logger logger = Logger.getLogger(MapGenerator.class);
-
-    protected FLYSArtifactCollection collection;
-
-    protected Artifact master;
-
-    protected Settings settings;
-
-    protected Document request;
-
-    protected OutputStream out;
-
-    protected CallContext context;
-
-    protected List<WMSLayerFacet> layers;
-
-    protected Envelope maxExtent;
-    protected Envelope initialExtent;
-
-    protected String srid;
-
-
-
-    @Override
-    public void init(Document request, OutputStream out, CallContext context) {
-        logger.debug("MapGenerator.init");
-
-        this.request  = request;
-        this.out      = out;
-        this.context  = context;
-
-        this.layers = new ArrayList<WMSLayerFacet>();
-
-        this.maxExtent = null;
-        this.initialExtent = null;
-    }
-
-
-    @Override
-    public void setMasterArtifact(Artifact master) {
-        logger.debug("MapGenerator.setMasterArtifact");
-        this.master = master;
-    }
-
-    @Override
-    public void setCollection(FLYSArtifactCollection collection) {
-        this.collection = collection;
-    }
-
-    @Override
-    public void doOut(
-        ArtifactAndFacet artifactFacet,
-        Document         attr,
-        boolean          visible)
-    {
-        String name = artifactFacet.getFacetName();
-
-        logger.debug("MapGenerator.doOut: " +
-            artifactFacet.getArtifact().identifier() + " | " + name);
-        FLYSArtifact flys = (FLYSArtifact) artifactFacet.getArtifact();
-
-        Facet nativeFacet = artifactFacet.getFacet();
-
-        if (nativeFacet instanceof WMSLayerFacet) {
-            WMSLayerFacet wms = (WMSLayerFacet) nativeFacet;
-            Envelope   extent = wms.getOriginalExtent();
-
-            layers.add(wms);
-
-            setMaxExtent(extent);
-            setSrid(wms.getSrid());
-
-            if (FLOODMAP_WSPLGEN.equals(name)) {
-                setInitialExtent(extent);
-                createWSPLGENLayer(flys, wms, attr);
-            }
-            else if (FLOODMAP_BARRIERS.equals(name)) {
-                createBarriersLayer(flys, wms);
-            }
-            else if (FLOODMAP_USERSHAPE.equals(name)) {
-                createUserShapeLayer(flys, wms);
-            }
-            else {
-                logger.debug("doOut: createDatabaseLayer for facet name: " + name);
-                createDatabaseLayer(flys, wms, attr);
-            }
-        }
-        else {
-            logger.warn("Facet not supported: " + nativeFacet.getClass());
-        }
-    }
-
-
-    protected void createWSPLGENLayer(
-        FLYSArtifact  flys,
-        WMSLayerFacet wms,
-        Document      attr
-    ) {
-        try {
-            if(wms instanceof WSPLGENLayerFacet) {
-                // Retrieve waterlevel ground differences from artifact
-                StateData dFrom = flys.getData(WaterlevelGroundDifferences.LOWER_FIELD);
-                StateData dTo   = flys.getData(WaterlevelGroundDifferences.UPPER_FIELD);
-                StateData dStep = flys.getData(WaterlevelGroundDifferences.DIFF_FIELD);
-
-                String fromStr = dFrom != null ? (String) dFrom.getValue() : null;
-                String toStr   = dTo   != null ? (String) dTo.getValue()   : null;
-                String stepStr = dStep != null ? (String) dStep.getValue() : null;
-
-                float from = Float.parseFloat(fromStr);
-                float to   = Float.parseFloat(toStr);
-                float step = Float.parseFloat(stepStr);
-
-                ArtifactMapfileGenerator mfg = new ArtifactMapfileGenerator();
-                mfg.createUeskLayer(
-                    flys,
-                    (WSPLGENLayerFacet) wms,
-                    ThemeUtil.createDynamicMapserverStyle(attr, from, to, step),
-                    context);
-            }
-            else {
-                logger.warn("Cannot create WSPLGEN layer from: " +
-                            wms.getClass());
-            }
-        }
-        catch (IOException ioe) {
-            logger.error(ioe, ioe);
-        }
-    }
-
-
-    protected void createBarriersLayer(FLYSArtifact flys, WMSLayerFacet wms) {
-        ArtifactMapfileGenerator mfg = new ArtifactMapfileGenerator();
-
-        try {
-            mfg.createBarriersLayer(flys, wms);
-        }
-        catch (FileNotFoundException fnfe) {
-            logger.error(fnfe, fnfe);
-        }
-        catch (IOException ioe) {
-            logger.error(ioe, ioe);
-        }
-    }
-
-
-    protected void createUserShapeLayer(FLYSArtifact flys, WMSLayerFacet wms) {
-        ArtifactMapfileGenerator mfg = new ArtifactMapfileGenerator();
-
-        try {
-            mfg.createUserShapeLayer(flys, wms);
-        }
-        catch (FileNotFoundException fnfe) {
-            logger.error(fnfe, fnfe);
-        }
-        catch (IOException ioe) {
-            logger.error(ioe, ioe);
-        }
-    }
-
-
-    protected void createDatabaseLayer(
-        FLYSArtifact  flys,
-        WMSLayerFacet wms,
-        Document      attr
-    ) {
-        logger.debug("createDatabaseLayer for facet: " + wms.getName());
-
-        ArtifactMapfileGenerator mfg = new ArtifactMapfileGenerator();
-
-        try {
-            File baseDir = mfg.getShapefileBaseDir();
-            File artDir  = new File(baseDir, flys.identifier());
-
-            if (artDir != null && !artDir.exists()) {
-                logger.debug("Create new directory: " + artDir.getPath());
-                artDir.mkdir();
-            }
-
-            if (wms instanceof WMSDBLayerFacet) {
-                mfg.createDatabaseLayer(
-                    flys,
-                    (WMSDBLayerFacet) wms,
-                    ThemeUtil.createMapserverStyle(attr));
-            }
-            else {
-                logger.warn("Cannot create DB layer from: " + wms.getClass());
-            }
-        }
-        catch (FileNotFoundException fnfe) {
-            logger.error(fnfe, fnfe);
-        }
-        catch (IOException ioe) {
-            logger.error(ioe, ioe);
-        }
-    }
-
-
-    @Override
-    public void generate()
-    throws IOException
-    {
-        logger.debug("MapGenerator.generate");
-
-        ArtifactMapfileGenerator mfg = new ArtifactMapfileGenerator();
-        mfg.generate();
-
-        Document response = XMLUtils.newDocument();
-        ElementCreator c  = new ElementCreator(
-            response,
-            ArtifactNamespaceContext.NAMESPACE_URI,
-            ArtifactNamespaceContext.NAMESPACE_PREFIX);
-
-        Element root   = c.create("floodmap");
-        Element layers = c.create("layers");
-
-        response.appendChild(root);
-        root.appendChild(layers);
-
-        appendLayers(layers);
-        appendMapInformation(root, c);
-
-        XMLUtils.toStream(response, out);
-    }
-
-
-    protected void appendLayers(Element parent) {
-        for (WMSLayerFacet facet: layers) {
-            parent.appendChild(facet.toXML(parent.getOwnerDocument()));
-        }
-    }
-
-
-    protected void setMaxExtent(Envelope maxExtent) {
-        if (maxExtent == null) {
-            return;
-        }
-
-        if (this.maxExtent == null) {
-            logger.debug("Set max extent to: " + maxExtent);
-            this.maxExtent = new Envelope(maxExtent);
-            return;
-        }
-
-        this.maxExtent.expandToInclude(maxExtent);
-    }
-
-
-    protected void setInitialExtent(Envelope initialExtent) {
-        if (this.initialExtent == null && initialExtent != null) {
-            logger.debug("Set initial extent to: " + initialExtent);
-            this.initialExtent = new Envelope(initialExtent);
-        }
-    }
-
-
-    protected void setSrid(String srid) {
-        if (srid == null || srid.length() == 0) {
-            return;
-        }
-
-        this.srid = srid;
-    }
-
-
-    protected void appendMapInformation(Element parent, ElementCreator c) {
-        String mE = GeometryUtils.jtsBoundsToOLBounds(this.maxExtent);
-
-        Element maxExtent = c.create("maxExtent");
-        maxExtent.setTextContent(mE);
-
-        if(this.initialExtent != null) {
-            String iE = GeometryUtils.jtsBoundsToOLBounds(this.initialExtent);
-            Element initExtent = c.create("initialExtent");
-            initExtent.setTextContent(iE);
-            parent.appendChild(initExtent);
-        }
-
-        Element srid = c.create("srid");
-        srid.setTextContent(this.srid);
-
-        // TODO zoom levels
-        // TODO resolutation
-
-        parent.appendChild(maxExtent);
-        parent.appendChild(srid);
-    }
-
-
-    /**
-     * Returns an instance of <i>EmptySettings</i> currently!
-     *
-     * @return an instance of <i>EmptySettings</i>.
-     */
-    @Override
-    public Settings getSettings() {
-        return new EmptySettings();
-    }
-
-
-    @Override
-    public void setSettings(Settings settings) {
-        this.settings = settings;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/MiddleBedHeightExporter.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,160 +0,0 @@
-package de.intevation.flys.exports;
-
-import java.io.OutputStream;
-import java.text.NumberFormat;
-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 de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.MiddleBedHeightData;
-import de.intevation.flys.utils.FLYSUtils;
-import de.intevation.flys.utils.Formatter;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class MiddleBedHeightExporter extends AbstractExporter {
-
-    /** Private logger. */
-    private static final Logger logger =
-        Logger.getLogger(MiddleBedHeightExporter.class);
-
-    public static final String CSV_KM =
-        "export.bedheight_middle.csv.header.km";
-
-    public static final String CSV_SOUNDING =
-        "export.bedheight_middle.csv.header.sounding";
-
-    public static final String CSV_HEIGHT =
-        "export.bedheight_middle.csv.header.height";
-
-    public static final String CSV_UNCERTAINTY =
-        "export.bedheight_middle.csv.header.uncertainty";
-
-    public static final String CSV_DATA_GAP =
-        "export.bedheight_middle.csv.header.datagap";
-
-    public static final String CSV_SOUNDING_WIDTH =
-        "export.bedheight_middle.csv.header.soundingwidth";
-
-    public static final String CSV_WIDTH =
-        "export.bedheight_middle.csv.header.width";
-
-    public static final String CSV_LOCATIONS =
-        "export.bedheight_middle.csv.header.locations";
-
-
-    protected List<MiddleBedHeightData[]> data;
-
-
-    public void init(Document request, OutputStream out, CallContext cc) {
-        super.init(request, out, cc);
-        data = new ArrayList<MiddleBedHeightData[]>();
-    }
-
-
-    @Override
-    protected void addData(Object d) {
-        if (d instanceof CalculationResult) {
-            d = ((CalculationResult) d).getData();
-
-            if (d instanceof MiddleBedHeightData[]) {
-                logger.debug("Add new data of type MiddleBedHeightData");
-                data.add((MiddleBedHeightData[]) d);
-            }
-        }
-    }
-
-
-    @Override
-    protected void writeCSVData(CSVWriter writer) {
-        logger.info("MiddleBedHeightExporter.writeCSVData");
-        logger.debug("CSV gets " + data.size() + " MiddleBedHeightData objects.");
-
-        writeCSVHeader(writer);
-
-        for (MiddleBedHeightData[] d: data) {
-            data2CSV(writer, d);
-        }
-    }
-
-
-    protected void writeCSVHeader(CSVWriter writer) {
-        writer.writeNext(new String[] {
-            msg(CSV_KM, CSV_KM),
-            msg(CSV_SOUNDING, CSV_SOUNDING),
-            msg(CSV_HEIGHT, CSV_HEIGHT),
-            msg(CSV_UNCERTAINTY, CSV_UNCERTAINTY),
-            msg(CSV_DATA_GAP, CSV_DATA_GAP),
-            msg(CSV_SOUNDING_WIDTH, CSV_SOUNDING_WIDTH),
-            msg(CSV_WIDTH, CSV_WIDTH),
-            msg(CSV_LOCATIONS, CSV_LOCATIONS)
-        });
-    }
-
-
-    protected void data2CSV(CSVWriter writer, MiddleBedHeightData[] mData) {
-        logger.debug("Add next MiddleBedHeightData to CSV");
-
-        FLYSArtifact flys = (FLYSArtifact) master;
-
-        NumberFormat kmF     = Formatter.getMiddleBedHeightKM(context);
-        NumberFormat heightF = Formatter.getMiddleBedHeightHeight(context);
-        NumberFormat uncertF = Formatter.getMiddleBedHeightUncert(context);
-        NumberFormat gapF    = Formatter.getMiddleBedHeightDataGap(context);
-        NumberFormat soundF  = Formatter.getMiddleBedHeightSounding(context);
-        NumberFormat widthF  = Formatter.getMiddleBedHeightWidth(context);
-
-        heightF.setMaximumFractionDigits(1);
-        soundF.setMaximumFractionDigits(1);
-        for (MiddleBedHeightData data: mData) {
-            for (int i = 0, n = data.size(); i < n; i++) {
-                int start = data.getStartYear();
-                int end   = data.getEndYear();
-
-                if (start == end) {
-                    writer.writeNext(new String[] {
-                        kmF.format(data.getKM(i)),
-                        data.getDescription(),
-                        heightF.format(data.getMiddleHeight(i)),
-                        uncertF.format(data.getUncertainty(i)),
-                        gapF.format(data.getDataGap(i)) + "%",
-                        soundF.format(data.getSoundingWidth(i)),
-                        widthF.format(data.getWidth(i)),
-                        FLYSUtils.getLocationDescription(flys, data.getKM(i)),
-                    });
-                }
-                else {
-                    writer.writeNext(new String[] {
-                        kmF.format(data.getKM(i)),
-                        data.getDescription(),
-                        heightF.format(data.getMiddleHeight(i)),
-                        "",
-                        "",
-                        "",
-                        "",
-                        FLYSUtils.getLocationDescription(flys, data.getKM(i)),
-                    });
-                }
-
-            }
-        }
-    }
-
-
-    @Override
-    protected void writePDF(OutputStream out) {
-        logger.error("TODO: Implement FlowVelocityExporter.writePDF");
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/MiddleBedHeightGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,274 +0,0 @@
-package de.intevation.flys.exports;
-
-import de.intevation.artifactdatabase.state.ArtifactAndFacet;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.MiddleBedHeightData;
-import de.intevation.flys.exports.process.BedheightProcessor;
-import de.intevation.flys.exports.process.Processor;
-import de.intevation.flys.exports.process.WOutProcessor;
-import de.intevation.flys.jfree.FLYSAnnotation;
-import de.intevation.flys.jfree.StyledXYSeries;
-import de.intevation.flys.utils.FLYSUtils;
-
-import org.apache.log4j.Logger;
-import org.jfree.data.xy.XYSeries;
-import org.w3c.dom.Document;
-
-// TODO Move class to de.intevation.flys.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() {
-        FLYSArtifact flys = (FLYSArtifact) master;
-
-        return msg(
-            I18N_XAXIS_LABEL,
-            I18N_XAXIS_LABEL_DEFAULT,
-            new Object[] { FLYSUtils.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) {
-           FLYSArtifact flys = (FLYSArtifact) master;
-           String unit = FLYSUtils.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() {
-        return msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL_DEFAULT);
-    }
-
-
-    /**
-     * 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(
-                (FLYSAnnotation) 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 (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
-     * FLYSArtifact.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);
-
-        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/flys-artifacts/src/main/java/de/intevation/flys/exports/MiddleBedHeightInfoGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-package de.intevation.flys.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/flys-artifacts/src/main/java/de/intevation/flys/exports/NormalizedReferenceCurveGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-package de.intevation.flys.exports;
-
-/**
- * An OutGenerator that generates reference curves.
- */
-public class NormalizedReferenceCurveGenerator
-extends      ReferenceCurveGenerator
-{
-    public static final String I18N_NORMALIZED_CHART_TITLE =
-        "chart.normalized.reference.curve.title";
-
-    public static final String I18N_NORMALIZED_CHART_TITLE_DEFAULT  =
-        "Reduzierte Bezugslinie";
-
-    public NormalizedReferenceCurveGenerator() {
-    }
-
-    /** Get default chart title. */
-    @Override
-    protected String getDefaultChartTitle() {
-        return msg(
-            I18N_NORMALIZED_CHART_TITLE,
-            I18N_NORMALIZED_CHART_TITLE_DEFAULT);
-    }
-
-    @Override
-    protected String facetName() {
-        return REFERENCE_CURVE_NORMALIZED;
-    }
-
-    @Override
-    protected boolean doNormalize() {
-        return true;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/NormalizedReferenceCurveInfoGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@
-package de.intevation.flys.exports;
-
-
-/**
- * A ChartInfoGenerator that generates meta information for specific reference
- * curves.
- */
-public class NormalizedReferenceCurveInfoGenerator
-extends      ChartInfoGenerator
-{
-    public NormalizedReferenceCurveInfoGenerator() {
-        super(new NormalizedReferenceCurveGenerator());
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/OutGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,83 +0,0 @@
-package de.intevation.flys.exports;
-
-import java.io.IOException;
-import java.io.OutputStream;
-
-import org.w3c.dom.Document;
-
-import de.intevation.artifactdatabase.state.ArtifactAndFacet;
-import de.intevation.artifactdatabase.state.Settings;
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.flys.collections.FLYSArtifactCollection;
-
-
-/**
- * An OutGenerator is used to create a collected outputs of a list of Artifacts.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public interface OutGenerator {
-
-    /**
-     * Initializes the OutGenerator with meta information which are necessary
-     * for the output generation.
-     *
-     * @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);
-
-    /**
-     * This method is used to tell the OutGenerator which artifact is the master
-     * artifact which is used for special operations.
-     *
-     * @param master The master artifact.
-     */
-    void setMasterArtifact(Artifact master);
-
-    /**
-     * This method is used to set the Collection of the OutGenerator.
-     *
-     * @param collection A reference to the collection.
-     */
-    void setCollection(FLYSArtifactCollection collection);
-
-    /**
-     * Creates the output of an Artifact and appends that single output to the
-     * total output.
-     *
-     * @param bundle The Facet and artifact that provides information and data for the
-     * single output.
-     * @param attr A document that might contain some attributes used while
-     * producing the output.
-     * @param visible Specifies, if this output should be visible or not.
-     */
-    void doOut(ArtifactAndFacet bundle, Document attr, boolean visible);
-
-    /**
-     * Writes the collected output of all artifacts specified in the
-     * <i>request</i> (see init()) document to the OutputStream <i>out</i> (see
-     * init()).
-     */
-    void generate() throws IOException;
-
-    /**
-     * This method is used to set a <i>Settings</i> object for the <i>Output</i>
-     * that is produced by this <i>OutGenerator</i>.
-     *
-     * @param settings The <i>Settings</i> that might be used while
-     * <i>Output</i> creation.
-     */
-    void setSettings(Settings settings);
-
-    /**
-     * Returns the Settings for the Output produced by this OutGenerator.
-     *
-     * @return the Settings for the Output produced by this OutGenerator.
-     */
-    Settings getSettings();
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/OutputHelper.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,515 +0,0 @@
-package de.intevation.flys.exports;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.xml.xpath.XPathConstants;
-
-import org.apache.log4j.Logger;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-
-import de.intevation.artifactdatabase.Backend;
-import de.intevation.artifactdatabase.Backend.PersistentArtifact;
-import de.intevation.artifactdatabase.state.ArtifactAndFacet;
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.ArtifactDatabase;
-import de.intevation.artifacts.ArtifactDatabaseException;
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.common.ArtifactNamespaceContext;
-import de.intevation.artifacts.common.utils.ClientProtocolUtils;
-import de.intevation.artifacts.common.utils.XMLUtils;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.context.FLYSContext;
-import de.intevation.flys.artifacts.model.ManagedDomFacet;
-import de.intevation.flys.artifacts.model.ManagedFacet;
-import de.intevation.flys.themes.Theme;
-import de.intevation.flys.themes.ThemeFactory;
-
-public class OutputHelper {
-    /** The logger used in this class. */
-    private static Logger log = Logger.getLogger(OutputHelper.class);
-
-    protected String identifier;
-
-    public OutputHelper(String identifier) {
-        this.identifier = identifier;
-    }
-    /**
-     * Creates a concrete output.
-     *
-     * @param generator The OutGenerator that creates the output.
-     * @param outputName The name of the requested output.
-     * @param attributes The collection's attributes for this concrete output
-     * type.
-     * @param context The context object.
-     */
-    public void doOut(
-        OutGenerator generator,
-        String       outName,
-        String       facet,
-        Document     attributes,
-        CallContext  context)
-    throws IOException
-    {
-        boolean debug = log.isDebugEnabled();
-
-        if (debug) {
-            log.debug("FLYSArtifactCollection.doOut: " + outName);
-        }
-
-        ThemeList themeList = new ThemeList(attributes);
-
-        int size = themeList.size();
-        if (debug) {
-            log.debug("Output will contain " + size + " elements.");
-        }
-
-        List<ArtifactAndFacet> dataProviders =
-            doBlackboardPass(themeList, context);
-
-        try {
-            for (int i = 0; i < size; i++) {
-                ManagedFacet theme = themeList.get(i);
-
-                if (theme == null) {
-                    log.debug("Theme is empty - no output is generated.");
-                    continue;
-                }
-
-                String art = theme.getArtifact();
-                String facetName = theme.getName();
-
-                if (debug) {
-                    log.debug("Do output for...");
-                    log.debug("... artifact: " + art);
-                    log.debug("... facet: " + facetName);
-                }
-
-                if (outName.equals("export") && !facetName.equals(facet)) {
-                    continue;
-                }
-
-                // Skip invisible themes.
-                if (theme.getVisible() == 0) {
-                    continue;
-                }
-
-                if (outName.equals("sq_overview")) {
-                    generator.doOut(
-                        dataProviders.get(i),
-                        attributes,
-                        theme.getActive() == 1);
-                }
-                else {
-                    generator.doOut(
-                            dataProviders.get(i),
-                            getFacetThemeFromAttribute(
-                                art,
-                                outName,
-                                facetName,
-                                theme.getDescription(),
-                                theme.getIndex(),
-                                context),
-                            theme.getActive() == 1);
-                }
-            }
-        }
-        catch (ArtifactDatabaseException ade) {
-            log.error(ade, ade);
-        }
-    }
-    /**
-     * Returns the attribute that belongs to an artifact and facet stored in
-     * this collection.
-     *
-     * @param uuid The Artifact's uuid.
-     * @param outname The name of the requested output.
-     * @param facet The name of the requested facet.
-     * @param context The CallContext.
-     *
-     * @return an attribute in form of a document.
-     */
-    protected Document getFacetThemeFromAttribute(
-        String      uuid,
-        String      outName,
-        String      facet,
-        String      pattern,
-        int         index,
-        CallContext context)
-    throws    ArtifactDatabaseException
-    {
-        boolean debug = log.isDebugEnabled();
-
-        if (debug) {
-            log.debug(
-                "FLYSArtifactCollection.getFacetThemeFromAttribute(facet="
-                + facet + ", index=" + index + ")");
-        }
-
-        ArtifactDatabase db = context.getDatabase();
-        CallMeta       meta = context.getMeta();
-
-        Document attr = db.getCollectionItemAttribute(identifier, uuid, meta);
-
-        if (attr == null) {
-            attr = initItemAttribute(uuid, facet, pattern, index, outName, context);
-
-            if (attr == null) {
-                return null;
-            }
-        }
-
-        if (debug) {
-            log.debug("Search attribute of collection item: " + uuid);
-        }
-
-        Node tmp = (Node) XMLUtils.xpath(
-            attr,
-            "/art:attribute",
-            XPathConstants.NODE,
-            ArtifactNamespaceContext.INSTANCE);
-
-        if (tmp == null) {
-            log.warn("No attribute found. Operation failed.");
-            return null;
-        }
-
-        if (debug) {
-            log.debug("Search theme for facet '" + facet + "' in attribute.");
-        }
-
-        Map<String, String> vars = new HashMap<String, String>();
-        vars.put("facet", facet);
-        vars.put("index", String.valueOf(index));
-
-        Node theme = (Node) XMLUtils.xpath(
-            tmp,
-            "art:themes/theme[@facet=$facet and @index=$index]",
-            XPathConstants.NODE,
-            ArtifactNamespaceContext.INSTANCE,
-            vars);
-
-        if (theme == null) {
-            log.warn("Could not find the theme in attribute of: " + facet + " " + uuid);
-
-            Theme t = getThemeForFacet(
-                uuid, facet, pattern, index, outName, context);
-
-            if (t == null) {
-                log.warn("No theme found for facet: " + facet);
-                return null;
-            }
-
-            addThemeToAttribute(uuid, attr, t, context);
-            theme = t.toXML().getFirstChild();
-        }
-
-        Document doc = XMLUtils.newDocument();
-        doc.appendChild(doc.importNode(theme, true));
-
-        return doc;
-    }
-    /**
-     * Adds the theme of a facet to a CollectionItem's attribute.
-     *
-     * @param uuid The uuid of the artifact.
-     * @param attr The current attribute of an artifact.
-     * @param t The theme to add.
-     * @param context The CallContext.
-     */
-    protected void addThemeToAttribute(
-        String      uuid,
-        Document    attr,
-        Theme       t,
-        CallContext context)
-    {
-        log.debug("FLYSArtifactCollection.addThemeToAttribute: " + uuid);
-
-        if (t == null) {
-            log.warn("Theme is empty - cancel adding it to attribute!");
-            return;
-        }
-
-        XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(
-            attr,
-            ArtifactNamespaceContext.NAMESPACE_URI,
-            ArtifactNamespaceContext.NAMESPACE_PREFIX);
-
-        Node tmp = (Node) XMLUtils.xpath(
-            attr,
-            "/art:attribute",
-            XPathConstants.NODE,
-            ArtifactNamespaceContext.INSTANCE);
-
-        if (tmp == null) {
-            tmp = ec.create("attribute");
-            attr.appendChild(tmp);
-        }
-
-        Node themes = (Node) XMLUtils.xpath(
-            tmp,
-            "art:themes",
-            XPathConstants.NODE,
-            ArtifactNamespaceContext.INSTANCE);
-
-        if (themes == null) {
-            themes = ec.create("themes");
-            tmp.appendChild(themes);
-        }
-
-        themes.appendChild(attr.importNode(t.toXML().getFirstChild(), true));
-
-        try {
-            setCollectionItemAttribute(uuid, attr, context);
-        }
-        catch (ArtifactDatabaseException e) {
-            // do nothing
-            log.warn("Cannot set attribute of item: " + uuid);
-        }
-    }
-
-    /**
-     * Sets the attribute of a CollectionItem specified by <i>uuid</i> to a new
-     * value <i>attr</i>.
-     *
-     * @param uuid The uuid of the CollectionItem.
-     * @param attr The new attribute for the CollectionItem.
-     * @param context The CallContext.
-     */
-    public void setCollectionItemAttribute(
-        String      uuid,
-        Document    attr,
-        CallContext context)
-    throws ArtifactDatabaseException
-    {
-        Document doc = ClientProtocolUtils.newSetItemAttributeDocument(
-            uuid,
-            attr);
-
-        if (doc == null) {
-            log.warn("Cannot set item attribute: No attribute found.");
-            return;
-        }
-
-        ArtifactDatabase db = context.getDatabase();
-        CallMeta       meta = context.getMeta();
-
-        db.setCollectionItemAttribute(identifier, uuid, doc, meta);
-    }
-
-
-    /**
-     * Show blackboard (context) to each facet and create a list of
-     * ArtifactAndFacets on the fly (with the same ordering as the passed
-     * ThemeList).
-     * @param themeList ThemeList to create a ArtifactAndFacetList along.
-     * @param context   The "Blackboard".
-     */
-    protected List<ArtifactAndFacet> doBlackboardPass(
-        ThemeList themeList, CallContext context
-    ) {
-        ArrayList<ArtifactAndFacet> dataProviders =
-            new ArrayList<ArtifactAndFacet>();
-        int size = themeList.size();
-
-        try {
-            // Collect all ArtifactAndFacets for blackboard pass.
-            for (int i = 0; i < size; i++) {
-                ManagedFacet theme = themeList.get(i);
-                if (theme == null) {
-                    log.warn("A ManagedFacet in ThemeList is null.");
-                    continue;
-                }
-                String uuid        = theme.getArtifact();
-                Artifact artifact  = getArtifact(uuid, context);
-                FLYSArtifact flys  = (FLYSArtifact) artifact;
-
-                ArtifactAndFacet artifactAndFacet = new ArtifactAndFacet(
-                    artifact,
-                    flys.getNativeFacet(theme));
-
-                // XXX HELP ME PLEASE
-                artifactAndFacet.setFacetDescription(theme.getDescription());
-
-                // Show blackboard to facet.
-                artifactAndFacet.register(context);
-
-                // Add to themes.
-                dataProviders.add(i, artifactAndFacet);
-            }
-        }
-        catch (ArtifactDatabaseException ade) {
-            log.error("ArtifactDatabaseException!", ade);
-        }
-
-        return dataProviders;
-    }
-    /**
-     * Returns a concrete Artifact of this collection specified by its uuid.
-     *
-     * @param uuid The Artifact's uuid.
-     * @param context The CallContext.
-     *
-     * @return an Artifact.
-     */
-    protected Artifact getArtifact(String uuid, CallContext context)
-    throws    ArtifactDatabaseException
-    {
-        log.debug("FLYSArtifactCollection.getArtifact");
-
-        Backend backend               = Backend.getInstance();
-        PersistentArtifact persistent = backend.getArtifact(uuid);
-
-        return persistent != null ? persistent.getArtifact() : null;
-    }
-
-    /**
-     * Initializes the attribute of an collection item with the theme of a
-     * specific facet.
-     *
-     * @param uuid The uuid of an artifact.
-     * @param facet The name of a facet.
-     * @param context The CallContext.
-     *
-     * @param the new attribute.
-     */
-    protected Document initItemAttribute(
-        String      uuid,
-        String      facet,
-        String      pattern,
-        int         index,
-        String      outName,
-        CallContext context)
-    {
-        boolean debug = log.isDebugEnabled();
-
-        if (debug) {
-            log.debug("FLYSArtifactCollection.initItemAttribute");
-        }
-
-        Theme t = getThemeForFacet(uuid, facet, pattern, index, outName, context);
-
-        if (t == null) {
-            log.info("Could not find theme for facet. Cancel initialization.");
-            return null;
-        }
-
-        Document attr = XMLUtils.newDocument();
-        addThemeToAttribute(uuid, attr, t, context);
-
-        if (debug) {
-            log.debug("initItemAttribute for facet " + facet + ": "
-                + XMLUtils.toString(attr));
-        }
-
-        return attr;
-    }
-
-        /**
-     * Returns the theme of a specific facet.
-     *
-     * @param uuid The uuid of an artifact.
-     * @param facet The name of the facet.
-     * @param context The CallContext object.
-     *
-     * @return the desired theme.
-     */
-    protected Theme getThemeForFacet(
-        String uuid,
-        String facet,
-        String pattern,
-        int    index,
-        String outName,
-        CallContext context)
-    {
-        log.info("FLYSArtifactCollection.getThemeForFacet: " + facet);
-
-        FLYSContext flysContext = context instanceof FLYSContext
-            ? (FLYSContext) context
-            : (FLYSContext) context.globalContext();
-
-        // Push artifact in flysContext.
-        ArtifactDatabase db = context.getDatabase();
-        try {
-            FLYSArtifact artifact = (FLYSArtifact) db.getRawArtifact(uuid);
-            log.debug("Got raw artifact");
-            flysContext.put(FLYSContext.ARTIFACT_KEY, artifact);
-        }
-        catch (ArtifactDatabaseException dbe) {
-            log.error("Exception caught when trying to get art.", dbe);
-        }
-
-        Theme t = ThemeFactory.getTheme(
-                      flysContext,
-                      facet,
-                      pattern,
-                      outName,
-                      "default");
-
-        if (t != null) {
-            log.debug("found theme for facet '" + facet + "'");
-            t.setFacet(facet);
-            t.setIndex(index);
-        }
-        else {
-            log.warn("unable to find theme for facet '" + facet + "'");
-        }
-
-        return t;
-    }
-
-    /**
-     * Inner class to structure/order the themes of a chart.
-     */
-    private static class ThemeList {
-        private Logger logger = Logger.getLogger(ThemeList.class);
-        protected List<ManagedFacet> themes;
-
-        public ThemeList(Document output) {
-            themes = new ArrayList<ManagedFacet>();
-            parse(output);
-        }
-
-        protected void parse(Document output) {
-            NodeList themeList = (NodeList) XMLUtils.xpath(
-                output,
-                "art:output/art:facet",
-                XPathConstants.NODESET,
-                ArtifactNamespaceContext.INSTANCE);
-
-            int num = themeList != null ? themeList.getLength() : 0;
-
-            logger.debug("Output has " +  num + " elements.");
-
-            if (num == 0) {
-                return;
-            }
-
-            for (int i = 0; i < num; i++) {
-                Element theme = (Element) themeList.item(i);
-
-                ManagedDomFacet facet = new ManagedDomFacet(theme);
-                themes.add(facet);
-            }
-
-            Collections.sort(themes);
-        }
-
-        public ManagedFacet get(int idx) {
-            return themes.get(idx);
-        }
-
-        public int size() {
-            return themes.size();
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/ReferenceCurveExporter.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,370 +0,0 @@
-package de.intevation.flys.exports;
-
-import au.com.bytecode.opencsv.CSVWriter;
-
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.artifacts.common.utils.Config;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.WINFOArtifact;
-
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.WWQQ;
-import de.intevation.flys.artifacts.model.WWQQJRDataSource;
-
-import de.intevation.flys.artifacts.resources.Resources;
-
-import de.intevation.flys.utils.FLYSUtils;
-
-import java.io.IOException;
-import java.io.OutputStream;
-
-import java.text.DateFormat;
-import java.text.NumberFormat;
-
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-
-import net.sf.jasperreports.engine.JRException;
-import net.sf.jasperreports.engine.JasperExportManager;
-import net.sf.jasperreports.engine.JasperFillManager;
-import net.sf.jasperreports.engine.JasperPrint;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Document;
-
-/**
- * (CSV)Exporter for Reference Curves.
- */
-public class ReferenceCurveExporter extends AbstractExporter {
-
-    /** The logger used in this exporter. */
-    private static Logger logger = Logger.getLogger(ReferenceCurveExporter.class);
-
-    public static final String RC_CSV_KM_HEADER =
-        "export.reference_curve.csv.header.km";
-
-    public static final String RC_CSV_W_CM_HEADER =
-        "export.reference_curve.csv.header.w.cm";
-
-    public static final String RC_CSV_W_M_HEADER =
-        "export.reference_curve.csv.header.w.m";
-
-    public static final String RC_CSV_Q_HEADER =
-        "export.reference_curve.csv.header.w.q";
-
-    public static final String CSV_LOCATION_HEADER =
-        "export.waterlevel.csv.header.location";
-
-    public static final String DEFAULT_CSV_LOCATION_HEADER = "Lage";
-
-    public static final String RC_DEFAULT_CSV_KM_HEADER = "Fluss-Km";
-    public static final String RC_DEFAULT_CSV_W_M_HEADER  = "W (m + NHN)";
-    public static final String RC_DEFAULT_CSV_W_CM_HEADER  = "W (cm am Pegel)";
-    public static final String RC_DEFAULT_CSV_Q_HEADER  = "gleichw. Q (m\u00b3/s)";
-
-    public static final String PDF_HEADER_MODE = "export.reference_curve.pdf.mode";
-    public static final String JASPER_FILE = "export.reference_curve.pdf.file";
-    public static final String JASPER_FILE_GAUGE = "export.reference_curve.pdf.file.gauge";
-    public static final String JASPER_FILE_GAUGE_END = "export.reference_curve.pdf.file.gauge.end";
-    public static final String JASPER_FILE_GAUGE_START_END = "export.reference_curve.pdf.file.gauge.start.end";
-
-    /** The storage that contains all WKms objects for the different facets. */
-    protected List<WWQQ[]> data;
-
-    protected boolean startAtGauge = false;
-
-    protected boolean endAtGauge = false;
-
-
-    @Override
-    public void init(Document request, OutputStream out, CallContext context) {
-        logger.debug("ReferenceCurveExporter.init");
-
-        super.init(request, out, context);
-
-        this.data = new ArrayList<WWQQ[]>();
-    }
-
-
-    /**
-     * Genereate data in csv format.
-     */
-    @Override
-    public void generate()
-    throws IOException
-    {
-        logger.debug("ReferenceCurveExporter.generate");
-
-        if (facet == null) {
-            throw new IOException("invalid (null) facet for exporter");
-        }
-        else if (facet.equals(AbstractExporter.FACET_CSV)) {
-            generateCSV();
-        }
-        else if (facet.equals(AbstractExporter.FACET_PDF)) {
-            generatePDF();
-        }
-        else {
-            throw new IOException("invalid facet (" + facet + ") for exporter");
-        }
-    }
-
-
-    /**
-     * Adds given data.
-     * @param d A CalculationResult with WWQQ[].
-     */
-    @Override
-    protected void addData(Object d) {
-        logger.debug("ReferenceCurveExporter.addData");
-
-        if (d instanceof CalculationResult) {
-            d = ((CalculationResult)d).getData();
-            if (d instanceof WWQQ []) {
-                WWQQ[] wwqqs = (WWQQ []) d;
-                for (WWQQ wwqq: wwqqs) {
-                    if (wwqq.startAtGauge()) {
-                        startAtGauge = true;
-                    }
-                    // TODO this one probably has to be inverted.
-                    if (wwqq.endAtGauge()) {
-                        endAtGauge = true;
-                    }
-                }
-                data.add(wwqqs);
-                logger.debug("ReferenceCurveExporter.addData wwqq[].");
-            }
-            else {
-                logger.warn("ReferenceCurveExporter.addData/1 unknown type ("
-                    + d + ").");
-            }
-        }
-        else {
-            logger.warn("ReferenceCurveExporter.addData/2 unknown type ("
-                + d + ").");
-        }
-    }
-
-
-    /**
-     * Lets writer write all data (including header).
-     * @param writer Writer to write data with.
-     */
-    @Override
-    protected void writeCSVData(CSVWriter writer) {
-        logger.debug("ReferenceCurveExporter.writeData");
-
-        writeCSVHeader(writer);
-
-        for (WWQQ[] tmp: data) {
-            for (WWQQ ww: tmp) {
-                wWQQ2CSV(writer, ww);
-            }
-        }
-    }
-
-
-    /**
-     * Lets csvwriter write the header (first line in file).
-     * @param writer Writer to write header with.
-     */
-    protected void writeCSVHeader(CSVWriter writer) {
-        logger.info("ReferenceCurveExporter.writeCSVHeader");
-
-        StepCSVWriter stepWriter = new StepCSVWriter();
-        stepWriter.setCSVWriter(writer);
-
-        stepWriter.addNexts(
-            msg(RC_CSV_KM_HEADER, RC_DEFAULT_CSV_KM_HEADER),
-            msg(RC_CSV_W_M_HEADER, RC_DEFAULT_CSV_W_M_HEADER)
-            );
-        if (startAtGauge) {
-            stepWriter.addNext(
-                msg(RC_CSV_W_CM_HEADER, RC_DEFAULT_CSV_W_CM_HEADER));
-        }
-        stepWriter.addNexts(
-            msg(RC_CSV_Q_HEADER, RC_DEFAULT_CSV_Q_HEADER),
-            msg(CSV_LOCATION_HEADER, DEFAULT_CSV_LOCATION_HEADER),
-            msg(RC_CSV_KM_HEADER, RC_DEFAULT_CSV_KM_HEADER),
-            msg(RC_CSV_W_M_HEADER, RC_DEFAULT_CSV_W_M_HEADER)
-            );
-        if (endAtGauge) {
-            stepWriter.addNext(
-                msg(RC_CSV_W_CM_HEADER, RC_DEFAULT_CSV_W_CM_HEADER)
-                );
-        }
-        stepWriter.addNexts(
-                msg(RC_CSV_Q_HEADER, RC_DEFAULT_CSV_Q_HEADER),
-                msg(CSV_LOCATION_HEADER, DEFAULT_CSV_LOCATION_HEADER)
-                );
-
-        stepWriter.flush();
-    }
-
-
-    protected void wWQQ2CSV(CSVWriter writer, WWQQ ww) {
-        logger.debug("ReferenceCurveExporter.wWQQ2CSV");
-
-        NumberFormat kmf = getKmFormatter();
-        NumberFormat wf  = getWFormatter();
-        NumberFormat qf  = getQFormatter();
-
-        int         size = ww.size();
-
-        FLYSArtifact flys       = (FLYSArtifact) master;
-
-        StepCSVWriter stepWriter = new StepCSVWriter();
-        stepWriter.setCSVWriter(writer);
-
-        String startLocationDescription = FLYSUtils.getLocationDescription(
-            flys, ww.getStartKm());
-
-        String endLocationDescription = FLYSUtils.getLocationDescription(
-            flys, ww.getEndKm());
-
-        for (int i = 0; i < size; i ++) {
-            stepWriter.addNexts(kmf.format(ww.getStartKm()));
-            stepWriter.addNext(wf.format(ww.getW1(i)));
-            if (startAtGauge) {
-                stepWriter.addNext(wf.format(ww.getRelHeight1Cm(i)));
-            }
-            stepWriter.addNexts(
-                qf.format(ww.getQ1(i)), // "Q"
-                startLocationDescription,
-                kmf.format(ww.getEndKm())
-                );
-            stepWriter.addNext(wf.format(ww.getW2(i)));
-            if (endAtGauge) {
-                if (ww.endAtGauge()) {
-                    stepWriter.addNext(wf.format(ww.getRelHeight2Cm(i)));
-                }
-                else {
-                    stepWriter.addNext("-");
-                }
-            }
-            stepWriter.addNexts(
-                qf.format(ww.getQ2(i)), // "Q"
-                endLocationDescription
-                );
-            stepWriter.flush();
-        }
-    }
-
-
-    @Override
-    protected void writePDF(OutputStream out) {
-        WWQQJRDataSource source = createJRData();
-
-        String filename = JASPER_FILE;
-        if (startAtGauge && endAtGauge) {
-            filename = JASPER_FILE_GAUGE_START_END;
-        }
-        else if (startAtGauge) {
-            filename = JASPER_FILE_GAUGE;
-        }
-        else if (endAtGauge) {
-            filename = JASPER_FILE_GAUGE_END;
-        }
-
-        String jasperFile = Resources.getMsg(
-                                context.getMeta(),
-                                filename,
-                                "/jasper/reference_en.jasper");
-        String confPath = Config.getConfigDirectory().toString();
-
-        Map parameters = new HashMap();
-        parameters.put("ReportTitle", "Exported Data");
-        try {
-            JasperPrint print = JasperFillManager.fillReport(
-                confPath + jasperFile,
-                parameters,
-                source);
-            JasperExportManager.exportReportToPdfStream(print, out);
-        }
-        catch(JRException je) {
-            logger.warn("Error generating PDF Report!");
-            je.printStackTrace();
-        }
-    }
-
-    protected WWQQJRDataSource createJRData() {
-        WWQQJRDataSource source = new WWQQJRDataSource();
-
-        addMetaData(source);
-
-        for (WWQQ[] tmp: data) {
-            for (WWQQ ww: tmp) {
-                addWWQQData(source, ww);
-            }
-        }
-        return source;
-    }
-
-
-    protected void addMetaData(WWQQJRDataSource source) {
-        CallMeta meta = context.getMeta();
-
-        WINFOArtifact flys = (WINFOArtifact) master;
-
-        source.addMetaData ("river", FLYSUtils.getRivername(flys));
-
-        Locale locale = Resources.getLocale(meta);
-        DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, locale);
-
-        source.addMetaData("date", df.format(new Date()));
-
-        source.addMetaData("calculation", Resources.getMsg(
-                                            locale,
-                                            PDF_HEADER_MODE,
-                                            "Reference Curve"));
-    }
-
-
-    protected void addWWQQData(WWQQJRDataSource source, WWQQ ww) {
-        NumberFormat kmf = getKmFormatter();
-        NumberFormat wf  = getWFormatter();
-        NumberFormat qf  = getQFormatter();
-
-        int          size = ww.size();
-
-        FLYSArtifact flys = (FLYSArtifact) master;
-
-        String startLocationDescription = FLYSUtils.getLocationDescription(
-            flys, ww.getStartKm());
-
-        String endLocationDescription = FLYSUtils.getLocationDescription(
-            flys, ww.getEndKm());
-
-        for (int i = 0; i < size; i ++) {
-            String start = "-";
-            String end = "-";
-            if (startAtGauge) {
-                start = wf.format(ww.getRelHeight1Cm(i));
-            }
-            if (ww.endAtGauge()) {
-                end = wf.format(ww.getRelHeight2Cm(i));
-            }
-            source.addData(new String[] {
-                kmf.format(ww.getStartKm()),
-                startLocationDescription,
-                wf.format(ww.getW1(i)),
-                qf.format(ww.getQ1(i)), // "Q"
-                kmf.format(ww.getEndKm()),
-                endLocationDescription,
-                wf.format(ww.getW2(i)),
-                qf.format(ww.getQ2(i)), // "Q"
-                start,
-                end
-            });
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/ReferenceCurveGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,271 +0,0 @@
-package de.intevation.flys.exports;
-
-import de.intevation.artifactdatabase.state.ArtifactAndFacet;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.WW;
-import de.intevation.flys.artifacts.model.WW.ApplyFunctionIterator;
-import de.intevation.flys.artifacts.model.WWAxisTypes;
-import de.intevation.flys.jfree.FLYSAnnotation;
-import de.intevation.flys.jfree.StyledXYSeries;
-import de.intevation.flys.utils.Formatter;
-
-import java.awt.geom.Point2D;
-
-import org.apache.log4j.Logger;
-import org.jfree.chart.axis.NumberAxis;
-import org.jfree.chart.axis.NumberTickUnit;
-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.
- */
-public class ReferenceCurveGenerator
-extends      XYChartGenerator
-implements   FacetTypes
-{
-    public static enum YAXIS {
-        W(0);
-
-        public int idx;
-        private YAXIS(int c) {
-           idx = c;
-        }
-    }
-
-    /** House logger. */
-    private static Logger logger =
-        Logger.getLogger(ReferenceCurveGenerator.class);
-
-    public static final String I18N_CHART_TITLE =
-        "chart.reference.curve.title";
-
-    public static final String I18N_CHART_SUBTITLE =
-        "chart.reference.curve.subtitle";
-
-    public static final String I18N_X_AXIS_IN_CM =
-        "chart.reference.curve.x.axis.in.cm";
-
-    public static final String I18N_X_AXIS_IN_M =
-        "chart.reference.curve.x.axis.in.m";
-
-    public static final String I18N_Y_AXIS_IN_CM =
-        "chart.reference.curve.y.axis.in.cm";
-
-    public static final String I18N_Y_AXIS_IN_M =
-        "chart.reference.curve.y.axis.in.m";
-
-    public static final String I18N_CHART_TITLE_DEFAULT  =
-        "Bezugslinie";
-
-
-    public ReferenceCurveGenerator() {
-    }
-
-    /**
-     * Create Axis for given index.
-     * @return axis with according internationalized label.
-     */
-    @Override
-    protected NumberAxis createYAxis(int index) {
-        NumberAxis axis = super.createYAxis(index);
-        axis.setAutoRangeIncludesZero(false);
-        return axis;
-    }
-
-
-    /** Get default chart title. */
-    @Override
-    protected String getDefaultChartTitle() {
-        return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT);
-    }
-
-    @Override
-    protected String getDefaultChartSubtitle() {
-        Object[] args = new Object[] {
-            getRiverName(),
-        };
-
-        return msg(I18N_CHART_SUBTITLE, "", args);
-    }
-
-
-    /** True if axis is in cm (because at gauge). */
-    protected boolean getInCm(int index) {
-        Object obj = context.getContextValue("reference.curve.axis.scale");
-        return obj instanceof WWAxisTypes && ((WWAxisTypes)obj).getInCm(index);
-    }
-
-
-    /** Get Label for X-axis (W). */
-    @Override
-    protected String getDefaultXAxisLabel() {
-        return msg(getInCm(0) ? I18N_X_AXIS_IN_CM : I18N_X_AXIS_IN_M);
-    }
-
-
-    /**
-     * Get Label for primary and other Y Axes.
-     * @param index Axis-Index (0-based).
-     */
-    @Override
-    protected String getDefaultYAxisLabel(int index) {
-        return msg(getInCm(1) ? I18N_Y_AXIS_IN_CM : I18N_Y_AXIS_IN_M);
-    }
-
-    protected String facetName() {
-        return REFERENCE_CURVE;
-    }
-
-
-    /**
-     * Called for each facet/them in the out mapped to this generator.
-     * @param artifactFacet artifact and facet for this theme.
-     * @param theme         styling info.
-     * @param visible       Whether or not the theme is visible.
-     */
-    @Override
-    public void doOut(
-        ArtifactAndFacet artifactFacet,
-        Document         theme,
-        boolean          visible
-    ) {
-        String name = artifactFacet.getFacetName();
-
-        logger.debug("ReferenceCurveGenerator.doOut: " + name);
-
-        if (name == null || name.length() == 0) {
-            logger.error("No facet given. Cannot create dataset.");
-            return;
-        }
-
-        if (name.equals(facetName())) {
-            doReferenceOut(artifactFacet.getData(context), theme, visible);
-        }
-        else if (FacetTypes.IS.MANUALPOINTS(name)) {
-            doPoints(
-                artifactFacet.getData(context),
-                artifactFacet,
-                theme,
-                visible,
-                YAXIS.W.idx);
-        }
-        else if (name.equals(RELATIVE_POINT)) {
-            doPointOut(
-                (Point2D) artifactFacet.getData(context),
-                artifactFacet,
-                theme,
-                visible);
-        }
-        else if (name.equals(MAINVALUES_W)) {
-            doAnnotations(
-                ((FLYSAnnotation) artifactFacet.getData(context)).flipStickyAxis(),
-                artifactFacet,
-                theme,
-                visible);
-
-        }
-        else {
-            logger.warn("Unknown facet name: " + name);
-        }
-    }
-
-    protected boolean doNormalize() {
-        return false;
-    }
-
-
-    /** Register DataSeries with (maybe transformed) points. */
-    public void doReferenceOut(
-        Object   data,
-        Document theme,
-        boolean  visible
-    ) {
-        WW ww = (WW)data;
-
-        Object obj = context.getContextValue("reference.curve.axis.scale");
-
-        WWAxisTypes wwat = obj instanceof WWAxisTypes
-            ? (WWAxisTypes)obj
-            : new WWAxisTypes(ww);
-
-        ApplyFunctionIterator iter = wwat.transform(ww, doNormalize());
-
-        XYSeries series = new StyledXYSeries(
-            ww.getName(), false, theme);
-
-        double [] values = new double[2];
-
-        while (iter.hasNext()) {
-            iter.next(values);
-            series.add(values[0], values[1], false);
-        }
-
-        addAxisSeries(series, YAXIS.W.idx, visible);
-    }
-
-    // TODO resolve duplicate in DurationCurveGenerator
-    protected void doPointOut(
-        Point2D point,
-        ArtifactAndFacet aandf,
-        Document theme,
-        boolean visible
-    ){
-        logger.debug("ReferenceCurveGenerator.doPointOut");
-
-        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
-
-        series.add(point.getX(), point.getY());
-
-        addAxisSeries(series, YAXIS.W.idx, visible);
-    }
-
-
-    /** Set the tick units for given axis. */
-    protected void setAxisTickUnit(double tick, ValueAxis axis) {
-        TickUnits units = new TickUnits();
-        units.add(new NumberTickUnit(tick, Formatter.getWaterlevelW(context)));
-        axis.setStandardTickUnits(units);
-        axis.setAutoTickUnitSelection(true);
-    }
-
-    @Override
-    protected void localizeDomainAxis(ValueAxis domainAxis) {
-        super.localizeDomainAxis(domainAxis);
-        if (getInCm(0)) {
-            setAxisTickUnit(100d, domainAxis);
-        }
-        else {
-            setAxisTickUnit(1d, domainAxis);
-        }
-    }
-
-
-    @Override
-    protected void localizeRangeAxis(ValueAxis rangeAxis) {
-        super.localizeRangeAxis(rangeAxis);
-        setAxisTickUnit(1d, rangeAxis);
-    }
-
-    /** Get Walker to iterate over all axes. */
-    @Override
-    protected YAxisWalker getYAxisWalker() {
-        return new YAxisWalker() {
-            /** Get number of items. */
-            @Override
-            public int length() {
-                return YAXIS.values().length;
-            }
-
-            /** Get identifier for this index. */
-            @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/flys-artifacts/src/main/java/de/intevation/flys/exports/ReferenceCurveInfoGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@
-package de.intevation.flys.exports;
-
-
-/**
- * A ChartInfoGenerator that generates meta information for specific reference
- * curves.
- */
-public class ReferenceCurveInfoGenerator
-extends      ChartInfoGenerator
-{
-    public ReferenceCurveInfoGenerator() {
-        super(new ReferenceCurveGenerator());
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/ReportGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,96 +0,0 @@
-package de.intevation.flys.exports;
-
-import java.io.IOException;
-import java.io.OutputStream;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.artifacts.common.utils.XMLUtils;
-
-import de.intevation.artifactdatabase.state.ArtifactAndFacet;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifactdatabase.state.Settings;
-
-import de.intevation.flys.artifacts.model.Calculation;
-import de.intevation.flys.collections.FLYSArtifactCollection;
-
-import org.w3c.dom.Document;
-
-public class ReportGenerator
-implements   OutGenerator
-{
-    private static Logger logger = Logger.getLogger(ReportGenerator.class);
-
-    protected Document     result;
-    protected OutputStream out;
-    protected CallContext  context;
-
-    public ReportGenerator() {
-    }
-
-    @Override
-    public void init(Document request, OutputStream out, CallContext context) {
-        logger.debug("init");
-        this.out     = out;
-        this.context = context;
-        result       = null;
-    }
-
-    @Override
-    public void setMasterArtifact(Artifact master) {
-        // not needed
-    }
-
-    @Override
-    public void setCollection(FLYSArtifactCollection collection) {
-        // not needed
-    }
-
-    @Override
-    public void doOut(
-        ArtifactAndFacet artifactFacet,
-        Document         attr,
-        boolean          visible
-    ) {
-        logger.debug("doOut");
-        Facet facet = artifactFacet.getFacet();
-        if (facet != null) {
-            Calculation report = (Calculation) artifactFacet.getData(context);
-            if (result == null) {
-                result = XMLUtils.newDocument();
-                report.toXML(result, context.getMeta());
-            }
-        }
-    }
-
-    @Override
-    public void generate() throws IOException {
-        logger.debug("generate");
-        XMLUtils.toStream(result != null
-            ? result
-            : XMLUtils.newDocument(), out);
-    }
-
-
-    /**
-     * Returns an instance of <i>EmptySettings</i> currently!
-     *
-     * @return an instance of <i>EmptySettings</i>.
-     */
-    public Settings getSettings() {
-        return new EmptySettings();
-    }
-
-
-    /**
-     * This method is not implemented. Override it in subclasses if those need a
-     * <i>Settings</i> object.
-     */
-    public void setSettings(Settings settings) {
-        // do nothing
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/StepCSVWriter.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,51 +0,0 @@
-package de.intevation.flys.exports;
-
-import au.com.bytecode.opencsv.CSVWriter;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-
-/**
- * Class to overcome shortcoming of CSVWriter to accept String-Arrays only.
- * The StepCSVWriter buffers incoming values, such that rows in a csv can be
- * created more dynamically. Do not forget to call flush().
- */
-public class StepCSVWriter {
-
-    /** Writer to use when calling flush. */
-    CSVWriter writer = null;
-    /** Buffer of strings (values). */
-    ArrayList<String> buffer;
-
-
-    /** Trivial constructor. */
-    public StepCSVWriter() {
-        buffer = new ArrayList<String>();
-    }
-
-
-    /** Set writer. */
-    public void setCSVWriter(CSVWriter writer) {
-        this.writer = writer;
-    }
-
-
-    /** Add a value to next flush. */
-    public void addNext(String value) {
-        buffer.add(value);
-    }
-
-
-    /** Add many values to next flush. */
-    public void addNexts(String ... values) {
-        buffer.addAll(Arrays.asList(values));
-    }
-
-
-    /** Write the row with csvwriter. */
-    public void flush() {
-        writer.writeNext(buffer.toArray(new String[buffer.size()]));
-        buffer.clear();
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/StringAttribute.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,34 +0,0 @@
-package de.intevation.flys.exports;
-
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class StringAttribute extends VisibleAttribute {
-
-
-    public StringAttribute(String name, String value, boolean visible) {
-        super(name, value, visible);
-    }
-
-
-    /**
-     * Calls VisibleAttribute.toXML() and appends afterwards an attribute
-     * <i>type</i> with value <i>string</i>.
-     *
-     * @param parent The parent Node.
-     *
-     * @return the new Node that represents this Attribute.
-     */
-    @Override
-    public Node toXML(Node parent) {
-        Element ele = (Element) super.toXML(parent);
-        ele.setAttribute("type", "string");
-
-        return ele;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/StyledSeriesBuilder.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,240 +0,0 @@
-package de.intevation.flys.exports;
-
-import org.apache.log4j.Logger;
-
-import org.jfree.data.xy.XYSeries;
-
-import de.intevation.flys.artifacts.model.WKms;
-import de.intevation.flys.artifacts.model.WQKms;
-import de.intevation.flys.artifacts.model.WWQQ;
-
-/**
- * Helper to create and modify StyledXYSeries.
- */
-public class StyledSeriesBuilder {
-
-    /**
-     * JFreeChart and the area calculation will fail if we use Double.INFINITY
-     * or Double.MAX_VALUE (probably because these are really used in
-     * calculations). We define and use a more handy value instead.
-     */
-    final static double BIG_DOUBLE_VALUE = 1234567d;
-
-    private static final Logger logger = Logger.getLogger
-        (StyledSeriesBuilder.class);
-
-
-    /**
-     * Trivial, hidden constructor.
-     */
-    private StyledSeriesBuilder() {
-    }
-
-
-    /**
-     * Add points to series.
-     *
-     * @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.
-     */
-    public static void addPoints(XYSeries series, double[][] points, boolean skipNANs) {
-        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;
-            }
-            series.add(xPoints[i], yPoints[i], false);
-        }
-    }
-
-
-    /**
-     * Add points to series (km to 1st dim, w to 2nd dim).
-     *
-     * @param series Series to add points to.
-     * @param wkms WKms to add to series.
-     */
-    public static void addPoints(XYSeries series, WKms wkms) {
-        if (wkms == null) {
-            return;
-        }
-
-        int size = wkms.size();
-
-        for (int i = 0; i < size; i++) {
-            series.add(wkms.getKm(i), wkms.getW(i), false);
-        }
-    }
-
-
-    /**
-     * Add points to dataset with an offset (shift all points by given amount).
-     * @param series series to add data to.
-     * @param wkms WKms of which the Ws will be shifted.
-     * @param off the offset.
-     */
-    public static void addUpperBand(XYSeries series, WKms wkms, double off) {
-        if (wkms == null) {
-            return;
-        }
-
-        int size = wkms.size();
-
-        for (int i = 0; i < size; i++) {
-            series.add(wkms.getKm(i), wkms.getW(i)+off, false);
-        }
-    }
-
-
-    /**
-     * Add points to dataset with an offset (shift all points 'down' by given
-     * amount).
-     * @param series series to add data to.
-     * @param wkms WKms of which the Ws will be shifted.
-     * @param off the offset.
-     */
-    public static void addLowerBand(XYSeries series, WKms wkms, double off) {
-        addUpperBand(series, wkms, -off);
-    }
-
-
-    /**
-     * Add points to series (km to 1st dim, q to 2nd dim).
-     *
-     * @param series Series to add points to.
-     * @param wqkms WQKms to add to series.
-     */
-    public static void addPointsKmQ(XYSeries series, WQKms wqkms) {
-        if (wqkms == null) {
-            return;
-        }
-
-        int size = wqkms.size();
-
-        for (int i = 0; i < size; i++) {
-            series.add(wqkms.getKm(i), wqkms.getQ(i), false);
-        }
-    }
-
-
-    /**
-     * Add points to series (km to 1st dim, q to 2nd dim), adding points
-     * to achieve a step-like curve.
-     *
-     * @param series Series to add points to.
-     * @param wqkms WQKms to add to series.
-     */
-    public static void addStepPointsKmQ(XYSeries series, WQKms wqkms) {
-        if (wqkms == null) {
-            return;
-        }
-
-        int size = wqkms.size();
-
-        for (int i = 0; i < size; i++) {
-            if (i==0) {
-                series.add(wqkms.getKm(i), wqkms.getQ(i), false);
-            }
-            else {
-                //Add two points.
-                double halveX = (wqkms.getKm(i-1) + wqkms.getKm(i)) / 2d;
-                series.add(halveX, wqkms.getQ(i-1), false);
-                series.add(halveX, wqkms.getQ(i), false);
-            }
-            if (i == size-1) {
-                series.add(wqkms.getKm(i), wqkms.getQ(i), false);
-            }
-        }
-    }
-
-
-    /**
-     * Add points to series (q to 1st dim, w to 2nd dim).
-     *
-     * @param series Series to add points to.
-     * @param wqkms WQKms to add to series.
-     */
-    public static void addPointsQW(XYSeries series, WQKms wqkms) {
-        if (wqkms == null) {
-            return;
-        }
-
-        int size = wqkms.size();
-
-        for (int i = 0; i < size; i++) {
-            series.add(wqkms.getQ(i), wqkms.getW(i), false);
-        }
-    }
-
-
-    /**
-     * Add points to series (q to 1st dim, w to 2nd dim).
-     *
-     * @param series Series to add points to.
-     * @param qs the Qs to add, assumed same length than ws.
-     * @param ws the Ws to add, assumed same length than qs.
-     */
-    public static void addPointsQW(XYSeries series, double[] qs, double ws[]) {
-        if (ws == null || qs == null) {
-            return;
-        }
-
-        int size = qs.length;
-
-        for (int i = 0; i < size; i++) {
-            series.add(qs[i], ws[i], false);
-        }
-    }
-
-
-    /**
-     * Add points to series (q to 1st dim, w to 2nd dim).
-     *
-     * @param series Series to add points to.
-     * @param wwqq WWQQ to add to series.
-     */
-    public static void addPoints(XYSeries series, WWQQ wwqq) {
-        if (wwqq == null) {
-            return;
-        }
-
-        int size = wwqq.size();
-
-        for (int i = 0; i < size; i++) {
-            series.add(wwqq.getW1(i), wwqq.getW2(i), false);
-        }
-    }
-
-
-    /**
-     * Create a Series such that an infinitely big area can be filled
-     * between the newly created and the given series.
-     */
-    public static XYSeries createGroundAtInfinity(XYSeries series) {
-        XYSeries ground = new XYSeries(series.getKey() + /** TODO rand + */ "INF");
-        ground.add(series.getMinX(), -BIG_DOUBLE_VALUE);
-        ground.add(series.getMaxX(), -BIG_DOUBLE_VALUE);
-        return ground;
-    }
-
-
-    /**
-     * Create a Series such that an infinitely big area can be filled
-     * between the newly created and the given series.
-     */
-    public static XYSeries createCeilingAtInfinity(XYSeries series) {
-        XYSeries ground = new XYSeries(series.getKey() + /** TODO rand + */ "INF");
-        ground.add(series.getMinX(), BIG_DOUBLE_VALUE);
-        ground.add(series.getMaxX(), BIG_DOUBLE_VALUE);
-        return ground;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/TimeseriesChartGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,892 +0,0 @@
-package de.intevation.flys.exports;
-
-import de.intevation.artifactdatabase.state.ArtifactAndFacet;
-import de.intevation.flys.artifacts.resources.Resources;
-import de.intevation.flys.jfree.Bounds;
-import de.intevation.flys.jfree.CollisionFreeXYTextAnnotation;
-import de.intevation.flys.jfree.DoubleBounds;
-import de.intevation.flys.jfree.FLYSAnnotation;
-import de.intevation.flys.jfree.StyledTimeSeries;
-import de.intevation.flys.jfree.TimeBounds;
-
-import java.awt.Color;
-import java.awt.Font;
-import java.text.DateFormat;
-import java.text.ParseException;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-import javax.swing.ImageIcon;
-
-import org.apache.log4j.Logger;
-import org.jfree.chart.ChartFactory;
-import org.jfree.chart.JFreeChart;
-import org.jfree.chart.LegendItem;
-import org.jfree.chart.LegendItemCollection;
-import org.jfree.chart.annotations.XYAnnotation;
-import org.jfree.chart.annotations.XYImageAnnotation;
-import org.jfree.chart.annotations.XYTextAnnotation;
-import org.jfree.chart.axis.ValueAxis;
-import org.jfree.chart.plot.Marker;
-import org.jfree.chart.plot.XYPlot;
-import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
-import org.jfree.data.Range;
-import org.jfree.data.general.Series;
-import org.jfree.data.time.Day;
-import org.jfree.data.time.TimeSeries;
-import org.jfree.data.time.TimeSeriesCollection;
-import org.jfree.data.xy.XYDataset;
-import org.jfree.ui.Layer;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.w3c.dom.Document;
-
-/**
- * @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
-
-    protected List<Marker> domainMarker;
-
-    protected List<Marker> valueMarker;
-
-    protected Map<String, String> attributes;
-
-    protected boolean domainZeroLineVisible;
-
-    private static final Logger logger =
-        Logger.getLogger(TimeseriesChartGenerator.class);
-
-    public static final int AXIS_SPACE = 5;
-
-    protected Map<Integer, Bounds> xBounds;
-
-    protected Map<Integer, Bounds> yBounds;
-
-
-    /**
-     * The default constructor that initializes internal datastructures.
-     */
-    public TimeseriesChartGenerator() {
-        super();
-
-        xBounds = new HashMap<Integer, Bounds>();
-        yBounds = new HashMap<Integer, Bounds>();
-        domainMarker = new ArrayList<Marker>();
-        valueMarker = new ArrayList<Marker>();
-        attributes = new HashMap<String, String>();
-    }
-
-
-
-    @Override
-    public JFreeChart generateChart() {
-        logger.info("Generate Timeseries Chart.");
-
-        JFreeChart chart = ChartFactory.createTimeSeriesChart(
-            getChartTitle(),
-            getXAxisLabel(),
-            getYAxisLabel(0),
-            null,
-            isLegendVisible(),
-            false,
-            false);
-
-        XYPlot plot = (XYPlot) chart.getPlot();
-
-        chart.setBackgroundPaint(Color.WHITE);
-        plot.setBackgroundPaint(Color.WHITE);
-
-        addSubtitles(chart);
-        adjustPlot(plot);
-        addDatasets(plot);
-        adjustAxes(plot);
-        addDomainAxisMarker(plot);
-        addValueAxisMarker(plot);
-        adaptZoom(plot);
-
-        applySeriesAttributes(plot);
-
-        addAnnotationsToRenderer(plot);
-        addLogo(plot);
-        aggregateLegendEntries(plot);
-        return chart;
-    }
-
-
-    /**
-     * Return left most data points x value (on first axis).
-     * Shortcut, especially to be overridden in (LS) charts where
-     * axis could be inverted.
-     */
-    protected double getLeftX() {
-        return (Long)getXBounds(0).getLower();
-    }
-
-
-    /**
-     * Return right most data points x value (on first axis).
-     * Shortcut, especially to be overridden in (LS) charts where
-     * axis could be inverted.
-     */
-    protected double getRightX() {
-        return (Long)getXBounds(0).getUpper();
-    }
-
-
-    /**
-     * Add a logo as background annotation to plot.
-     * Copy from XYChartGenerator.
-     */
-    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:
-         XYChartGenerator.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 = XYChartGenerator.class.getResource("/images/intevation.png");
-        }
-        else { // TODO else if ...
-            imageURL = XYChartGenerator.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 = ((Long)getXBounds(0).getUpper() + (Long)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);
-    }
-
-
-    @Override
-    protected Series getSeriesOf(XYDataset dataset, int idx) {
-        return ((TimeSeriesCollection) dataset).getSeries(idx);
-    }
-
-
-    /**
-     * This method creates new instances of TimeseriesAxisDataset.
-     *
-     * @param idx The symbol for the new TimeseriesAxisDataset.
-     */
-    @Override
-    protected AxisDataset createAxisDataset(int idx) {
-        logger.debug("Create a new AxisDataset for index: " + idx);
-        return new TimeseriesAxisDataset(idx);
-    }
-
-
-    @Override
-    protected void combineXBounds(Bounds bounds, int index) {
-        if (bounds != null) {
-            Bounds old = getXBounds(index);
-
-            if (old != null) {
-                bounds = bounds.combine(old);
-            }
-
-            setXBounds(index, bounds);
-        }
-    }
-
-
-    @Override
-    protected void combineYBounds(Bounds bounds, int index) {
-        if (bounds != null) {
-            Bounds old = getYBounds(index);
-
-            if (old != null) {
-                bounds = bounds.combine(old);
-            }
-
-            setYBounds(index, bounds);
-        }
-    }
-
-
-    // TODO REPLACE THIS METHOD WITH getBoundsForAxis(index)
-    @Override
-    public Range[] getRangesForAxis(int index) {
-        // TODO
-        Bounds[] bounds = getBoundsForAxis(index);
-
-        return new Range[] {
-            new Range(
-                bounds[0].getLower().doubleValue(),
-                bounds[0].getUpper().doubleValue()),
-            new Range(
-                bounds[1].getLower().doubleValue(),
-                bounds[1].getUpper().doubleValue())
-        };
-    }
-
-
-    @Override
-    public Bounds getXBounds(int axis) {
-        return xBounds.get(axis);
-    }
-
-
-    @Override
-    protected void setXBounds(int axis, Bounds bounds) {
-        xBounds.put(axis, bounds);
-    }
-
-
-    @Override
-    public Bounds getYBounds(int axis) {
-        return yBounds.get(axis);
-    }
-
-
-    @Override
-    protected void setYBounds(int axis, Bounds bounds) {
-        if (bounds != null) {
-            yBounds.put(axis, bounds);
-        }
-    }
-
-
-    public Bounds[] getBoundsForAxis(int index) {
-        logger.debug("Return x and y bounds for axis at: " + index);
-
-        Bounds rx = getXBounds(Integer.valueOf(index));
-        Bounds ry = getYBounds(Integer.valueOf(index));
-
-        if (rx == null) {
-            logger.warn("Range for x axis not set." +
-                        " Using default values: 0 - 1.");
-            rx = new TimeBounds(0l, 1l);
-        }
-
-        if (ry == null) {
-            logger.warn("Range for y axis not set." +
-                        " Using default values: 0 - 1.");
-            ry = new DoubleBounds(0l, 1l);
-        }
-
-        logger.debug("X Bounds at index " + index + " is: " + rx);
-        logger.debug("Y Bounds at index " + index + " is: " + ry);
-
-        return new Bounds[] {rx, ry};
-    }
-
-
-    /** Get (zoom)values from request. */
-    public Bounds getDomainAxisRange() {
-        String[] ranges = getDomainAxisRangeFromRequest();
-
-        if (ranges == null || ranges.length < 2) {
-            logger.debug("No zoom range for domain axis specified.");
-            return null;
-        }
-
-        if (ranges[0] == null || ranges[1] == null) {
-            logger.warn("Invalid ranges for domain axis specified!");
-            return null;
-        }
-
-        try {
-            double lower = Double.parseDouble(ranges[0]);
-            double upper = Double.parseDouble(ranges[1]);
-
-            return new DoubleBounds(lower, upper);
-        }
-        catch (NumberFormatException nfe) {
-            logger.warn("Invalid ranges for domain axis specified: " + nfe);
-        }
-
-        return null;
-    }
-
-
-    public Bounds getValueAxisRange() {
-        String[] ranges = getValueAxisRangeFromRequest();
-
-        if (ranges == null || ranges.length < 2) {
-            logger.debug("No zoom range for domain axis specified.");
-            return null;
-        }
-
-        if (ranges[0] == null || ranges[1] == null) {
-            logger.warn("Invalid ranges for domain axis specified!");
-            return null;
-        }
-
-        try {
-            double lower = Double.parseDouble(ranges[0]);
-            double upper = Double.parseDouble(ranges[1]);
-
-            return new DoubleBounds(lower, upper);
-        }
-        catch (NumberFormatException nfe) {
-            logger.warn("Invalid ranges for domain axis specified: " + nfe);
-        }
-
-        return null;
-    }
-
-
-    protected void adaptZoom(XYPlot plot) {
-        logger.debug("Adapt zoom of Timeseries chart.");
-
-        zoomX(plot, plot.getDomainAxis(), getXBounds(0), getDomainAxisRange());
-
-        Bounds valueAxisBounds = getValueAxisRange();
-
-        for (int j = 0, n = plot.getRangeAxisCount(); j < n; j++) {
-            zoomY(
-                plot,
-                plot.getRangeAxis(j),
-                getYBounds(j),
-                valueAxisBounds);
-        }
-    }
-
-
-    /**
-     * @param plot the plot.
-     * @param axis the value (x, time) axis of which to set bounds.
-     * @param total the current bounds (?).
-     */
-    protected void zoomX(
-        XYPlot    plot,
-        ValueAxis axis,
-        Bounds    total,//we could equally nicely getXBounds(0)
-        Bounds    user
-    ) {
-        if (logger.isDebugEnabled()) {
-            logger.debug("== Zoom X axis ==");
-            logger.debug("    Total axis range  : " + total);
-            logger.debug("    User defined range: " + user);
-        }
-
-        if (user != null) {
-            long min  = total.getLower().longValue();
-            long max  = total.getUpper().longValue();
-            long diff = max > min ? max - min : min - max;
-
-            long newMin = Math.round(min + user.getLower().doubleValue() * diff);
-            long newMax = Math.round(min + user.getUpper().doubleValue() * diff);
-
-            TimeBounds newBounds = new TimeBounds(newMin, newMax);
-
-            logger.debug("    Zoom axis to: " + newBounds);
-
-            newBounds.applyBounds(axis, AXIS_SPACE);
-        }
-        else {
-            logger.debug("No user specified zoom values found!");
-            if (total != null && axis != null) {
-                total.applyBounds(axis, AXIS_SPACE);
-            }
-        }
-    }
-
-
-    /**
-     * @param user zoom values in percent.
-     */
-    protected void zoomY(
-        XYPlot    plot,
-        ValueAxis axis,
-        Bounds    total,
-        Bounds    user
-    ) {
-        if (logger.isDebugEnabled()) {
-            logger.debug("== Zoom Y axis ==");
-            logger.debug("    Total axis range  : " + total);
-            logger.debug("    User defined range: " + user);
-        }
-
-        if (user != null) {
-            double min  = total.getLower().doubleValue();
-            double max  = total.getUpper().doubleValue();
-            double diff = max > min ? max - min : min - max;
-
-            double newMin = min + user.getLower().doubleValue() * diff;
-            double newMax = min + user.getUpper().doubleValue() * diff;
-
-            DoubleBounds newBounds = new DoubleBounds(newMin, newMax);
-
-            logger.debug("    Zoom axis to: " + newBounds);
-
-            newBounds.applyBounds(axis, AXIS_SPACE);
-        }
-        else {
-            logger.debug("No user specified zoom values found!");
-            if (total != null && axis != null) {
-                total.applyBounds(axis, AXIS_SPACE);
-            }
-        }
-    }
-
-
-    /**
-     * Adjusts the axes of a plot. This method sets the <i>labelFont</i> of the
-     * X axis.
-     *
-     * (Duplicate in XYChartGenerator).
-     *
-     * @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);
-    }
-
-
-    protected Date decodeXAxisValue(JSONArray array) throws JSONException, ParseException {
-        try {
-            double x = array.getDouble(0);
-            long l = (new Double(x)).longValue();
-            return new Date(l);
-        }
-        catch(JSONException ex) {
-            String str = array.getString(0);
-            DateFormat df = DateFormat.getDateInstance(
-                    DateFormat.MEDIUM, Resources.getLocale(context.getMeta()));
-            return df.parse(str);
-        }
-    }
-
-    /**
-     * Do Points out.
-     */
-    protected void doPoints(
-        Object     o,
-        ArtifactAndFacet aandf,
-        Document   theme,
-        boolean    visible,
-        int        axisIndex
-    ) {
-        String seriesName = aandf.getFacetDescription();
-        TimeSeries series = new StyledTimeSeries(seriesName, theme);
-
-        // Add text annotations for single points.
-        List<XYTextAnnotation> xy = new ArrayList<XYTextAnnotation>();
-        HashMap<Day, String> names = new HashMap<Day, String>();
-
-        try {
-            JSONArray points = new JSONArray((String) o);
-            for (int i = 0, P = points.length(); i < P; i++) {
-                JSONArray array = points.getJSONArray(i);
-
-                double y    = array.getDouble(1);
-                String name = array.getString(2);
-                boolean act = array.getBoolean(3);
-                if (!act) {
-                    continue;
-                }
-
-                Date date = decodeXAxisValue(array);
-
-                Day day = new Day(date);
-                series.add(day, y, false);
-                names.put(day, name);
-            }
-        }
-        catch(JSONException ex) {
-            logger.error("Could not decode json");
-        }
-        catch(ParseException ex) {
-            logger.error("Could not parse date string");
-        }
-
-        TimeSeriesCollection tsc = new TimeSeriesCollection();
-        tsc.addSeries(series);
-        // Add Annotations.
-        for (int i = 0, S = series.getItemCount(); i < S; i++) {
-            double x = tsc.getXValue(0, i);
-            double y = tsc.getYValue(0, i);
-            xy.add(new CollisionFreeXYTextAnnotation(
-                       names.get(series.getTimePeriod(i)), x, y));
-            logger.debug("doPoints(): x=" + x + " y=" + y);
-        }
-        FLYSAnnotation annotations =
-            new FLYSAnnotation(null, null, null, theme);
-        annotations.setTextAnnotations(xy);
-
-        // Do not generate second legend entry. (null was passed for the aand before).
-        doAnnotations(annotations, null, theme, visible);
-
-        addAxisDataset(tsc, axisIndex, visible);
-    }
-
-    public void addDomainAxisMarker(XYPlot plot) {
-        logger.debug("domainmarkers: " + domainMarker.size());
-        for (Marker marker: domainMarker) {
-            logger.debug("adding domain marker");
-            plot.addDomainMarker(marker, Layer.BACKGROUND);
-        }
-        domainMarker.clear();
-    }
-
-    public void addValueAxisMarker(XYPlot plot) {
-        for (Marker marker: valueMarker) {
-            logger.debug("adding value marker..");
-            plot.addRangeMarker(marker, Layer.BACKGROUND);
-        }
-        valueMarker.clear();
-    }
-
-    public void addAttribute(String seriesKey, String name) {
-        attributes.put(seriesKey, name);
-    }
-
-    private LegendItem getLegendItemFor(XYPlot plot, String interSeriesKey) {
-        LegendItemCollection litems = plot.getLegendItems();
-        Iterator<LegendItem> iter = litems.iterator();
-        while(iter.hasNext()) {
-            LegendItem item = iter.next();
-            if(interSeriesKey.startsWith(item.getSeriesKey().toString())) {
-                return item;
-            }
-        }
-        return null;
-    }
-
-    protected void applySeriesAttributes(XYPlot plot) {
-        int count  = plot.getDatasetCount();
-        for (int i = 0; i < count; i++) {
-            XYDataset data = plot.getDataset(i);
-            if (data == null) {
-                continue;
-            }
-
-            int seriesCount = data.getSeriesCount();
-            for (int j = 0; j < seriesCount; j++) {
-                StyledTimeSeries series =
-                    (StyledTimeSeries)getSeriesOf(data, j);
-                String key = series.getKey().toString();
-
-                if (attributes.containsKey(key)) {
-                    // Interpolated points are drawn unfilled
-                    if (attributes.get(key).equals("interpolate")) {
-                        XYLineAndShapeRenderer renderer =
-                                series.getStyle().getRenderer();
-                        renderer.setSeriesPaint(
-                            j,
-                            renderer.getSeriesFillPaint(j));
-                        renderer.setSeriesShapesFilled(j, false);
-
-                        LegendItem legendItem = getLegendItemFor(plot, key);
-                        if(legendItem != null) {
-                            LegendItem interLegend = new LegendItem(
-                                    legendItem.getLabel(),
-                                    legendItem.getDescription(),
-                                    legendItem.getToolTipText(),
-                                    legendItem.getURLText(),
-                                    legendItem.isShapeVisible(),
-                                    legendItem.getShape(),
-                                    false, // shapeFilled?
-                                    legendItem.getFillPaint(),
-                                    true,  // shapeOutlineVisible?
-                                    renderer.getSeriesFillPaint(j),
-                                    legendItem.getOutlineStroke(),
-                                    legendItem.isLineVisible(),
-                                    legendItem.getLine(),
-                                    legendItem.getLineStroke(),
-                                    legendItem.getLinePaint()
-                                    );
-                            interLegend.setSeriesKey(series.getKey());
-                            logger.debug("applySeriesAttributes: draw unfilled legend item");
-                            plot.getLegendItems().add(interLegend);
-                        }
-                    }
-                }
-
-                if (attributes.containsKey(key)) {
-                    if(attributes.get(key).equals("outline")) {
-                        XYLineAndShapeRenderer renderer =
-                            series.getStyle().getRenderer();
-                        renderer.setSeriesPaint(
-                            j,
-                            renderer.getSeriesFillPaint(j));
-                        renderer.setDrawOutlines(true);
-                    }
-                }
-            }
-        }
-    }
-
-    /** Two Ranges that span a rectangular area. */
-    public static class Area {
-        protected Range xRange;
-        protected Range yRange;
-
-        public Area(Range rangeX, Range rangeY) {
-            this.xRange = rangeX;
-            this.yRange = rangeY;
-        }
-
-        public Area(ValueAxis axisX, ValueAxis axisY) {
-            this.xRange = axisX.getRange();
-            this.yRange = axisY.getRange();
-        }
-
-        public double ofLeft(double percent) {
-            return xRange.getLowerBound()
-                + xRange.getLength() * percent;
-        }
-
-        public double ofRight(double percent) {
-            return xRange.getUpperBound()
-                - xRange.getLength() * percent;
-        }
-
-        public double ofGround(double percent) {
-            return yRange.getLowerBound()
-                + yRange.getLength() * percent;
-        }
-
-        public double atTop() {
-            return yRange.getUpperBound();
-        }
-
-        public double atGround() {
-            return yRange.getLowerBound();
-        }
-
-        public double atRight() {
-            return xRange.getUpperBound();
-        }
-
-        public double atLeft() {
-            return xRange.getLowerBound();
-        }
-
-        public double above(double percent, double base) {
-            return base + yRange.getLength() * percent;
-        }
-    }
-
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/TypeSection.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,136 +0,0 @@
-package de.intevation.flys.exports;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifactdatabase.state.Attribute;
-import de.intevation.artifactdatabase.state.DefaultSection;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class TypeSection extends DefaultSection {
-
-    private static final Logger logger = Logger.getLogger(TypeSection.class);
-
-    public TypeSection(String key) {
-        super(key);
-    }
-
-
-    /** Set a string value for a attribute with additional (choice) type. */
-    public void setChoiceStringValue(String key, String value, String choiceType) {
-        if (value == null || value.length() == 0) {
-            return;
-        }
-
-        Attribute attr = getAttribute(key);
-        if (attr == null) {
-            attr = new ChoiceStringAttribute(key, value, true, choiceType);
-            addAttribute(key, attr);
-        }
-        else {
-            attr.setValue(value);
-        }
-    }
-
-
-    public void setStringValue(String key, String value) {
-        if (value == null || value.length() == 0) {
-            return;
-        }
-
-        Attribute attr = getAttribute(key);
-        if (attr == null) {
-            attr = new StringAttribute(key, value, true);
-            addAttribute(key, attr);
-        }
-        else {
-            attr.setValue(value);
-        }
-    }
-
-
-    public String getStringValue(String key) {
-        Attribute attr = getAttribute(key);
-
-        if (attr instanceof StringAttribute) {
-            return (String) attr.getValue();
-        }
-
-        logger.debug("attribute " + key + " not found in typesection.getString");
-
-        return null;
-    }
-
-
-    public void setIntegerValue(String key, int value) {
-        Attribute attr = getAttribute(key);
-        if (attr == null) {
-            attr = new IntegerAttribute(key, value, true);
-            addAttribute(key, attr);
-        }
-        else {
-            attr.setValue(value);
-        }
-    }
-
-
-    public Integer getIntegerValue(String key) {
-        Attribute attr = getAttribute(key);
-
-        if (attr instanceof IntegerAttribute) {
-            return (Integer) attr.getValue();
-        }
-
-        return null;
-    }
-
-
-
-    public void setDoubleValue(String key, double value) {
-        Attribute attr = getAttribute(key);
-        if (attr == null) {
-            attr = new DoubleAttribute(key, value, true);
-            addAttribute(key, attr);
-        }
-        else {
-            attr.setValue(value);
-        }
-    }
-
-
-    public Double getDoubleValue(String key) {
-        Attribute attr = getAttribute(key);
-
-        if (attr instanceof DoubleAttribute) {
-            return (Double) attr.getValue();
-        }
-
-        return null;
-    }
-
-
-    public void setBooleanValue(String key, boolean value) {
-        Attribute attr = getAttribute(key);
-        if (attr == null) {
-            attr = new BooleanAttribute(key, value, true);
-            addAttribute(key, attr);
-        }
-        else {
-            attr.setValue(value);
-        }
-    }
-
-
-    public Boolean getBooleanValue(String key) {
-        Attribute attr = getAttribute(key);
-
-        if (attr instanceof BooleanAttribute) {
-            return (Boolean) attr.getValue();
-        }
-
-        return null;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/VisibleAttribute.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-package de.intevation.flys.exports;
-
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-
-import de.intevation.artifactdatabase.state.DefaultAttribute;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class VisibleAttribute extends DefaultAttribute {
-
-    protected boolean visible;
-
-
-    public VisibleAttribute(String name, Object value, boolean visible) {
-        super(name, value);
-        this.visible = visible;
-    }
-
-
-    /**
-     * This implementation of Attribute calls DefaultAttribute.toXML() first.
-     * After this, a new Attr <i>display</i> is added to the resulting Node.
-     *
-     * @param parent The parent Node.
-     *
-     * @return a new Node that represents this Attribute.
-     */
-    @Override
-    public Node toXML(Node parent) {
-        Element ele = (Element) super.toXML(parent);
-        ele.setAttribute("display", String.valueOf(visible));
-
-        return ele;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,109 +0,0 @@
-package de.intevation.flys.exports;
-
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.WKms;
-
-import org.jfree.chart.JFreeChart;
-import org.jfree.chart.plot.XYPlot;
-
-
-/**
- * An OutGenerator that generates w differences curves.
- */
-public class WDifferencesCurveGenerator
-extends      LongitudinalSectionGenerator
-implements   FacetTypes
-{
-    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;
-    }
-
-
-    /**
-     *
-     */
-    @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/flys-artifacts/src/main/java/de/intevation/flys/exports/WDifferencesCurveInfoGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@
-package de.intevation.flys.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/flys-artifacts/src/main/java/de/intevation/flys/exports/WDifferencesExporter.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,258 +0,0 @@
-package de.intevation.flys.exports;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.text.NumberFormat;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.HashMap;
-import java.util.Date;
-import java.text.DateFormat;
-import java.util.Locale;
-
-import org.w3c.dom.Document;
-
-import org.apache.log4j.Logger;
-
-import au.com.bytecode.opencsv.CSVWriter;
-
-import net.sf.jasperreports.engine.JasperExportManager;
-import net.sf.jasperreports.engine.JasperFillManager;
-import net.sf.jasperreports.engine.JasperPrint;
-import net.sf.jasperreports.engine.JRException;
-
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.common.utils.Config;
-
-import de.intevation.flys.artifacts.WINFOArtifact;
-
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.WKms;
-import de.intevation.flys.artifacts.model.WKmsJRDataSource;
-import de.intevation.flys.artifacts.resources.Resources;
-
-import de.intevation.flys.utils.FLYSUtils;
-import de.intevation.flys.utils.Formatter;
-
-/**
- * (CSV)Exporter for WDifferences.
- */
-public class WDifferencesExporter extends AbstractExporter {
-
-    /** The logger used in this exporter. */
-    private static Logger logger = Logger.getLogger(WDifferencesExporter.class);
-
-
-    public static final String WDIFF_CSV_KM_HEADER =
-        "export.w_differences.csv.header.km";
-
-    public static final String WDIFF_CSV_W_HEADER =
-        "export.w_differences.csv.header.w";
-
-    public static final String WDIFF_DEFAULT_CSV_KM_HEADER = "Fluss-Km";
-    public static final String WDIFF_DEFAULT_CSV_W_HEADER  = "m";
-
-    public static final String PDF_HEADER_MODE = "export.wdifferences.pdf.mode";
-    public static final String JASPER_FILE = "export.wdifferences.pdf.file";
-
-    /** 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[]>();
-    }
-
-
-    /**
-     * Genereate data in csv format.
-     */
-    @Override
-    public void generate()
-    throws IOException
-    {
-        logger.debug("WDifferencesExporter.generate");
-
-        if (facet == null) {
-            throw new IOException("invalid (null) facet for exporter");
-        }
-        else if (facet.equals(AbstractExporter.FACET_CSV)) {
-            generateCSV();
-        }
-        else if (facet.equals(AbstractExporter.FACET_PDF)) {
-            generatePDF();
-        }
-        else {
-            throw new IOException("invalid facet (" + facet + ") for exporter");
-        }
-    }
-
-
-    /**
-     * Adds given data.
-     * @param d either a WKms or a CalculationResult to add to data.
-     */
-    @Override
-    protected void addData(Object d) {
-        if (d instanceof CalculationResult) {
-            d = ((CalculationResult)d).getData();
-            if (d instanceof WKms []) {
-                data.add((WKms [])d);
-            }
-        }
-        else if (d instanceof WKms) {
-            data.add(new WKms[] { (WKms) d });
-        }
-    }
-
-
-    /**
-     * Lets writer write all data (including header).
-     * @param writer Writer to write data with.
-     */
-    @Override
-    protected void writeCSVData(CSVWriter writer) {
-        logger.info("WDifferencesExporter.writeData");
-
-        writeCSVHeader(writer);
-
-        for (WKms[] tmp: data) {
-            for (WKms wkms: tmp) {
-                wKms2CSV(writer, wkms);
-            }
-        }
-    }
-
-
-    /**
-     * Lets csvwriter write the header (first line in file).
-     * @param writer Writer to write header with.
-     */
-    protected void writeCSVHeader(CSVWriter writer) {
-        logger.info("WDifferencesExporter.writeCSVHeader");
-
-        writer.writeNext(new String[] {
-            msg(WDIFF_CSV_KM_HEADER, WDIFF_DEFAULT_CSV_KM_HEADER),
-            msg(WDIFF_CSV_W_HEADER, WDIFF_DEFAULT_CSV_W_HEADER)
-        });
-    }
-
-
-    protected void wKms2CSV(CSVWriter writer, WKms wkms) {
-        logger.debug("WDifferencesExporter.wKms2CSV");
-
-        NumberFormat kmf  = getKmFormatter();
-        NumberFormat wf   = getWFormatter();
-        int          size = wkms.size();
-
-        for (int i = 0; i < size; i ++) {
-
-            writer.writeNext(new String[] {
-                kmf.format(wkms.getKm(i)),
-                wf.format(wkms.getW(i))
-            });
-        }
-    }
-
-
-    /**
-     * Returns the number formatter for kilometer values.
-     *
-     * @return the number formatter for kilometer values.
-     */
-    protected NumberFormat getKmFormatter() {
-        return Formatter.getWaterlevelKM(context);
-    }
-
-
-    /**
-     * Returns the number formatter for W values.
-     *
-     * @return the number formatter for W values.
-     */
-    protected NumberFormat getWFormatter() {
-        return Formatter.getWaterlevelW(context);
-    }
-
-
-    @Override
-    protected void writePDF(OutputStream out) {
-        WKmsJRDataSource source = createJRData();
-
-        String jasperFile = Resources.getMsg(
-                                context.getMeta(),
-                                JASPER_FILE,
-                                "/jasper/wdifferences_en.jasper");
-        String confPath = Config.getConfigDirectory().toString();
-
-        Map parameters = new HashMap();
-        parameters.put("ReportTitle", "Exported Data");
-        try {
-            JasperPrint print = JasperFillManager.fillReport(
-                confPath + jasperFile,
-                parameters,
-                source);
-            JasperExportManager.exportReportToPdfStream(print, out);
-        }
-        catch(JRException je) {
-            logger.warn("Error generating PDF Report!");
-            je.printStackTrace();
-        }
-    }
-
-    protected WKmsJRDataSource createJRData() {
-        WKmsJRDataSource source = new WKmsJRDataSource();
-
-        addMetaData(source);
-        for (WKms[] tmp: data) {
-            for (WKms wkms: tmp) {
-                addWKmsData(source, wkms);
-            }
-        }
-        return source;
-    }
-
-
-    protected void addMetaData(WKmsJRDataSource source) {
-        CallMeta meta = context.getMeta();
-
-        WINFOArtifact flys = (WINFOArtifact) master;
-
-        source.addMetaData ("river", FLYSUtils.getRivername(flys));
-
-        Locale locale = Resources.getLocale(meta);
-        DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, locale);
-
-        source.addMetaData("date", df.format(new Date()));
-
-        String differences = FLYSUtils.getWDifferences(flys, context);
-        source.addMetaData("differences", differences);
-
-        source.addMetaData("calculation", Resources.getMsg(
-                                            locale,
-                                            PDF_HEADER_MODE,
-                                            "W Differences"));
-    }
-
-    protected void addWKmsData(WKmsJRDataSource source, WKms wkms) {
-        NumberFormat kmf  = getKmFormatter();
-        NumberFormat wf   = getWFormatter();
-        int          size = wkms.size();
-
-        for (int i = 0; i < size; i ++) {
-
-            source.addData(new String[] {
-                kmf.format(wkms.getKm(i)),
-                wf.format(wkms.getW(i))
-            });
-        }
-    }
-
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/WaterlevelExporter.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,854 +0,0 @@
-package de.intevation.flys.exports;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.text.DateFormat;
-import java.text.NumberFormat;
-import java.util.ArrayList;
-import java.util.Map;
-import java.util.HashMap;
-import java.util.Date;
-import java.util.List;
-import java.util.Locale;
-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;
-
-import de.intevation.flys.artifacts.model.ConstantWQKms;
-
-import net.sf.jasperreports.engine.JasperExportManager;
-import net.sf.jasperreports.engine.JasperFillManager;
-import net.sf.jasperreports.engine.JasperPrint;
-import net.sf.jasperreports.engine.JRException;
-
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.CallMeta;
-import de.intevation.artifacts.common.utils.Config;
-
-import de.intevation.flys.model.Gauge;
-
-import de.intevation.flys.artifacts.access.FixRealizingAccess;
-import de.intevation.flys.artifacts.access.RangeAccess;
-import de.intevation.flys.artifacts.FixationArtifact;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.WINFOArtifact;
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.Segment;
-import de.intevation.flys.artifacts.model.WQCKms;
-import de.intevation.flys.artifacts.model.WQKms;
-import de.intevation.flys.artifacts.model.WKmsJRDataSource;
-import de.intevation.flys.artifacts.model.WQKmsResult;
-import de.intevation.flys.artifacts.resources.Resources;
-
-import de.intevation.flys.utils.FLYSUtils;
-import de.intevation.flys.utils.FLYSUtils.WQ_MODE;
-import de.intevation.flys.utils.Formatter;
-
-/**
- * Generates different output formats (wst, csv, pdf) of data that resulted from
- * a waterlevel computation.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class WaterlevelExporter extends AbstractExporter {
-
-    /** The logger used in this exporter.*/
-    private static Logger logger = Logger.getLogger(WaterlevelExporter.class);
-
-    public static final String FACET_WST = "wst";
-
-    public static final String CSV_KM_HEADER =
-        "export.waterlevel.csv.header.km";
-
-    public static final String CSV_W_HEADER =
-        "export.waterlevel.csv.header.w";
-
-    public static final String CSV_Q_HEADER =
-        "export.waterlevel.csv.header.q";
-
-    public static final String CSV_Q_DESC_HEADER =
-        "export.waterlevel.csv.header.q.desc";
-
-    public static final String CSV_W_DESC_HEADER =
-        "export.waterlevel.csv.header.w.desc";
-
-    public static final String CSV_LOCATION_HEADER =
-        "export.waterlevel.csv.header.location";
-
-    public static final String CSV_GAUGE_HEADER =
-        "export.waterlevel.csv.header.gauge";
-
-    public static final String CSV_META_RESULT =
-        "export.waterlevel.csv.meta.result";
-
-    public static final String CSV_META_CREATION =
-        "export.waterlevel.csv.meta.creation";
-
-    public static final String CSV_META_CALCULATIONBASE =
-        "export.waterlevel.csv.meta.calculationbase";
-
-    public static final String CSV_META_RIVER =
-        "export.waterlevel.csv.meta.river";
-
-    public static final String CSV_META_RANGE =
-        "export.waterlevel.csv.meta.range";
-
-    public static final String CSV_META_GAUGE =
-        "export.waterlevel.csv.meta.gauge";
-
-    public static final String CSV_META_Q =
-        "export.waterlevel.csv.meta.q";
-
-    public static final String CSV_META_W =
-        "export.waterlevel.csv.meta.w";
-
-    public static final String CSV_NOT_IN_GAUGE_RANGE =
-        "export.waterlevel.csv.not.in.gauge.range";
-
-    public static final Pattern NUMBERS_PATTERN =
-        Pattern.compile("\\D*(\\d++.\\d*)\\D*");
-
-    public static final String DEFAULT_CSV_KM_HEADER       = "Fluss-Km";
-    public static final String DEFAULT_CSV_W_HEADER        = "W [NN + m]";
-    public static final String DEFAULT_CSV_Q_HEADER        = "Q [m\u00b3/s]";
-    public static final String DEFAULT_CSV_Q_DESC_HEADER   = "Bezeichnung";
-    public static final String DEFAULT_CSV_W_DESC_HEADER   = "W/Pegel [cm]";
-    public static final String DEFAULT_CSV_LOCATION_HEADER = "Lage";
-    public static final String DEFAULT_CSV_GAUGE_HEADER    = "Bezugspegel";
-    public static final String DEFAULT_CSV_NOT_IN_GAUGE_RANGE =
-        "außerhalb des gewählten Bezugspegels";
-
-    public static final String PDF_HEADER_MODE = "export.waterlevel.pdf.mode";
-    public static final String JASPER_FILE     = "export.waterlevel.pdf.file";
-
-    /** The storage that contains all WQKms objects for the different facets.*/
-    protected List<WQKms[]> data;
-
-
-    public void init(Document request, OutputStream out, CallContext context) {
-        logger.debug("WaterlevelExporter.init");
-
-        super.init(request, out, context);
-
-        this.data = new ArrayList<WQKms[]>();
-    }
-
-
-    @Override
-    public void generate()
-    throws IOException
-    {
-        logger.debug("WaterlevelExporter.generate");
-
-        if (facet != null && facet.equals(AbstractExporter.FACET_CSV)) {
-            generateCSV();
-        }
-        else if (facet != null && facet.equals(FACET_WST)) {
-            generateWST();
-        }
-        else if (facet != null && facet.equals(AbstractExporter.FACET_PDF)) {
-            generatePDF();
-        }
-        else {
-            throw new IOException("invalid facet for exporter");
-        }
-    }
-
-
-    @Override
-    protected void addData(Object d) {
-        if (d instanceof CalculationResult) {
-            d = ((CalculationResult)d).getData();
-            if (d instanceof WQKms []) {
-                data.add((WQKms [])d);
-            }
-            else if (d instanceof WQKmsResult) {
-                data.add(((WQKmsResult) d).getWQKms());
-            }
-        }
-    }
-
-
-    /**
-     * This method is used to prepare the column titles of waterlevel exports.
-     * Titles in this export include the Q value. If a Q value matches a named
-     * main value (as HQ100 or MNQ) this named main value should be used as
-     * title. This method resets the name of the <i>wqkms</i> object if such
-     * named main value fits to the chosen Q.
-     *
-     * @param winfo A WINFO Artifact.
-     * @param wqkms A WQKms object that should be prepared.
-     */
-    protected String getColumnTitle(WINFOArtifact winfo, WQKms wqkms) {
-        logger.debug("WaterlevelExporter.getColumnTitle");
-
-        String name = wqkms.getName();
-
-        logger.debug("Name of WQKms = '" + name + "'");
-
-        if (name.indexOf("W=") >= 0) {
-            return name;
-        }
-
-        Matcher m = NUMBERS_PATTERN.matcher(name);
-
-        if (m.matches()) {
-            String raw = m.group(1);
-
-            try {
-                double v = Double.valueOf(raw);
-
-                String nmv = FLYSUtils.getNamedMainValue(winfo, v);
-
-                if (nmv != null && nmv.length() > 0) {
-                    nmv  = FLYSUtils.stripNamedMainValue(nmv);
-                    nmv += "=" + String.valueOf(v);
-                    logger.debug("Set named main value '" + nmv + "'");
-
-                    return nmv;
-                }
-            }
-            catch (NumberFormatException nfe) {
-                // do nothing here
-            }
-        }
-
-        return name;
-    }
-
-
-    protected String getCSVRowTitle(WINFOArtifact winfo, WQKms wqkms) {
-        logger.debug("WaterlevelExporter.prepareNamedValue");
-
-        String name = wqkms.getName();
-
-        logger.debug("Name of WQKms = '" + name + "'");
-
-        WQ_MODE wqmode = FLYSUtils.getWQMode(winfo);
-
-        if (wqmode == WQ_MODE.WFREE || wqmode == WQ_MODE.QGAUGE) {
-            return localizeWQKms(winfo, wqkms);
-        }
-
-        Double v = wqkms.getRawValue();
-
-        String nmv = FLYSUtils.getNamedMainValue(winfo, v);
-
-        if (nmv != null && nmv.length() > 0) {
-            nmv = FLYSUtils.stripNamedMainValue(nmv);
-            logger.debug("Set named main value '" + nmv + "'");
-
-            return nmv;
-        }
-
-        return localizeWQKms(winfo, wqkms);
-    }
-
-
-    /**
-     * Get a string like 'W=' or 'Q=' with a number following in localized
-     * format.
-     */
-    protected String localizeWQKms(WINFOArtifact winfo, WQKms wqkms) {
-        WQ_MODE wqmode   = FLYSUtils.getWQMode(winfo);
-        Double  rawValue = wqkms.getRawValue();
-
-        if (rawValue == null) {
-            return wqkms.getName();
-        }
-
-        NumberFormat nf = Formatter.getRawFormatter(context);
-
-        if (wqmode == WQ_MODE.WFREE || wqmode == WQ_MODE.WGAUGE) {
-            return "W=" + nf.format(rawValue);
-        }
-        else {
-            return "Q=" + nf.format(rawValue);
-        }
-    }
-
-
-    @Override
-    protected void writeCSVData(CSVWriter writer) {
-        logger.info("WaterlevelExporter.writeData");
-
-        WQ_MODE mode    = FLYSUtils.getWQMode((FLYSArtifact)master);
-        boolean atGauge = mode == WQ_MODE.QGAUGE || mode == WQ_MODE.WGAUGE;
-        boolean isQ     = mode == WQ_MODE.QGAUGE || mode == WQ_MODE.QFREE;
-        FLYSUtils.WQ_INPUT input
-            = FLYSUtils.getWQInputMode((FLYSArtifact)master);
-
-        writeCSVMeta(writer);
-        writeCSVHeader(writer, atGauge, isQ);
-
-        for (WQKms[] tmp: data) {
-            for (WQKms wqkms: tmp) {
-                wQKms2CSV(writer, wqkms, atGauge, isQ);
-            }
-        }
-    }
-
-
-    protected void writeCSVMeta(CSVWriter writer) {
-        logger.info("WaterlevelExporter.writeCSVMeta");
-
-        // TODO use Access instead of FLYSUtils
-
-        CallMeta meta = context.getMeta();
-
-        FLYSArtifact flys = (FLYSArtifact) master;
-
-        writer.writeNext(new String[] {
-            Resources.getMsg(
-                meta,
-                CSV_META_RESULT,
-                CSV_META_RESULT,
-                new Object[] { FLYSUtils.getRivername(flys) })
-        });
-
-        Locale locale = Resources.getLocale(meta);
-        DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, locale);
-
-        writer.writeNext(new String[] {
-            Resources.getMsg(
-                meta,
-                CSV_META_CREATION,
-                CSV_META_CREATION,
-                new Object[] { df.format(new Date()) })
-        });
-
-        writer.writeNext(new String[] {
-            Resources.getMsg(
-                meta,
-                CSV_META_CALCULATIONBASE,
-                CSV_META_CALCULATIONBASE,
-                new Object[] { "" }) // TODO what is required at this place?
-        });
-
-        writer.writeNext(new String[] {
-            Resources.getMsg(
-                meta,
-                CSV_META_RIVER,
-                CSV_META_RIVER,
-                new Object[] { FLYSUtils.getRivername(flys) })
-        });
-
-        RangeAccess rangeAccess = new RangeAccess(flys, null);
-        double[] kms = rangeAccess.getKmRange();
-        writer.writeNext(new String[] {
-            Resources.getMsg(
-                meta,
-                CSV_META_RANGE,
-                CSV_META_RANGE,
-                new Object[] { kms[0], kms[kms.length-1] })
-        });
-
-        writer.writeNext(new String[] {
-            Resources.getMsg(
-                meta,
-                CSV_META_GAUGE,
-                CSV_META_GAUGE,
-                new Object[] { FLYSUtils.getGaugename(flys) })
-        });
-
-        FLYSUtils.WQ_MODE wq = FLYSUtils.getWQMode(flys);
-        if (wq == FLYSUtils.WQ_MODE.QFREE || wq == FLYSUtils.WQ_MODE.QGAUGE) {
-            double[] qs  = FLYSUtils.getQs(flys);
-            FLYSUtils.WQ_INPUT input = FLYSUtils.getWQInputMode(flys);
-
-            String data = "";
-
-            if ((input == FLYSUtils.WQ_INPUT.ADAPTED ||
-                input == FLYSUtils.WQ_INPUT.RANGE) &&
-                qs != null && qs.length > 0)
-            {
-                data = String.valueOf(qs[0]);
-                data += " - " + String.valueOf(qs[qs.length-1]);
-            }
-            else if (input == FLYSUtils.WQ_INPUT.SINGLE && qs != null){
-                data = String.valueOf(qs[0]);
-                for (int i = 1; i < qs.length; i++) {
-                    data += ", " + String.valueOf(qs[i]);
-                }
-            }
-            else {
-                logger.warn("Could not determine Q range!");
-            }
-
-            writer.writeNext(new String[] {
-                Resources.getMsg(
-                    meta,
-                    CSV_META_Q,
-                    CSV_META_Q,
-                    new Object[] {data})
-            });
-        }
-        else {
-            double[] ws = FLYSUtils.getWs(flys);
-
-            String lower = "";
-            String upper = "";
-
-            if (ws != null && ws.length > 0) {
-                lower = String.valueOf(ws[0]);
-                upper = String.valueOf(ws[ws.length-1]);
-            }
-            else {
-                logger.warn("Could not determine W range!");
-            }
-
-            writer.writeNext(new String[] {
-                Resources.getMsg(
-                    meta,
-                    CSV_META_W,
-                    CSV_META_W,
-                    new Object[] { lower, upper })
-            });
-        }
-
-        writer.writeNext(new String[] { "" });
-    }
-
-
-    /**
-     * Write the header, with different headings depending on whether at a
-     * gauge or at a location.
-     */
-    protected void writeCSVHeader(
-        CSVWriter writer,
-        boolean   atGauge,
-        boolean   isQ
-    ) {
-        logger.info("WaterlevelExporter.writeCSVHeader");
-
-        String unit = FLYSUtils.getRiver((FLYSArtifact) master).getWstUnit().getName();
-
-        if (atGauge) {
-            writer.writeNext(new String[] {
-                msg(CSV_KM_HEADER, DEFAULT_CSV_KM_HEADER),
-                msg(CSV_W_HEADER, DEFAULT_CSV_W_HEADER, new Object[] { unit }),
-                msg(CSV_Q_HEADER, DEFAULT_CSV_Q_HEADER),
-                (isQ
-                    ? msg(CSV_Q_DESC_HEADER, DEFAULT_CSV_Q_DESC_HEADER)
-                    : msg(CSV_W_DESC_HEADER, DEFAULT_CSV_W_DESC_HEADER)),
-                msg(CSV_LOCATION_HEADER, DEFAULT_CSV_LOCATION_HEADER),
-                msg(CSV_GAUGE_HEADER, DEFAULT_CSV_GAUGE_HEADER)
-            });
-        }
-        else {
-            writer.writeNext(new String[] {
-                msg(CSV_KM_HEADER, DEFAULT_CSV_KM_HEADER),
-                    // TODO flys/issue1128 (unit per river)
-                msg(CSV_W_HEADER, DEFAULT_CSV_W_HEADER, new Object[] { unit }),
-                msg(CSV_Q_HEADER, DEFAULT_CSV_Q_HEADER),
-                msg(CSV_LOCATION_HEADER, DEFAULT_CSV_LOCATION_HEADER)
-            });
-        }
-    }
-
-
-    /** Linearly search for gauge which is valid at km. */
-    private static Gauge findGauge(double km, List<Gauge> gauges) {
-        for (Gauge gauge: gauges) {
-            if (gauge.getRange().contains(km)) {
-                return gauge;
-            }
-        }
-        return null;
-    }
-
-    private static Segment findSegment(double km, List<Segment> segments) {
-        for (Segment segment: segments) {
-            if (segment.inside(km)) {
-                return segment;
-            }
-        }
-        return null;
-    }
-
-
-    private void writeRow4(CSVWriter writer, double wqkm[], FLYSArtifact flys) {
-        NumberFormat kmf = getKmFormatter();
-        NumberFormat wf  = getWFormatter();
-        NumberFormat qf  = getQFormatter();
-
-       writer.writeNext(new String[] {
-           kmf.format(wqkm[2]),
-           wf.format(wqkm[0]),
-           qf.format(wqkm[1]),
-           FLYSUtils.getLocationDescription(flys, wqkm[2])
-       });
-    }
-
-
-    /** Write an csv-row at gauge location. */
-    private void writeRow6(CSVWriter writer, double wqkm[], String wOrQDesc,
-        FLYSArtifact flys, String gaugeName) {
-        NumberFormat kmf = getKmFormatter();
-        NumberFormat wf  = getWFormatter();
-        NumberFormat qf  = getQFormatter();
-
-        writer.writeNext(new String[] {
-            kmf.format(wqkm[2]),
-            wf.format(wqkm[0]),
-            qf.format(wqkm[1]),
-            wOrQDesc,
-            FLYSUtils.getLocationDescription(flys, wqkm[2]),
-            gaugeName
-        });
-    }
-
-
-    /**
-     * Write "rows" of csv data from wqkms with writer.
-     */
-    protected void wQKms2CSV(
-        CSVWriter writer,
-        WQKms     wqkms,
-        boolean   atGauge,
-        boolean   isQ
-    ) {
-        logger.debug("WaterlevelExporter.wQKms2CSV");
-
-        // Skip constant data.
-        if (wqkms instanceof ConstantWQKms) {
-            return;
-        }
-
-        NumberFormat kmf = getKmFormatter();
-        NumberFormat wf  = getWFormatter();
-        NumberFormat qf  = getQFormatter();
-
-        int      size   = wqkms.size();
-        double[] result = new double[3];
-
-        FLYSArtifact flys       = (FLYSArtifact) master;
-        List<Gauge>  gauges     = FLYSUtils.getGauges(flys);
-        Gauge        gauge      = FLYSUtils.getGauge(flys);
-        String       gaugeName  = gauge.getName();
-        String       desc       = "";
-        String       notinrange = msg(
-            CSV_NOT_IN_GAUGE_RANGE,
-            DEFAULT_CSV_NOT_IN_GAUGE_RANGE);
-
-        double a = gauge.getRange().getA().doubleValue();
-        double b = gauge.getRange().getB().doubleValue();
-
-        if (flys instanceof WINFOArtifact && isQ) {
-            desc = getCSVRowTitle((WINFOArtifact)flys, wqkms);
-        }
-        else if (!isQ) {
-            Double value = FLYSUtils.getValueFromWQ(wqkms);
-            desc         = value != null
-                ? Formatter.getWaterlevelW(context).format(value) : null;
-        }
-
-        long startTime = System.currentTimeMillis();
-
-        String colDesc = desc;
-        List<Segment> segments = null;
-        boolean isFixRealize = false;
-        if (flys instanceof WINFOArtifact) {
-            if (wqkms != null && wqkms.getRawValue() != null) {
-                WINFOArtifact winfo = (WINFOArtifact) flys;
-                colDesc = FLYSUtils.getNamedMainValue(winfo, wqkms.getRawValue());
-            }
-        }
-        else if (flys instanceof FixationArtifact) {
-            // Get W/Q input per gauge for this case.
-            FixRealizingAccess fixAccess = new FixRealizingAccess(flys, getCallContext());
-            segments = fixAccess.getSegments();
-            if (segments != null && !segments.isEmpty()) {
-                isFixRealize = true;
-            }
-        }
-
-        if (atGauge) { // "At gauge" needs more output.
-
-            // Kms tend to be close together so caching the last sector
-            // is a good time saving heuristic.
-            Segment lastSegment = null;
-            Gauge   lastGauge   = null;
-
-            NumberFormat nf =
-                Formatter.getFormatter(context.getMeta(), 0, 0);
-
-            for (int i = 0; i < size; ++i) {
-                result = wqkms.get(i, result);
-                double km = result[2];
-
-                if (segments != null) {
-                    Segment found = lastSegment != null
-                                    && lastSegment.inside(km)
-                        ? lastSegment
-                        : findSegment(km, segments);
-
-                    if (found != null) {
-                        colDesc = nf.format(found.getValues()[0]);
-                    }
-                    lastSegment = found;
-                }
-
-                String gaugeN;
-                if (isFixRealize) {
-                    Gauge found = lastGauge != null
-                                  && lastGauge.getRange().contains(km)
-                        ? lastGauge
-                        : findGauge(km, gauges);
-
-                    gaugeN = found != null ? found.getName() : notinrange;
-                    lastGauge = found;
-                }
-                else {
-                    // TODO issue1114: Take correct gauge
-                    gaugeN = km >= a && km <= b
-                        ? gaugeName
-                        : notinrange;
-                }
-                writeRow6(writer, result, colDesc, flys, gaugeN);
-            }
-        }
-        else { // Not at gauge.
-            for (int i = 0; i < size; ++i) {
-                result = wqkms.get(i, result);
-                writeRow4(writer, result, flys);
-            }
-        }
-
-        long stopTime = System.currentTimeMillis();
-
-        if (logger.isDebugEnabled()) {
-            logger.debug("Writing CSV took " +
-                (float)(stopTime-startTime)/1000f + " secs.");
-        }
-    }
-
-
-    /**
-     * Generates the output in WST format.
-     */
-    protected void generateWST()
-    throws    IOException
-    {
-        logger.info("WaterlevelExporter.generateWST");
-
-        int cols = data.get(0).length;
-        WstWriter writer = new WstWriter(cols);
-
-        writeWSTData(writer);
-
-        writer.write(out);
-    }
-
-
-    protected void writeWSTData(WstWriter writer) {
-        logger.debug("WaterlevelExporter.writeWSTData");
-
-        double[] result = new double[4];
-
-        for (WQKms[] tmp: data) {
-            for (WQKms wqkms: tmp) {
-                if (wqkms instanceof ConstantWQKms) {
-                    continue;
-                }
-                int size = wqkms != null ? wqkms.size() : 0;
-
-                addWSTColumn(writer, wqkms);
-
-                for (int i = 0; i < size; i++) {
-                    result = wqkms.get(i, result);
-
-                    writer.add(result);
-                }
-
-                if (wqkms instanceof WQCKms) {
-                    addWSTColumn(writer, wqkms);
-
-                    for (int c = 0; c < size; c++) {
-                        result = wqkms.get(c, result);
-
-                        writer.addCorrected(result);
-                    }
-                }
-            }
-        }
-    }
-
-
-    /**
-     * This method is used to register a new column at <i>writer</i>. The name /
-     * title of the column depends on the Q or W value of <i>wqkms</i>. If a Q
-     * was selected and the Q fits to a named main value, the title is set to
-     * the named main value. Otherwise, the name returned by
-     * <i>WQKms.getName()</i> is set.
-     *
-     * @param writer The WstWriter.
-     * @param wqkms The new WST column.
-     */
-    protected void addWSTColumn(WstWriter writer, WQKms wqkms) {
-        if (wqkms instanceof ConstantWQKms) {
-            return;
-        }
-        if (master instanceof WINFOArtifact) {
-            writer.addColumn(getColumnTitle((WINFOArtifact) master, wqkms));
-        }
-        else {
-            writer.addColumn(wqkms.getName());
-        }
-    }
-
-
-    /**
-     *
-     */
-    @Override
-    protected void writePDF(OutputStream out) {
-        logger.debug("write PDF");
-        WKmsJRDataSource source = createJRData();
-
-        String jasperFile = Resources.getMsg(
-                                context.getMeta(),
-                                JASPER_FILE,
-                                "/jasper/waterlevel_en.jasper");
-        String confPath = Config.getConfigDirectory().toString();
-
-
-        Map parameters = new HashMap();
-        parameters.put("ReportTitle", "Exported Data");
-        try {
-            JasperPrint print = JasperFillManager.fillReport(
-                confPath + jasperFile,
-                parameters,
-                source);
-            JasperExportManager.exportReportToPdfStream(print, out);
-        }
-        catch(JRException je) {
-            logger.warn("Error generating PDF Report!", je);
-        }
-    }
-
-    protected WKmsJRDataSource createJRData() {
-        WKmsJRDataSource source = new WKmsJRDataSource();
-
-        WQ_MODE mode    = FLYSUtils.getWQMode((FLYSArtifact)master);
-        boolean atGauge = mode == WQ_MODE.QGAUGE || mode == WQ_MODE.WGAUGE;
-        boolean isQ     = mode == WQ_MODE.QGAUGE || mode == WQ_MODE.QFREE;
-
-        addMetaData(source);
-        for (WQKms[] tmp: data) {
-            for (WQKms wqkms: tmp) {
-                addWKmsData(wqkms, atGauge, isQ, source);
-            }
-        }
-        return source;
-    }
-
-    protected void addMetaData(WKmsJRDataSource source) {
-        CallMeta meta = context.getMeta();
-
-        FLYSArtifact flys = (FLYSArtifact) master;
-
-        source.addMetaData ("river", FLYSUtils.getRivername(flys));
-
-        Locale locale = Resources.getLocale(meta);
-        DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, locale);
-
-        source.addMetaData("date", df.format(new Date()));
-
-        RangeAccess rangeAccess = new RangeAccess(flys, null);
-        double[] kms = rangeAccess.getKmRange();
-        source.addMetaData("range", kms[0] + " - " + kms[kms.length-1]);
-
-        source.addMetaData("gauge", FLYSUtils.getGaugename(flys));
-
-        source.addMetaData("calculation", Resources.getMsg(
-                                            locale,
-                                            PDF_HEADER_MODE,
-                                            "Waterlevel"));
-    }
-
-    protected void addWKmsData(
-        WQKms wqkms,
-        boolean atGauge,
-        boolean isQ,
-        WKmsJRDataSource source)
-    {
-        logger.debug("WaterlevelExporter.addWKmsData");
-
-        // Skip constant data.
-        if (wqkms instanceof ConstantWQKms) {
-            return;
-        }
-
-        NumberFormat kmf = getKmFormatter();
-        NumberFormat wf  = getWFormatter();
-        NumberFormat qf  = getQFormatter();
-
-        int      size   = wqkms.size();
-        double[] result = new double[3];
-
-        FLYSArtifact flys       = (FLYSArtifact) master;
-        Gauge        gauge      = FLYSUtils.getGauge(flys);
-        String       gaugeName  = gauge.getName();
-        String       desc       = "";
-        String       notinrange = msg(
-            CSV_NOT_IN_GAUGE_RANGE,
-            DEFAULT_CSV_NOT_IN_GAUGE_RANGE);
-
-        double a = gauge.getRange().getA().doubleValue();
-        double b = gauge.getRange().getB().doubleValue();
-
-        if (flys instanceof WINFOArtifact && isQ) {
-            desc = getCSVRowTitle((WINFOArtifact)flys, wqkms);
-        }
-        else if (!isQ) {
-            Double value = FLYSUtils.getValueFromWQ(wqkms);
-            desc         = value != null
-                ? Formatter.getWaterlevelW(context).format(value) : null;
-        }
-
-        long startTime = System.currentTimeMillis();
-
-        for (int i = 0; i < size; i ++) {
-            result = wqkms.get(i, result);
-
-            if (atGauge) {
-                source.addData(new String[] {
-                    kmf.format(result[2]),
-                    wf.format(result[0]),
-                    qf.format(result[1]),
-                    desc,
-                    FLYSUtils.getLocationDescription(flys, result[2]),
-                    result[2] >= a && result[2] <= b
-                        ? gaugeName
-                        : notinrange
-                });
-            }
-            else {
-                source.addData(new String[] {
-                    kmf.format(result[2]),
-                    wf.format(result[0]),
-                    qf.format(result[1]),
-                    desc,
-                    FLYSUtils.getLocationDescription(flys, result[2]),
-                    result[2] >= a && result[2] <= b
-                        ? gaugeName
-                        : notinrange
-                });
-            }
-        }
-
-        long stopTime = System.currentTimeMillis();
-
-        if (logger.isDebugEnabled()) {
-            logger.debug("Writing PDF data took " +
-                (float)(stopTime-startTime)/1000f + " secs.");
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/WstWriter.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,234 +0,0 @@
-package de.intevation.flys.exports;
-
-import java.io.BufferedWriter;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.TreeMap;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.flys.artifacts.model.WstLine;
-
-
-/**
- * A writer that creates WSTs.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class WstWriter {
-
-    /** The logger used in this class. */
-    private static Logger logger = Logger.getLogger(WstWriter.class);
-
-    /** The default unit that is written into the header of the WST. */
-    public static final String DEFAULT_UNIT = "Wassserstand [NN + m]";
-
-    /** The lines that need to be included for the export. */
-    protected Map<Double, WstLine> lines;
-
-    /** The column names. */
-    protected List<String> columnNames;
-
-    /** The locale used to format the values. */
-    protected Locale locale;
-
-    /** The number of discharge columns. */
-    protected int cols;
-
-    /** The last Q values. */
-    protected double[] qs;
-
-
-
-    /**
-     * This constructor creates a new WstWriter with a number of Q columns.
-     *
-     * @param cols The number of columns of the resulting WST.
-     */
-    public WstWriter(int cols) {
-        this.columnNames = new ArrayList<String>(cols);
-        this.lines       = new HashMap<Double, WstLine>();
-        this.qs          = new double[cols];
-        this.locale      = Locale.US;
-    }
-
-
-    /**
-     * This method is used to create the WST from the data that has been
-     * inserted using add(double[]) before.
-     * @param out Where to write to.
-     */
-    public void write(OutputStream out) {
-        logger.info("WstWriter.write");
-
-        PrintWriter writer = new PrintWriter(
-            new BufferedWriter(
-                new OutputStreamWriter(out)));
-
-        this.qs = new double[cols];
-
-        writeHeader(writer);
-
-        Collection<WstLine> collection = new TreeMap(lines).values();
-
-        for (WstLine line: collection) {
-            writeWLine(writer, line);
-        }
-
-        writer.flush();
-        writer.close();
-    }
-
-
-    /**
-     * This method is used to add a new line to the WST.
-     *
-     * @param wqkms A 3dim double array with [W,Q, KM].
-     */
-    public void add(double[] wqkms) {
-        Double km = wqkms[2];
-
-        WstLine line = lines.get(km);
-
-        if (line == null) {
-            line = new WstLine(km.doubleValue());
-            lines.put(km, line);
-        }
-
-        line.add(wqkms[0], wqkms[1]);
-    }
-
-
-    public void addCorrected(double[] wqckms) {
-        Double km = wqckms[2];
-
-        WstLine line = lines.get(km);
-
-        if (line == null) {
-            line = new WstLine(km.doubleValue());
-            lines.put(km, line);
-        }
-
-        line.add(wqckms[3], wqckms[1]);
-    }
-
-
-    /**
-     * Adds a further column name.
-     *
-     * @param name The name of the new column.
-     */
-    public void addColumn(String name) {
-        if (name != null) {
-            cols++;
-
-            String basename = name;
-
-            int i = 0;
-            while (columnNames.contains(name)) {
-                name = basename + "_" + i++;
-
-                if (name.length() > 9) {
-                    name = name.substring(name.length() - 9);
-                }
-            }
-
-            columnNames.add(name);
-        }
-    }
-
-
-    /**
-     * This method writes the header of the WST.
-     *
-     * @param writer The PrintWriter that creates the output.
-     */
-    protected void writeHeader(PrintWriter writer) {
-        logger.debug("WstWriter.writeHeader");
-
-        writer.println(cols);
-        writer.print("        ");
-
-        for (String columnName: columnNames) {
-            writer.printf(locale, "%9s", columnName);
-        }
-
-        writer.println();
-
-        writer.write("*   KM     ");
-        writer.write(DEFAULT_UNIT);
-        writer.println();
-    }
-
-
-    /**
-     * This method writes a line with W values and a certain kilometer.
-     *
-     * @param writer The PrintWriter that is used to create the output.
-     * @param line The WstLine that should be written to the output.
-     */
-    protected void writeWLine(PrintWriter writer, WstLine line) {
-        double   km  = line.getKm();
-        double[] qs  = line.getQs();
-        int      num = line.getSize();
-
-        if (dischargesChanged(qs)) {
-            writeQLine(writer, qs);
-        }
-
-        writer.printf(locale, "%8.3f", km);
-
-        for (int i = 0; i < num; i++) {
-            writer.printf(locale, "%9.2f", line.getW(i));
-        }
-
-        writer.println();
-    }
-
-
-    /**
-     * Writes a discharge line (Q values) into a WST.
-     *
-     * @param qs the Q values for the next range.
-     */
-    protected void writeQLine(PrintWriter writer, double[] qs) {
-        writer.write("*\u001f      ");
-
-        for (int i = 0; i < qs.length; i++) {
-            this.qs[i] = qs[i];
-
-            writer.printf(locale, "%9.2f", qs[i]);
-        }
-
-        writer.println();
-    }
-
-
-    /**
-     * This method determines if a Q has changed from the last line to the
-     * current one.
-     *
-     * @param newQs The Q values of the next line.
-     *
-     * @return true, if a Q value have changed, otherwise false.
-     */
-    protected boolean dischargesChanged(double[] newQs) {
-        // XXX maybe there is a way to do this faster
-        for (int i = 0; i < cols; i++) {
-            if (Math.abs(newQs[i] - qs[i]) >= 0.001) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/XYChartGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1074 +0,0 @@
-package de.intevation.flys.exports;
-
-import java.awt.Color;
-import java.awt.Font;
-import java.text.NumberFormat;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.swing.ImageIcon;
-
-import org.apache.log4j.Logger;
-import org.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.annotations.XYTextAnnotation;
-import org.jfree.chart.axis.NumberAxis;
-import org.jfree.chart.axis.ValueAxis;
-import org.jfree.chart.axis.LogarithmicAxis;
-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.json.JSONArray;
-import org.json.JSONException;
-import org.w3c.dom.Document;
-
-import de.intevation.artifactdatabase.state.ArtifactAndFacet;
-import de.intevation.flys.jfree.Bounds;
-import de.intevation.flys.jfree.CollisionFreeXYTextAnnotation;
-import de.intevation.flys.jfree.DoubleBounds;
-import de.intevation.flys.jfree.FLYSAnnotation;
-import de.intevation.flys.jfree.StyledAreaSeriesCollection;
-import de.intevation.flys.jfree.StyledXYSeries;
-
-
-/**
- * An abstract base class for creating XY charts.
- *
- * 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>
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-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();
-
-    public static final int AXIS_SPACE = 5;
-
-    /** The logger that is used in this generator. */
-    private static Logger logger = Logger.getLogger(XYChartGenerator.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;
-
-    public XYChartGenerator() {
-        super();
-
-        xBounds  = new HashMap<Integer, Bounds>();
-        yBounds  = new HashMap<Integer, Bounds>();
-    }
-
-
-    /**
-     * Generate the chart anew (including localized axis and all).
-     */
-    @Override
-    public JFreeChart generateChart() {
-        logger.debug("XYChartGenerator.generateChart");
-
-        JFreeChart chart = ChartFactory.createXYLineChart(
-            getChartTitle(),
-            getXAxisLabel(),
-            getYAxisLabel(0),
-            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.
-        addAnnotationsToRenderer(plot);
-
-        // Add a logo (maybe).
-        addLogo(plot);
-
-        aggregateLegendEntries(plot);
-
-        return chart;
-    }
-
-
-    /**
-     * Return left most data points x value (on first axis).
-     * Shortcut, especially to be overridden in (LS) charts where
-     * axis could be inverted.
-     */
-    protected double getLeftX() {
-        return (Double)getXBounds(0).getLower();
-    }
-
-
-    /**
-     * Return right most data points x value (on first axis).
-     * Shortcut, especially to be overridden in (LS) charts where
-     * axis could be inverted.
-     */
-    protected double getRightX() {
-        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:
-         XYChartGenerator.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 = XYChartGenerator.class.getResource("/images/intevation.png");
-        }
-        else { // TODO else if ...
-            imageURL = XYChartGenerator.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 XYAxisDataset for index: " + idx);
-        return new XYAxisDataset(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 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;
-        }
-
-        XYAxisDataset axisDataset = (XYAxisDataset) 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  ('symbolic') index of the series and of its 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);
-
-        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);
-        }
-    }
-
-
-    /**
-     * 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.
-     *
-     * (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);
-    }
-
-
-    /**
-     * 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);
-    }
-
-
-    /**
-     * Do Points out.
-     */
-    protected void doPoints(
-        Object     o,
-        ArtifactAndFacet aandf,
-        Document   theme,
-        boolean    visible,
-        int        axisIndex
-    ) {
-        String seriesName = aandf.getFacetDescription();
-        XYSeries series = new StyledXYSeries(seriesName, theme);
-
-        // Add text annotations for single points.
-        List<XYTextAnnotation> xy = new ArrayList<XYTextAnnotation>();
-
-        try {
-            JSONArray points = new JSONArray((String) o);
-            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.");
-        }
-
-        FLYSAnnotation annotations = new FLYSAnnotation(null, null, null, theme);
-        annotations.setTextAnnotations(xy);
-
-        // Do not generate second legend entry. (null was passed for the aand before).
-        doAnnotations(annotations, null, theme, visible);
-        addAxisSeries(series, axisIndex, visible);
-    }
-
-
-    /**
-     * 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 has been inverted. */
-    public void setInverted(boolean inverted) {
-        this.inverted = inverted;
-    }
-
-
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/extreme/ExtremeWQCurveGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,194 +0,0 @@
-package de.intevation.flys.exports.extreme;
-
-import java.awt.Color;
-
-import org.apache.log4j.Logger;
-import org.jfree.chart.JFreeChart;
-import org.jfree.chart.plot.Marker;
-import org.jfree.chart.plot.ValueMarker;
-import org.jfree.chart.title.TextTitle;
-import org.jfree.data.xy.XYSeries;
-import org.w3c.dom.Document;
-
-import de.intevation.artifactdatabase.state.ArtifactAndFacet;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.flys.artifacts.access.FixAnalysisAccess;
-import de.intevation.flys.artifacts.model.DateRange;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.extreme.Curve;
-import de.intevation.flys.artifacts.model.extreme.ExtremeCurveFacet;
-import de.intevation.flys.artifacts.resources.Resources;
-import de.intevation.flys.exports.fixings.FixWQCurveGenerator;
-import de.intevation.flys.exports.StyledSeriesBuilder;
-import de.intevation.flys.jfree.JFreeUtil;
-import de.intevation.flys.jfree.StyledXYSeries;
-
-import de.intevation.flys.utils.ThemeUtil;
-
-
-/**
- * Generator for WQ fixing charts.
- * @author <a href="mailto:christian.lins@intevation.de">Christian Lins</a>
- */
-public class ExtremeWQCurveGenerator
-extends      FixWQCurveGenerator
-implements   FacetTypes
-{
-    /** Private logger. */
-    private static Logger logger =
-            Logger.getLogger(ExtremeWQCurveGenerator.class);
-
-    public static final String I18N_CHART_TITLE =
-            "chart.extreme.wq.title";
-
-    public static final String I18N_CHART_SUBTITLE =
-            "chart.extreme.wq.subtitle";
-
-    public static final String I18N_CHART_SUBTITLE1 =
-            "chart.extreme.wq.subtitle1";
-
-    public static final String I18N_XAXIS_LABEL =
-            "chart.extreme.wq.xaxis.label";
-
-    public static final String I18N_YAXIS_LABEL =
-            "chart.extreme.wq.yaxis.label";
-
-    public static final String I18N_CHART_TITLE_DEFAULT  =
-            "Extremkurvenanalyse";
-
-    public static final String I18N_XAXIS_LABEL_DEFAULT  =
-            "Q [m\u00B3/s]";
-
-    public static final String I18N_YAXIS_LABEL_DEFAULT  =
-            "W [NN + m]";
-
-
-    @Override
-    public boolean prepareChartData(ArtifactAndFacet aaf, Document theme, boolean visible) {
-        if (!super.prepareChartData(aaf, theme, visible)) {
-            String name = aaf.getFacetName();
-            if (name.equals(EXTREME_WQ_CURVE)) {
-                doExtremeCurveOut(aaf, theme, visible);
-                return true;
-            }
-            else if (name.equals(EXTREME_WQ_CURVE_BASE)) {
-                doExtremeCurveBaseOut(aaf, theme, visible);
-                return true;
-            }
-            return false;
-        }
-        return true;
-    }
-
-    /** Do Extreme Curve nonextrapolated points out. */
-    protected void doExtremeCurveBaseOut(ArtifactAndFacet aaf, Document theme, boolean visible) {
-        logger.debug("doExtremeCurveBaseOut");
-        ExtremeCurveFacet facet = (ExtremeCurveFacet) aaf.getFacet();
-        Curve curve = (Curve) facet.getData(aaf.getArtifact(), context);
-        if (curve == null) {
-            logger.warn("doExtremeCurveBaseOut: Facet does not contain Curve");
-            return;
-        }
-
-        XYSeries qwseries = new StyledXYSeries(aaf.getFacetDescription(), theme);
-        StyledSeriesBuilder.addPointsQW(qwseries, curve.getQs(), curve.getWs());
-
-        addAxisSeries(qwseries, YAXIS.W.idx, visible);
-    }
-
-
-    /** Do Extreme Curve out */
-    protected void doExtremeCurveOut(ArtifactAndFacet aaf, Document theme, boolean visible) {
-        logger.debug("doExtremeCurveOut");
-        ExtremeCurveFacet facet = (ExtremeCurveFacet) aaf.getFacet();
-        Curve curve = (Curve) facet.getData(aaf.getArtifact(), context);
-        if (curve == null) {
-            logger.warn("doExtremeCurveOut: Facet does not contain Curve");
-            return;
-        }
-
-        double maxQ = curve.getSuggestedMaxQ();
-        if (maxQ == Double.MAX_VALUE) {
-            maxQ = 8000;
-        }
-
-        StyledXYSeries series = JFreeUtil.sampleFunction2D(
-                curve,
-                theme,
-                aaf.getFacetDescription(),
-                500,   // number of samples
-                0.0 ,  // start
-                maxQ); // end
-
-        // Add marker from where on its extrapolated.
-        if (ThemeUtil.parseShowExtraMark(theme)) {
-            double[] qs = curve.getQs();
-            double extrapolateFrom = qs[qs.length-1];
-
-            Marker m = new ValueMarker(extrapolateFrom);
-            m.setPaint(Color.black);
-            addDomainMarker(m);
-        }
-
-        addAxisSeries(series, 0, visible);
-    }
-
-
-    @Override
-    protected String getChartTitle() {
-        return Resources.format(
-                context.getMeta(),
-                I18N_CHART_TITLE,
-                I18N_CHART_TITLE_DEFAULT,
-                context.getContextValue(CURRENT_KM));
-    }
-
-
-    @Override
-    protected String getDefaultChartTitle() {
-        return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT);
-    }
-
-    @Override
-    protected String getDefaultChartSubtitle() {
-        FixAnalysisAccess access = new FixAnalysisAccess(artifact, context);
-        DateRange dateRange = access.getDateRange();
-        DateRange refRange  = access.getReferencePeriod();
-
-        if (dateRange != null && refRange != null) {
-            return Resources.format(
-                    context.getMeta(),
-                    I18N_CHART_SUBTITLE,
-                    "",
-                    access.getRiver(),
-                    dateRange.getFrom(),
-                    dateRange.getTo(),
-                    refRange.getFrom(),
-                    refRange.getTo());
-        }
-
-        return null;
-    }
-
-    @Override
-    protected void addSubtitles(JFreeChart chart) {
-        String defaultSubtitle = getDefaultChartSubtitle();
-
-        if (defaultSubtitle == null || defaultSubtitle.length() == 0) {
-            return;
-        }
-
-        chart.addSubtitle(new TextTitle(defaultSubtitle));
-    }
-
-    @Override
-    protected String getDefaultXAxisLabel() {
-        return msg(I18N_XAXIS_LABEL, I18N_XAXIS_LABEL_DEFAULT);
-    }
-
-    @Override
-    protected String getDefaultYAxisLabel(int pos) {
-        return msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL_DEFAULT);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/extreme/ExtremeWQCurveInfoGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@
-package de.intevation.flys.exports.extreme;
-
-import de.intevation.flys.exports.ChartInfoGenerator;
-
-/**
- * A ChartInfoGenerator that generates meta information for specific extreme
- * analysis W/Q curves.
- */
-public class ExtremeWQCurveInfoGenerator extends ChartInfoGenerator {
-
-    public ExtremeWQCurveInfoGenerator() {
-        super(new ExtremeWQCurveGenerator());
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/fixings/DeltaWtExporter.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,291 +0,0 @@
-package de.intevation.flys.exports.fixings;
-
-import au.com.bytecode.opencsv.CSVWriter;
-
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.flys.artifacts.model.CalculationResult;
-
-import de.intevation.flys.artifacts.model.fixings.AnalysisPeriod;
-import de.intevation.flys.artifacts.model.fixings.FixAnalysisResult;
-import de.intevation.flys.artifacts.model.fixings.QWD;
-
-import de.intevation.flys.artifacts.resources.Resources;
-
-import de.intevation.flys.exports.AbstractExporter;
-
-import de.intevation.flys.utils.Formatter;
-import de.intevation.flys.utils.KMIndex;
-
-import java.io.IOException;
-import java.io.OutputStream;
-
-import java.text.DateFormat;
-import java.text.MessageFormat;
-import java.text.NumberFormat;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.TreeMap;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Document;
-
-/** Exports fixation analysis deltaw(t) computation results to csv. */
-public class DeltaWtExporter
-extends      AbstractExporter
-{
-    /** Private logger. */
-    private static Logger log = Logger.getLogger(DeltaWtExporter.class);
-
-    public static final String CSV_KM_HEADER =
-        "export.fixings.deltawt.csv.header.km";
-
-    public static final String CSV_DELTA_W_HEADER =
-        "export.fixings.deltawt.csv.header.deltaw";
-
-    public static final String CSV_Q_HEADER =
-        "export.fixings.deltawt.csv.header.q";
-
-    public static final String CSV_W_HEADER =
-        "export.fixings.deltawt.csv.header.w";
-
-    public static final String CSV_TRANGE_HEADER =
-        "export.fixings.deltawt.csv.header.time.range";
-
-    public static final String CSV_T_HEADER =
-        "export.fixings.deltawt.csv.header.t";
-
-    public static final String CSV_T_FORMAT =
-        "export.fixings.deltawt.csv.t.format";
-
-    public static final String DEFAULT_CSV_KM_HEADER = "km";
-
-    public static final String DEFAULT_CSV_DELTA_W_HEADER = "\u0394 W [cm]";
-
-    public static final String DEFAULT_CSV_W_HEADER = "Wasserstand [m]";
-
-    public static final String DEFAULT_CSV_Q_HEADER = "Abfluss [m\u00b3/s]";
-
-    public static final String DEFAULT_CSV_T_HEADER = "Datum";
-
-    public static final String DEFAULT_CSV_TRANGE_DESC_HEADER =
-        "Status";
-
-    public static final String CSV_REFERENCE =
-        "export.fixings.deltawt.csv.reference";
-
-    public static final String CSV_ANALYSIS =
-        "export.fixings.deltawt.csv.analysis";
-
-    public static final String DEFAULT_CSV_REFERENCE =
-        "B";
-
-    public static final String DEFAULT_CSV_ANALYSIS =
-        "A{0,number,integer}";
-
-    public static final String DEFAULT_CSV_T_FORMAT =
-        "dd.MM.yyyy";
-
-    protected List<KMIndex<AnalysisPeriod []>> analysisPeriods;
-
-    protected List<KMIndex<QWD[]>> referenceEvents;
-
-    @Override
-    public void init(Document request, OutputStream out, CallContext context) {
-        log.debug("DeltaWtExporter.init");
-        super.init(request, out, context);
-        analysisPeriods = new ArrayList<KMIndex<AnalysisPeriod []>>();
-        referenceEvents = new ArrayList<KMIndex<QWD[]>>();
-    }
-
-    @Override
-    protected void addData(Object d) {
-        log.debug("DeltaWtExporter.addData");
-        if (!(d instanceof CalculationResult)) {
-            log.warn("Invalid data type");
-            return;
-        }
-
-        Object data = ((CalculationResult)d).getData();
-        if (!(data instanceof FixAnalysisResult)) {
-            log.warn("Invalid data stored in result.");
-        }
-        FixAnalysisResult result = (FixAnalysisResult)data;
-        analysisPeriods.add(result.getAnalysisPeriods());
-        referenceEvents.add(result.getReferenced());
-    }
-
-    @Override
-    protected void writeCSVData(CSVWriter writer) throws IOException {
-
-        boolean debug = log.isDebugEnabled();
-
-        writeCSVHeader(writer);
-
-        NumberFormat kmF = getKMFormatter();
-        NumberFormat dwF = getDeltaWFormatter();
-        NumberFormat qF  = getQFormatter();
-        NumberFormat wF  = getWFormatter();
-
-        DateFormat dF = getDateFormatter();
-
-        TreeMap<Double, ArrayList<String []>> sorted =
-            new TreeMap<Double, ArrayList<String []>>();
-
-        String referenceS = getReference();
-
-        for (KMIndex<QWD[]> reference: referenceEvents) {
-
-            for (KMIndex.Entry<QWD[]> kmEntry: reference) {
-
-                Double km = kmEntry.getKm();
-
-                ArrayList<String []> list = sorted.get(km);
-
-                if (list == null) {
-                    list = new ArrayList<String []>();
-                    sorted.put(km, list);
-                }
-
-                String kmS = kmF.format(kmEntry.getKm());
-                for (QWD qwd: kmEntry.getValue()) {
-                    String deltaWS = dwF.format(qwd.getDeltaW());
-                    String qS      = qF.format(qwd.getQ());
-                    String wS      = wF.format(qwd.getW());
-                    String dateS   = dF.format(qwd.getDate());
-
-                    list.add(new String[] {
-                        kmS,
-                        dateS,
-                        qS,
-                        wS,
-                        referenceS,
-                        deltaWS
-                        });
-                }
-            }
-        }
-
-        if (debug) {
-            log.debug("AnalysisPeriods: " + analysisPeriods.size());
-        }
-
-        String analysisTemplate = getAnalysisTemplate();
-
-        for (KMIndex<AnalysisPeriod []> periods: analysisPeriods) {
-
-            for (KMIndex.Entry<AnalysisPeriod []> kmEntry: periods) {
-
-                Double km = kmEntry.getKm();
-
-                ArrayList<String []> list = sorted.get(km);
-
-                if (list == null) {
-                    list = new ArrayList<String []>();
-                    sorted.put(km, list);
-                }
-
-                String kmS = kmF.format(kmEntry.getKm());
-                int analysisCount = 1;
-
-                for (AnalysisPeriod period: kmEntry.getValue()) {
-                    // Typically resulting in A1,A2...
-                    String analyisS = MessageFormat.format(analysisTemplate,
-                        analysisCount);
-                    QWD [] qwds = period.getQWDs();
-
-                    if (qwds != null) {
-                        for (QWD qwd: qwds) {
-                            String deltaWS = dwF.format(qwd.getDeltaW());
-                            String qS      = qF.format(qwd.getQ());
-                            String wS      = wF.format(qwd.getW());
-                            String dateS   = dF.format(qwd.getDate());
-
-                            list.add(new String[] {
-                                kmS,
-                                dateS,
-                                qS,
-                                wS,
-                                analyisS,
-                                deltaWS });
-                        }
-                    }
-                    ++analysisCount;
-                }
-            }
-        }
-
-        for (ArrayList<String []> list: sorted.values()) {
-            for (String [] row: list) {
-                writer.writeNext(row);
-            }
-        }
-
-        writer.flush();
-    }
-
-    /** Template to create "State" strings like A1,A2... */
-    protected String getAnalysisTemplate() {
-        return Resources.getMsg(
-            context.getMeta(),
-            CSV_ANALYSIS, DEFAULT_CSV_ANALYSIS);
-    }
-
-    protected String getReference() {
-        return Resources.getMsg(
-            context.getMeta(),
-            CSV_REFERENCE, DEFAULT_CSV_REFERENCE);
-    }
-
-    protected NumberFormat getKMFormatter() {
-        return Formatter.getFixDeltaWKM(context);
-    }
-
-    protected NumberFormat getDeltaWFormatter() {
-        return Formatter.getFixDeltaWDeltaW(context);
-    }
-
-    protected NumberFormat getQFormatter() {
-        return Formatter.getFixDeltaWQ(context);
-    }
-
-    protected NumberFormat getWFormatter() {
-        return Formatter.getFixDeltaWW(context);
-    }
-
-    protected DateFormat getDateFormatter() {
-        CallMeta meta = context.getMeta();
-        return Formatter.getDateFormatter(
-            meta,
-            Resources.getMsg(
-                meta,
-                CSV_T_FORMAT,
-                DEFAULT_CSV_T_FORMAT));
-    }
-
-    protected void writeCSVHeader(CSVWriter writer) {
-        log.debug("DeltaWtExporter.writeCSVHeader");
-
-        /* issue825
-        km; Ereignis, Abfluss, GEMESSENER Wasserstand; Status (RECHTSBÜNDIG), del W
-        */
-
-        writer.writeNext(new String[] {
-            msg(CSV_KM_HEADER,      DEFAULT_CSV_KM_HEADER),
-            msg(CSV_T_HEADER,       DEFAULT_CSV_T_HEADER),
-            msg(CSV_Q_HEADER,       DEFAULT_CSV_Q_HEADER),
-            msg(CSV_W_HEADER,       DEFAULT_CSV_W_HEADER),
-            msg(CSV_TRANGE_HEADER,  DEFAULT_CSV_TRANGE_DESC_HEADER),
-            msg(CSV_DELTA_W_HEADER, DEFAULT_CSV_DELTA_W_HEADER)
-        });
-    }
-
-    @Override
-    protected void writePDF(OutputStream out) {
-        // TODO: Implement me!
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/fixings/FixATExport.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,100 +0,0 @@
-package de.intevation.flys.exports.fixings;
-
-import au.com.bytecode.opencsv.CSVWriter;
-
-import de.intevation.artifactdatabase.state.ArtifactAndFacet;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.flys.artifacts.access.FixAccess;
-
-import de.intevation.flys.artifacts.math.fitting.Function;
-import de.intevation.flys.artifacts.math.fitting.FunctionFactory;
-
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.Parameters;
-
-import de.intevation.flys.artifacts.model.fixings.FixResult;
-
-import de.intevation.flys.exports.AbstractExporter;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.Writer;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.NodeList;
-
-/** Export result of fixation analysis. */
-public class FixATExport extends AbstractExporter {
-
-    /** Private logger. */
-    private static Logger logger =
-        Logger.getLogger(FixATExport.class);
-
-    protected Function function;
-    protected Parameters parameters;
-
-
-    @Override
-    public void doOut(ArtifactAndFacet bundle, Document attr, boolean visible) {
-        logger.debug("AT Export doOut().");
-        Object data = bundle.getData(context);
-        if (data instanceof CalculationResult) {
-            CalculationResult cr = (CalculationResult)data;
-            Object resData = cr.getData();
-            if (resData instanceof FixResult) {
-                this.parameters = ((FixResult)resData).getParameters();
-            }
-        }
-        else {
-            logger.debug("No CalculationResult found for AT export.");
-            return;
-        }
-        FixAccess access = new FixAccess((FLYSArtifact)this.master, context);
-        String f = access.getFunction();
-        if (f == null || f.length() == 0) {
-            logger.debug("No function found for AT export.");
-            return;
-        }
-        this.function = FunctionFactory.getInstance().getFunction(f);
-    }
-
-    @Override
-    public void generate() throws IOException {
-        if (this.function == null || this.parameters == null) {
-            logger.debug("No function or paramters for AT export.");
-            return;
-        }
-
-        Writer writer = new OutputStreamWriter(out, DEFAULT_CSV_CHARSET);
-
-        FixAccess access = new FixAccess((FLYSArtifact)this.master, context);
-        FixATWriter atWriter = new FixATWriter(this.function, this.parameters);
-        NodeList nodes = request.getElementsByTagName("km");
-        String km = nodes.item(0).getTextContent();
-        double dkm = Double.parseDouble(km);
-        String river = access.getRiver();
-        atWriter.write(writer, context.getMeta(), river, dkm);
-        writer.close();
-    }
-
-    @Override
-    protected void writeCSVData(CSVWriter writer) throws IOException {
-        // The concrete writer is used to write csv data.
-    }
-
-    @Override
-    protected void writePDF(OutputStream out) {
-        // Implement me!
-    }
-
-    @Override
-    protected void addData(Object data) {
-        // Nothing to do here.
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/fixings/FixATWriter.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,187 +0,0 @@
-package de.intevation.flys.exports.fixings;
-
-import de.intevation.artifacts.CallMeta;
-
-import de.intevation.flys.artifacts.math.fitting.Function;
-
-import de.intevation.flys.artifacts.model.Parameters;
-
-import de.intevation.flys.artifacts.resources.Resources;
-
-import de.intevation.flys.exports.ATWriter;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.Writer;
-
-import java.util.Locale;
-
-import org.apache.log4j.Logger;
-
-/** Export Fixation Analysis Results to AT. */
-public class FixATWriter
-{
-    /** Private logger. */
-    private static Logger log = Logger.getLogger(FixATWriter.class);
-
-    public static final String I18N_HEADER_KEY =
-        "fix.export.at.header";
-
-    public static final String I18N_HEADER_DEFAULT =
-        "Exported fixings discharge curve for {0} {0}-km: {1}";
-
-    public static final String [] Q_MAX_COLUMN = new String [] { "max_q" };
-
-    private static final int    MAX_ITERATIONS = 10000;
-    private static final double EPSILON        = 1e-8;
-    private static final double MIN_Q          = 1e-4;
-
-    protected Function   function;
-    protected Parameters parameters;
-
-    public FixATWriter() {
-    }
-
-    public FixATWriter(Function function, Parameters parameters) {
-        this.function   = function;
-        this.parameters = parameters;
-    }
-
-    public void write(
-        Writer   writer,
-        CallMeta meta,
-        String   river,
-        double   km
-    )
-    throws IOException {
-        PrintWriter out = new PrintWriter(writer);
-        printHeader(out, meta, river, km);
-
-        double [] coeffs = parameters.interpolate(
-            "km", km, function.getParameterNames());
-
-        double [] qMax = parameters.interpolate(
-            "km", km, Q_MAX_COLUMN);
-
-        if (coeffs == null || qMax == null) {
-            log.debug("No data found at km " + km + ".");
-            return;
-        }
-
-        de.intevation.flys.artifacts.math.Function funcInst =
-            function.instantiate(coeffs);
-
-        // Increase Q max about 5%.
-        qMax[0] += Math.abs(qMax[0])*0.05;
-
-        double wMax = funcInst.value(qMax[0]);
-
-        if (Double.isNaN(wMax) || wMax < 0d) {
-            log.debug("function '" + function.getName() +
-                "' eval failed at " + wMax);
-            return;
-        }
-
-        Function inverse = function.getInverse();
-
-        de.intevation.flys.artifacts.math.Function invInst =
-            inverse.instantiate(coeffs);
-
-        double wMin = minW(invInst, wMax, qMax[0]);
-
-        double wMinCM = wMin * 100d;
-        double wMaxCM = wMax * 100d;
-
-        int wRow = ((int)wMinCM / 10) * 10;
-
-        if ((wMinCM - (int)wMinCM) > 0d) {
-            wMinCM = (int)wMinCM + 1d;
-        }
-
-        double w = wMinCM / 100.0;
-
-        int wcm = ((int)wMinCM) % 10;
-
-        if (log.isDebugEnabled()) {
-            log.debug("wMinCM: " + wMinCM);
-            log.debug("wMaxCM: " + wMaxCM);
-            log.debug("wcm: " + wcm);
-        }
-
-        out.printf(Locale.US, "%8d", wRow);
-
-        if (wcm > 0) {
-            int rest = 10 - wcm;
-            while (rest-- > 0) {
-                out.print(ATWriter.EMPTY);
-            }
-        }
-
-        for (;;) {
-            while (wcm++ < 10) {
-                if (w > wMax) {
-                    break;
-                }
-                double q = invInst.value(w);
-                if (Double.isNaN(w)) {
-                    out.print(ATWriter.EMPTY);
-                }
-                else {
-                    ATWriter.printQ(out, q);
-                }
-                w += 0.01d;
-            }
-            out.println();
-            if (w > wMax) {
-                break;
-            }
-            out.printf(Locale.US, "%8d", wRow += 10);
-            wcm = 0;
-        }
-
-        out.flush();
-    }
-
-    protected void printHeader(
-        PrintWriter out,
-        CallMeta    meta,
-        String      river,
-        double      km
-    ) {
-        out.println(Resources.format(
-            meta,
-            I18N_HEADER_KEY,
-            I18N_HEADER_DEFAULT,
-            river, km));
-    }
-
-    private static double minW(
-        de.intevation.flys.artifacts.math.Function function,
-        double maxW,
-        double maxQ
-    ) {
-        double stepWidth = 10d;
-
-        double lastW = maxW;
-        double lastQ = maxQ;
-
-        for (int i = 0; i < MAX_ITERATIONS; ++i) {
-            double w = lastW - stepWidth;
-            double q = function.value(w);
-
-            if (Double.isNaN(q) || q > lastQ || q < MIN_Q) {
-                if (stepWidth < EPSILON) {
-                    break;
-                }
-                stepWidth *= 0.5d;
-                continue;
-            }
-
-            lastW = w;
-            lastQ = q;
-        }
-
-        return lastW;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/fixings/FixChartGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +0,0 @@
-package de.intevation.flys.exports.fixings;
-
-import de.intevation.artifacts.ArtifactNamespaceContext;
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.common.utils.XMLUtils;
-import de.intevation.flys.exports.XYChartGenerator;
-
-import java.io.OutputStream;
-
-import org.apache.log4j.Logger;
-import org.w3c.dom.Document;
-
-/**
- * Base class for FixChartGenerator.
- */
-public abstract class FixChartGenerator
-extends XYChartGenerator
-{
-    /** Private logger. */
-    private static Logger logger = Logger.getLogger(FixChartGenerator.class);
-
-    public static final Double INVALID_KM = Double.valueOf(-1d);
-    public static final String CURRENT_KM = "currentKm";
-    public static final String XPATH_CHART_CURRENTKM =
-        "/art:action/art:attributes/art:currentKm/@art:km";
-
-    @Override
-    public void init(Document request, OutputStream out, CallContext context) {
-        super.init(request, out, context);
-
-        Double currentKm = getCurrentKmFromRequest(request);
-
-        if (logger.isDebugEnabled()) {
-            logger.debug("currentKm = " + currentKm);
-        }
-
-        if (currentKm != INVALID_KM) {
-            context.putContextValue(CURRENT_KM, currentKm);
-        }
-    }
-
-    public static final Double getCurrentKmFromRequest(Document request) {
-
-        String km = XMLUtils.xpathString(
-            request,
-            XPATH_CHART_CURRENTKM,
-            ArtifactNamespaceContext.INSTANCE);
-
-        if (km == null) {
-            return INVALID_KM;
-        }
-
-        try {
-            return Double.valueOf(km);
-        }
-        catch (NumberFormatException nfe) {
-            return INVALID_KM;
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/fixings/FixDeltaWtGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,460 +0,0 @@
-package de.intevation.flys.exports.fixings;
-
-import de.intevation.artifactdatabase.state.ArtifactAndFacet;
-import de.intevation.artifacts.CallContext;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.access.FixAnalysisAccess;
-import de.intevation.flys.artifacts.model.DateRange;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.QWDDateRange;
-import de.intevation.flys.artifacts.model.fixings.QWD;
-import de.intevation.flys.artifacts.resources.Resources;
-import de.intevation.flys.exports.TimeseriesChartGenerator;
-import de.intevation.flys.jfree.CollisionFreeXYTextAnnotation;
-import de.intevation.flys.jfree.FLYSAnnotation;
-import de.intevation.flys.jfree.StyledDomainMarker;
-import de.intevation.flys.jfree.StyledTimeSeries;
-import de.intevation.flys.jfree.StyledValueMarker;
-import de.intevation.flys.utils.ThemeUtil;
-
-import java.io.OutputStream;
-import java.text.NumberFormat;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-
-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.TimeSeries;
-import org.jfree.data.time.TimeSeriesCollection;
-import org.w3c.dom.Document;
-
-
-/**
- * Generator for Delta W(t) charts.
- * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
- */
-public class FixDeltaWtGenerator
-extends      TimeseriesChartGenerator
-implements   FacetTypes
-{
-    /** Private logger. */
-    private static Logger logger =
-            Logger.getLogger(FixDeltaWtGenerator.class);
-
-    public static final String I18N_CHART_TITLE =
-            "chart.fix.deltawt.title";
-
-    public static final String I18N_CHART_SUBTITLE =
-            "chart.fix.deltawt.subtitle";
-
-    public static final String I18N_XAXIS_LABEL =
-            "chart.fix.deltawt.xaxis.label";
-
-    public static final String I18N_YAXIS_LABEL =
-            "chart.fix.deltawt.yaxis.label";
-
-    public static final String I18N_YAXIS_SECOND_LABEL =
-            "chart.fix.deltawt.yaxis.second.label";
-
-
-    public static enum YAXIS {
-        dW(0);
-        protected int idx;
-        private YAXIS(int c) {
-            idx = c;
-        }
-    }
-
-
-    private FLYSArtifact artifact = null;
-
-
-    @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
-    protected String getDefaultChartTitle() {
-        return msg(I18N_CHART_TITLE, I18N_CHART_TITLE);
-    }
-
-
-    @Override
-    protected String getChartTitle() {
-        return Resources.format(
-                context.getMeta(),
-                I18N_CHART_TITLE,
-                "",
-                FixChartGenerator
-                .getCurrentKmFromRequest(request).doubleValue());
-    }
-
-
-    @Override
-    protected String getDefaultChartSubtitle() {
-        FixAnalysisAccess access = new FixAnalysisAccess(artifact, context);
-        DateRange dateRange = access.getDateRange();
-        DateRange refRange  = access.getReferencePeriod();
-        return Resources.format(
-                context.getMeta(),
-                I18N_CHART_SUBTITLE,
-                "",
-                access.getRiver(),
-                dateRange.getFrom(),
-                dateRange.getTo(),
-                refRange.getFrom(),
-                refRange.getTo());
-    }
-
-
-    @Override
-    protected String getDefaultXAxisLabel() {
-        return msg(I18N_XAXIS_LABEL, I18N_XAXIS_LABEL);
-    }
-
-    @Override
-    protected String getDefaultYAxisLabel(int pos) {
-        if (pos == 0) {
-            return msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL);
-        }
-        else if (pos == 1) {
-            return msg(I18N_YAXIS_SECOND_LABEL, I18N_YAXIS_SECOND_LABEL);
-        }
-        else {
-            return "NO TITLE FOR Y AXIS: " + pos;
-        }
-    }
-
-
-    @Override
-    public void doOut(
-            ArtifactAndFacet artifactFacet,
-            Document         theme,
-            boolean          visible
-            ) {
-        String name = artifactFacet.getFacetName();
-        logger.debug("FixDeltaWtGenerator.doOut: " + name);
-        logger.debug("Theme description is: " + artifactFacet.getFacetDescription());
-
-        this.artifact = (FLYSArtifact)artifactFacet.getArtifact();
-
-        if (name.contains(FIX_SECTOR_AVERAGE_DWT)) {
-            doSectorAverageOut(
-                    (FLYSArtifact) artifactFacet.getArtifact(),
-                    artifactFacet.getData(context),
-                    artifactFacet.getFacetDescription(),
-                    theme,
-                    visible);
-        }
-        else if (name.equals(FIX_REFERENCE_EVENTS_DWT)) {
-            doReferenceEventsOut(
-                    (FLYSArtifact) artifactFacet.getArtifact(),
-                    artifactFacet.getData(context),
-                    artifactFacet.getFacetDescription(),
-                    theme,
-                    visible);
-        }
-        else if (name.equals(FIX_ANALYSIS_EVENTS_DWT)) {
-            doAnalysisEventsOut(
-                    (FLYSArtifact) artifactFacet.getArtifact(),
-                    artifactFacet.getData(context),
-                    artifactFacet.getFacetDescription(),
-                    theme,
-                    visible);
-        }
-        else if (name.equals(FIX_DEVIATION_DWT)) {
-            doDeviationOut(
-                    (FLYSArtifact) artifactFacet.getArtifact(),
-                    artifactFacet.getData(context),
-                    artifactFacet.getFacetDescription(),
-                    theme,
-                    visible);
-        }
-        else if (name.equals(FIX_ANALYSIS_PERIODS_DWT)) {
-            doAnalysisPeriodsOut(
-                    (FLYSArtifact) artifactFacet.getArtifact(),
-                    artifactFacet.getData(context),
-                    artifactFacet.getFacetDescription(),
-                    theme,
-                    visible);
-        }
-        else if (name.equals(FIX_REFERENCE_PERIOD_DWT)) {
-            doReferencePeriodsOut(
-                    (FLYSArtifact) artifactFacet.getArtifact(),
-                    artifactFacet.getData(context),
-                    artifactFacet.getFacetDescription(),
-                    theme,
-                    visible);
-        }
-        else if (FacetTypes.IS.MANUALPOINTS(name)) {
-            doPoints (artifactFacet.getData(context),
-                    artifactFacet,
-                    theme, visible, YAXIS.dW.idx);
-        }
-        else {
-            logger.warn("doOut(): unknown facet name: " + name);
-            return;
-        }
-    }
-
-
-    protected void doReferencePeriodsOut(
-            FLYSArtifact artifact,
-            Object       data,
-            String       desc,
-            Document     theme,
-            boolean      visible)
-    {
-        logger.debug("doReferencePeriodsOut()");
-
-        if (visible) {
-            FixAnalysisAccess access = new FixAnalysisAccess(artifact, context);
-            DateRange refRange  = access.getReferencePeriod();
-
-            RegularTimePeriod start = new Day(refRange.getFrom());
-            RegularTimePeriod end = new Day(refRange.getTo());
-            StyledDomainMarker marker = new StyledDomainMarker(
-                    start.getMiddleMillisecond(),
-                    end.getMiddleMillisecond(),
-                    theme);
-            domainMarker.add(marker);
-        }
-    }
-
-
-    protected void doSectorAverageOut(
-            FLYSArtifact artifact,
-            Object       data,
-            String       desc,
-            Document     theme,
-            boolean      visible)
-    {
-        logger.debug("doSectorAverageOut(): description = " + desc);
-
-        QWDDateRange qwd = (QWDDateRange) data;
-        TimeSeriesCollection tsc = new TimeSeriesCollection();
-        TimeSeries        series = new StyledTimeSeries(desc, theme);
-
-        if (qwd == null || qwd.qwd == null || qwd.dateRange == null) {
-            return;
-        }
-        RegularTimePeriod rtp = new Day(qwd.qwd.getDate());
-        double value = qwd.qwd.getDeltaW();
-
-        // Draw a line spanning the analysis time.
-        series.add(rtp, value);
-        rtp = new Day(qwd.dateRange.getFrom());
-        series.addOrUpdate(rtp, value);
-        rtp = new Day(qwd.dateRange.getTo());
-        series.addOrUpdate(rtp, value);
-
-        tsc.addSeries(series);
-
-        addAxisDataset(tsc, 0, visible);
-
-        if (visible && ThemeUtil.parseShowLineLabel(theme)) {
-            List<XYTextAnnotation> textAnnos = new ArrayList<XYTextAnnotation>();
-            XYTextAnnotation anno = new CollisionFreeXYTextAnnotation(
-                    "\u0394 W(t) [cm] " + (float)Math.round(qwd.qwd.getDeltaW() * 10000) / 10000,
-                    tsc.getXValue(0, 0),
-                    qwd.qwd.getDeltaW());
-            textAnnos.add(anno);
-
-            FLYSAnnotation flysAnno = new FLYSAnnotation(null, null, null, theme);
-            flysAnno.setTextAnnotations(textAnnos);
-            addAnnotations(flysAnno);
-        }
-    }
-
-
-    protected void doAnalysisEventsOut(
-            FLYSArtifact artifact,
-            Object       data,
-            String       desc,
-            Document     theme,
-            boolean      visible
-            ) {
-        logger.debug("doAnalysisEventsOut: desc = " + desc);
-
-        QWD qwd = (QWD) data;
-        doQWDEventsOut(qwd, desc, theme, visible);
-    }
-
-
-    protected void doQWDEventsOut(QWD qwd, String desc, Document theme, boolean visible)
-    {
-        TimeSeriesCollection tsc = new TimeSeriesCollection();
-
-        TimeSeries   series = new StyledTimeSeries(desc, theme);
-        TimeSeries interpol = new StyledTimeSeries(desc + "interpol", theme);
-
-        if (qwd == null) {
-            logger.debug("doQWDEventsOut: qwd == null");
-            return;
-        }
-
-        Map<Integer, int[]> annoIdxMap = new HashMap<Integer, int[]>();
-
-        int idxInterpol = 0;
-        int idxRegular = 0;
-        RegularTimePeriod rtp = new Day(qwd.getDate());
-        double value =  qwd.getDeltaW();
-        boolean interpolate = qwd.getInterpolated();
-        if (interpolate) {
-            if(interpol.addOrUpdate(rtp, value) == null) {
-                annoIdxMap.put(
-                        0,
-                        new int[]{1, idxInterpol});
-                idxInterpol++;
-            }
-        }
-        else {
-            if(series.addOrUpdate(rtp, value) == null) {
-                annoIdxMap.put(
-                        0,
-                        new int[]{0, idxRegular});
-                idxRegular++;
-            }
-        }
-
-        tsc.addSeries(series);
-        tsc.addSeries(interpol);
-        addAxisDataset(tsc, 0, visible);
-        addAttribute(desc + "interpol", "interpolate");
-        addAttribute(desc, "outline");
-
-        doQWDTextAnnotations(annoIdxMap, tsc, qwd, theme, visible);
-    }
-
-
-    /**
-     * @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,
-            boolean visible) {
-        logger.debug("doQWDTextAnnotation()");
-
-        if (!visible || !ThemeUtil.parseShowPointLabel(theme)) {
-            logger.debug("doQWDTextAnnotation: annotation not visible");
-            return;
-        }
-
-        Locale locale = Resources.getLocale(context.getMeta());
-        NumberFormat nf = NumberFormat.getInstance(locale);
-
-        List<XYTextAnnotation> textAnnos = new ArrayList<XYTextAnnotation>();
-
-        for (int[] idxs: annoIdxMap.values()) {
-
-            double x = tsc.getXValue(idxs[0], idxs[1]);
-
-            XYTextAnnotation anno = new CollisionFreeXYTextAnnotation(
-                    nf.format(qwd.getQ()) + " m\u00B3/s",
-                    x,
-                    qwd.getDeltaW());
-            textAnnos.add(anno);
-        }
-
-        FLYSAnnotation flysAnno = new FLYSAnnotation(null, null, null, theme);
-        flysAnno.setTextAnnotations(textAnnos);
-        addAnnotations(flysAnno);
-    }
-
-
-    protected void doReferenceEventsOut(
-            FLYSArtifact artifact,
-            Object       data,
-            String       desc,
-            Document     theme,
-            boolean      visible
-            ) {
-        logger.debug("doReferenceEventsOut: desc = " + desc);
-
-        QWD qwd = (QWD) data;
-        doQWDEventsOut(qwd, desc, theme, visible);
-    }
-
-
-    protected void doDeviationOut(
-            FLYSArtifact artifact,
-            Object       data,
-            String       desc,
-            Document     theme,
-            boolean      visible
-            ) {
-        logger.debug("doDeviationOut: desc = " + desc);
-
-        if (data == null || !visible) {
-            logger.debug("no standard deviation");
-            return;
-        }
-        double[] value = (double[]) data;
-        StyledDomainMarker lower = new StyledDomainMarker((value[0] * -1), 0, theme);
-        StyledDomainMarker upper = new StyledDomainMarker(0, value[0], theme);
-
-        valueMarker.add(lower);
-        valueMarker.add(upper);
-    }
-
-
-    protected void doAnalysisPeriodsOut(
-            FLYSArtifact artifact,
-            Object       data,
-            String       desc,
-            Document     theme,
-            boolean      visible)
-    {
-        DateRange[] ranges = (DateRange[]) data;
-        if (ranges == null || !visible) {
-            return;
-        }
-        for (int i = 0; i < ranges.length; i++) {
-            logger.debug("creating domain marker");
-            RegularTimePeriod start = new Day(ranges[i].getFrom());
-            RegularTimePeriod end = new Day(ranges[i].getTo());
-            StyledDomainMarker marker =
-                    new StyledDomainMarker(start.getMiddleMillisecond(),
-                            end.getMiddleMillisecond(), theme);
-            marker.useSecondColor(i % 2 == 0);
-            domainMarker.add(marker);
-        }
-        logger.debug("domainmarkers: " + domainMarker.size());
-    }
-
-
-    @Override
-    public void init(Document request, OutputStream out, CallContext context) {
-        super.init(request, out, context);
-
-        Double currentKm = FixChartGenerator.getCurrentKmFromRequest(request);
-
-        if (logger.isDebugEnabled()) {
-            logger.debug("currentKm = " + currentKm);
-        }
-
-        context.putContextValue("currentKm", currentKm);
-
-        StyledValueMarker marker = new StyledValueMarker(0, request);
-        valueMarker.add(marker);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/fixings/FixDeltaWtInfoGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-package de.intevation.flys.exports.fixings;
-
-import de.intevation.flys.exports.ChartInfoGenerator;
-
-public class FixDeltaWtInfoGenerator extends ChartInfoGenerator {
-
-    public FixDeltaWtInfoGenerator() {
-        super(new FixDeltaWtGenerator());
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/fixings/FixDerivedCurveGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,138 +0,0 @@
-package de.intevation.flys.exports.fixings;
-
-import org.apache.log4j.Logger;
-import org.w3c.dom.Document;
-
-import de.intevation.artifactdatabase.state.ArtifactAndFacet;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.fixings.FixDerivateFacet;
-import de.intevation.flys.artifacts.model.fixings.FixFunction;
-import de.intevation.flys.artifacts.resources.Resources;
-import de.intevation.flys.exports.ChartGenerator;
-import de.intevation.flys.jfree.JFreeUtil;
-import de.intevation.flys.jfree.StyledXYSeries;
-
-/**
- * Generator for fixation derived function curve.
- *
- * @author <a href="mailto:christian.lins@intevation.de">Christian Lins</a>
- */
-public class FixDerivedCurveGenerator
-extends FixChartGenerator
-implements FacetTypes
-{
-    /** Private logger. */
-    private static Logger logger =
-            Logger.getLogger(FixDerivedCurveGenerator.class);
-
-    public static final String I18N_CHART_TITLE =
-            "chart.fixings.derivedcurve.title";
-
-    public static final String I18N_CHART_SUBTITLE =
-            "chart.fixings.derivedcurve.subtitle";
-
-    public static final String I18N_XAXIS_LABEL =
-            "chart.fixings.derivedcurve.xaxis.label";
-
-    public static final String I18N_YAXIS_LABEL =
-            "chart.fixings.derivedcurve.yaxis.label";
-
-    public static final String I18N_CHART_TITLE_DEFAULT =
-            "Ableitungskurve";
-
-    public static final String I18N_XAXIS_LABEL_DEFAULT =
-            "Q [m\u00B3/s]";
-
-    public static final String I18N_YAXIS_LABEL_DEFAULT =
-            "W [NN + m]";
-
-    public static enum YAXIS {
-        W(0),
-        Q(1);
-        public int idx;
-        private YAXIS(int c) {
-            idx = c;
-        }
-    }
-
-
-    @Override
-    public void doOut(ArtifactAndFacet aaf, Document doc, boolean visible) {
-        logger.debug("doOut");
-
-        if (FacetTypes.IS.MANUALPOINTS(aaf.getFacetName())) {
-            doPoints(aaf.getData(context),
-                    aaf,
-                    doc, visible, YAXIS.W.idx);
-        }
-        else {
-            FixDerivateFacet facet = (FixDerivateFacet)aaf.getFacet();
-            FixFunction func = (FixFunction)facet.getData(
-                    aaf.getArtifact(), context);
-
-            if (func == null) {
-                logger.warn("doOut: Facet does not contain FixFunction");
-                return;
-            }
-
-            double maxQ = func.getMaxQ();
-
-            if (maxQ > 0) {
-                StyledXYSeries series = JFreeUtil.sampleFunction2D(
-                        func.getFunction(),
-                        doc,
-                        aaf.getFacetDescription(),
-                        500,   // number of samples
-                        0.0 ,  // start
-                        maxQ); // end
-                addAxisSeries(series, 0, visible);
-            }
-        }
-    }
-
-
-    @Override
-    protected String getDefaultChartTitle() {
-        return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT);
-    }
-
-
-    @Override
-    protected String getChartTitle() {
-        return Resources.format(
-                context.getMeta(),
-                I18N_CHART_TITLE,
-                I18N_CHART_TITLE_DEFAULT,
-                context.getContextValue(CURRENT_KM));
-    }
-
-
-    @Override
-    protected String getDefaultXAxisLabel() {
-        return msg(I18N_XAXIS_LABEL, I18N_XAXIS_LABEL_DEFAULT);
-    }
-
-
-    @Override
-    protected String getDefaultYAxisLabel(int pos) {
-        return msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL_DEFAULT);
-    }
-
-
-    @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/flys-artifacts/src/main/java/de/intevation/flys/exports/fixings/FixDerivedCurveInfoGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-package de.intevation.flys.exports.fixings;
-
-import de.intevation.flys.exports.ChartInfoGenerator;
-
-public class FixDerivedCurveInfoGenerator extends ChartInfoGenerator {
-
-    public FixDerivedCurveInfoGenerator() {
-        super(new FixDerivedCurveGenerator());
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/fixings/FixLongitudinalSectionGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,213 +0,0 @@
-package de.intevation.flys.exports.fixings;
-
-import java.awt.BasicStroke;
-import java.awt.Color;
-
-import org.apache.log4j.Logger;
-import org.jfree.chart.plot.Marker;
-import org.jfree.chart.plot.ValueMarker;
-import org.jfree.data.xy.XYSeries;
-import org.w3c.dom.Document;
-
-import de.intevation.artifactdatabase.state.ArtifactAndFacet;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.fixings.AnalysisPeriod;
-import de.intevation.flys.artifacts.model.fixings.QWD;
-import de.intevation.flys.exports.ChartGenerator;
-import de.intevation.flys.exports.process.KMIndexProcessor;
-import de.intevation.flys.exports.process.Processor;
-import de.intevation.flys.jfree.FLYSAnnotation;
-import de.intevation.flys.jfree.StyledAreaSeriesCollection;
-import de.intevation.flys.jfree.StyledXYSeries;
-import de.intevation.flys.utils.KMIndex;
-
-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 enum YAXIS {
-        dW(0);
-        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();
-        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 (name.equals(FIX_DEVIATION_LS)) {
-            doReferenceDeviationOut(aaf, doc, visible);
-        }
-        else if (name.equals(LONGITUDINAL_ANNOTATION)) {
-            doAnnotations(
-                    (FLYSAnnotation) 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);
-        }
-    }
-
-    @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) {
-        return msg(I18N_DW_YAXIS_LABEL, I18N_DW_YAXIS_LABEL_DEFAULT);
-    }
-
-    @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/flys-artifacts/src/main/java/de/intevation/flys/exports/fixings/FixLongitudinalSectionInfoGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-package de.intevation.flys.exports.fixings;
-
-import de.intevation.flys.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/flys-artifacts/src/main/java/de/intevation/flys/exports/fixings/FixWQCurveGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,620 +0,0 @@
-package de.intevation.flys.exports.fixings;
-
-import java.awt.BasicStroke;
-import java.awt.Color;
-import java.text.DateFormat;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-import org.jfree.chart.JFreeChart;
-import org.jfree.chart.annotations.XYTextAnnotation;
-import org.jfree.chart.plot.Marker;
-import org.jfree.chart.plot.ValueMarker;
-import org.jfree.chart.title.TextTitle;
-import org.jfree.data.xy.XYSeries;
-import org.jfree.ui.RectangleAnchor;
-import org.jfree.ui.RectangleInsets;
-import org.jfree.ui.TextAnchor;
-import org.w3c.dom.Document;
-
-import de.intevation.artifactdatabase.state.ArtifactAndFacet;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.StaticWKmsArtifact;
-import de.intevation.flys.artifacts.WINFOArtifact;
-import de.intevation.flys.artifacts.access.FixAnalysisAccess;
-import de.intevation.flys.artifacts.model.DateRange;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.NamedDouble;
-import de.intevation.flys.artifacts.model.QWDDateRange;
-import de.intevation.flys.artifacts.model.WKms;
-import de.intevation.flys.artifacts.model.WQKms;
-import de.intevation.flys.artifacts.model.fixings.FixFunction;
-import de.intevation.flys.artifacts.model.fixings.FixWQCurveFacet;
-import de.intevation.flys.artifacts.model.fixings.QWD;
-import de.intevation.flys.artifacts.model.fixings.QWI;
-import de.intevation.flys.artifacts.resources.Resources;
-import de.intevation.flys.exports.ChartGenerator;
-import de.intevation.flys.exports.StyledSeriesBuilder;
-import de.intevation.flys.jfree.CollisionFreeXYTextAnnotation;
-import de.intevation.flys.jfree.FLYSAnnotation;
-import de.intevation.flys.jfree.JFreeUtil;
-import de.intevation.flys.jfree.StickyAxisAnnotation;
-import de.intevation.flys.jfree.StyledXYSeries;
-import de.intevation.flys.model.Gauge;
-import de.intevation.flys.model.River;
-import de.intevation.flys.utils.FLYSUtils;
-import de.intevation.flys.utils.ThemeUtil;
-
-/**
- * Generator for WQ fixing charts.
- * @author <a href="mailto:christian.lins@intevation.de">Christian Lins</a>
- */
-public class FixWQCurveGenerator
-extends      FixChartGenerator
-implements   FacetTypes
-{
-    /** Private logger. */
-    private static Logger logger =
-            Logger.getLogger(FixWQCurveGenerator.class);
-
-    public static final String I18N_CHART_TITLE =
-            "chart.fixings.wq.title";
-
-    public static final String I18N_CHART_SUBTITLE =
-            "chart.fixings.wq.subtitle";
-
-    public static final String I18N_CHART_SUBTITLE1 =
-            "chart.fixings.wq.subtitle1";
-
-    public static final String I18N_XAXIS_LABEL =
-            "chart.fixings.wq.xaxis.label";
-
-    public static final String I18N_YAXIS_LABEL =
-            "chart.fixings.wq.yaxis.label";
-
-    public static final String I18N_CHART_TITLE_DEFAULT  =
-            "Fixierungsanalyse";
-
-    public static final String I18N_XAXIS_LABEL_DEFAULT  =
-            "Q [m\u00B3/s]";
-
-    public static final String I18N_YAXIS_LABEL_DEFAULT  =
-            "W [NN + m]";
-
-    public static final double EPSILON = 0.001d;
-
-    public static enum YAXIS {
-        W(0),
-        Q(1);
-        public int idx;
-        private YAXIS(int c) {
-            idx = c;
-        }
-    }
-
-
-    /** Needed to access data to create subtitle. */
-    protected FLYSArtifact artifact;
-
-
-    @Override
-    public void doOut(ArtifactAndFacet aaf, Document doc, boolean visible) {
-        logger.debug("doOut: " + aaf.getFacetName());
-        if (!prepareChartData(aaf, doc, visible)) {
-            logger.warn("Unknown facet, name " + aaf.getFacetName());
-        }
-    }
-
-
-    /** Return true if data could be handled. */
-    public boolean prepareChartData(ArtifactAndFacet aaf, Document doc, boolean visible) {
-        String name = aaf.getFacetName();
-
-        this.artifact = (FLYSArtifact)aaf.getArtifact();
-
-        if(name.startsWith(FIX_SECTOR_AVERAGE_WQ)) {
-            doSectorAverageOut(aaf, doc, visible);
-        }
-        else if(FIX_ANALYSIS_EVENTS_WQ.equals(name)) {
-            doAnalysisEventsOut(aaf, doc, visible);
-        }
-        else if(FIX_REFERENCE_EVENTS_WQ.equals(name)) {
-            doReferenceEventsOut(aaf, doc, visible);
-        }
-        else if(FIX_WQ_CURVE.equals(name)) {
-            doWQCurveOut(aaf, doc, visible);
-        }
-        else if(FIX_OUTLIER.equals(name)) {
-            doOutlierOut(aaf, doc, visible);
-        }
-        else if(QSECTOR.equals(name)) {
-            doQSectorOut(aaf, doc, visible);
-        }
-        else if(FIX_EVENTS.equals(name)) {
-            doEventsOut(aaf, doc, visible);
-        }
-        else if(/*STATIC_WKMS_INTERPOL.equals(name) ||*/
-                STATIC_WKMS_MARKS.equals(name) ||
-                STATIC_WKMS.equals(name) ||
-                HEIGHTMARKS_POINTS.equals(name) ) {
-            doWAnnotations(
-                    aaf.getData(context),
-                    aaf,
-                    doc,
-                    visible);
-        }
-        else if (LONGITUDINAL_W.equals(name) || STATIC_WQ.equals(name)
-                        || STATIC_WKMS_INTERPOL.equals(name)) {
-            doWQOut(aaf.getData(context), aaf, doc, visible);
-        }
-        else if (name.equals(DISCHARGE_CURVE)) {
-            doDischargeOut(
-                    (WINFOArtifact) aaf.getArtifact(),
-                    aaf.getData(context),
-                    aaf.getFacetDescription(),
-                    doc,
-                    visible);
-        }
-        else if (FacetTypes.IS.MANUALPOINTS(aaf.getFacetName())) {
-            doPoints(aaf.getData(context),
-                    aaf,
-                    doc, visible, YAXIS.W.idx);
-        }
-        else {
-            return false;
-        }
-        return true;
-    }
-
-
-    /** Add sector average points to chart */
-    protected void doSectorAverageOut(ArtifactAndFacet aaf, Document doc, boolean visible) {
-        logger.debug("doSectorAverageOut");
-
-        QWDDateRange qwdd = (QWDDateRange) aaf.getData(context);
-        QWD qwd = qwdd != null ? qwdd.getQWD() : null;
-
-        if(qwd != null) {
-            addQWSeries(new QWD[] { qwd }, aaf, doc, visible);
-        }
-        else {
-            logger.debug("doSectorAverageOut: qwd == null");
-        }
-    }
-
-    /** Add analysis event points to chart */
-    protected void doAnalysisEventsOut(ArtifactAndFacet aaf, Document doc, boolean visible) {
-        logger.debug("doAnalysisEventsOut");
-
-        QWD qwd = (QWD)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());
-
-            XYTextAnnotation anno = new CollisionFreeXYTextAnnotation(
-                    dateFormat.format(qwd.getDate()),
-                    qwd.getQ(),
-                    qwd.getW());
-            textAnnos.add(anno);
-
-            addAxisSeries(series, 0, visible);
-            if(visible && ThemeUtil.parseShowPointLabel(doc)) {
-                FLYSAnnotation flysAnno = new FLYSAnnotation(null, null, null, doc);
-                flysAnno.setTextAnnotations(textAnnos);
-                addAnnotations(flysAnno);
-            }
-        }
-        else {
-            logger.debug("doAnalysisEventsOut: qwds == null");
-        }
-    }
-
-
-    /** Add reference event points to chart */
-    protected void doReferenceEventsOut(ArtifactAndFacet aaf, Document 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());
-
-            XYTextAnnotation anno = new CollisionFreeXYTextAnnotation(
-                    dateFormat.format(qwd.getDate()),
-                    qwd.getQ(),
-                    qwd.getW());
-            textAnnos.add(anno);
-
-            addAxisSeries(series, 0, visible);
-            if(visible && ThemeUtil.parseShowPointLabel(doc)) {
-                FLYSAnnotation flysAnno = new FLYSAnnotation(null, null, null, doc);
-                flysAnno.setTextAnnotations(textAnnos);
-                addAnnotations(flysAnno);
-            }
-        }
-        else {
-            logger.debug("doReferenceEventsOut: qwds == null");
-        }
-    }
-
-
-    private void addPointFromWQKms(WQKms wqkms,
-        String title,
-        Document theme,
-        boolean visible
-    ) {
-        XYSeries series = new StyledXYSeries(title, theme);
-        Double ckm = (Double) context.getContextValue(CURRENT_KM);
-        if (wqkms == null || wqkms.getKms().length == 0 || ckm == null) {
-            logger.info("addPointFromWQKms: No event data to show.");
-            return;
-        }
-        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));
-                addAxisSeries(series, YAXIS.W.idx, visible);
-                return;
-            }
-        }
-    }
-
-    protected void doEventsOut(ArtifactAndFacet aaf, Document doc, boolean visible) {
-        logger.debug("doEventsOut");
-        // Find W/Q at km.
-        addPointFromWQKms((WQKms) aaf.getData(context),
-            aaf.getFacetDescription(), doc, visible);
-    }
-
-
-    protected void doWQCurveOut(ArtifactAndFacet aaf, Document doc, boolean visible) {
-        logger.debug("doWQCurveOut");
-
-        FixWQCurveFacet facet = (FixWQCurveFacet)aaf.getFacet();
-        FixFunction func = (FixFunction)facet.getData(
-                aaf.getArtifact(), context);
-
-        if (func == null) {
-            logger.warn("doWQCurveOut: Facet does not contain FixFunction");
-            return;
-        }
-
-        double maxQ = func.getMaxQ();
-
-        if (maxQ > 0) {
-            StyledXYSeries series = JFreeUtil.sampleFunction2D(
-                    func.getFunction(),
-                    doc,
-                    aaf.getFacetDescription(),
-                    500,   // number of samples
-                    0.0 ,  // start
-                    maxQ); // end
-
-            addAxisSeries(series, 0, visible);
-        }
-        else {
-            logger.warn("doWQCurveOut: maxQ <= 0");
-        }
-    }
-
-    protected void doOutlierOut(ArtifactAndFacet aaf, Document doc, boolean visible) {
-        logger.debug("doOutlierOut");
-
-        QWI[] qws = (QWI[])aaf.getData(context);
-        addQWSeries(qws, aaf, doc, visible);
-    }
-
-
-    /** Add markers for q sectors. */
-    protected void doQSectorOut(ArtifactAndFacet aaf, Document theme, boolean visible) {
-        logger.debug("doQSectorOut");
-        if (!visible) {
-            return;
-        }
-
-        Object qsectorsObj = aaf.getData(context);
-        if (qsectorsObj == null || !(qsectorsObj instanceof List)) {
-            logger.warn("No QSectors coming from data.");
-            return;
-        }
-
-        List<?> qsectorsList = (List<?>) qsectorsObj;
-        if (qsectorsList.size() == 0 || !(qsectorsList.get(0) instanceof NamedDouble)) {
-            logger.warn("No QSectors coming from data.");
-            return;
-        }
-
-        @SuppressWarnings("unchecked")
-        List<NamedDouble> qsectors = (List<NamedDouble>) qsectorsList;
-
-        for (NamedDouble qsector : qsectors) {
-            if (Double.isNaN(qsector.getValue())) {
-                continue;
-            }
-            Marker m = new ValueMarker(qsector.getValue());
-            m.setPaint(Color.black);
-
-            float[] dashes = ThemeUtil.parseLineStyle(theme);
-            int size       = ThemeUtil.parseLineWidth(theme);
-            BasicStroke stroke;
-            if (dashes.length <= 1) {
-                stroke = new BasicStroke(size);
-            }
-            else {
-                stroke = new BasicStroke(size,
-                        BasicStroke.CAP_BUTT,
-                        BasicStroke.JOIN_ROUND,
-                        1.0f,
-                        dashes,
-                        0.0f);
-            }
-            m.setStroke(stroke);
-
-            if (ThemeUtil.parseShowLineLabel(theme)) {
-                m.setLabel(qsector.getName());
-                m.setPaint(ThemeUtil.parseTextColor(theme));
-                m.setLabelFont(ThemeUtil.parseTextFont(theme));
-            }
-            Color paint = ThemeUtil.parseLineColorField(theme);
-            if (paint != null) {
-                m.setPaint(paint);
-            }
-            m.setLabelAnchor(RectangleAnchor.TOP_LEFT);
-            m.setLabelTextAnchor(TextAnchor.TOP_LEFT);
-            m.setLabelOffset(new RectangleInsets(5, 5, 10, 10));
-            addDomainMarker(m);
-        }
-    }
-
-
-    /**
-     * Add W-Annotations to plot.
-     * @param wqkms actual data (double[][]).
-     * @param theme theme to use.
-     */
-    protected void doWAnnotations(
-            Object   wqkms,
-            ArtifactAndFacet aandf,
-            Document theme,
-            boolean  visible
-            ) {
-        Facet facet = aandf.getFacet();
-
-        List<StickyAxisAnnotation> xy = new ArrayList<StickyAxisAnnotation>();
-        if (wqkms instanceof double[][]) {
-            logger.debug("Got double[][]");
-            double [][] data = (double [][]) wqkms;
-            for (int i = 0; i< data[0].length; i++) {
-                xy.add(new StickyAxisAnnotation(aandf.getFacetDescription(),
-                        (float) data[1][i], StickyAxisAnnotation.SimpleAxis.Y_AXIS));
-            }
-
-            doAnnotations(new FLYSAnnotation(facet.getDescription(), xy),
-                    aandf, theme, visible);
-        }
-        else {
-            // Assume its WKms.
-            logger.debug("Got WKms");
-            WKms data = (WKms) wqkms;
-
-            Double ckm = (Double) context.getContextValue(CURRENT_KM);
-            double location = (ckm != null)
-                    ? ckm.doubleValue()
-                    : getRange()[0];
-            double w = StaticWKmsArtifact.getWAtKmLin(data, location);
-            xy.add(new StickyAxisAnnotation(aandf.getFacetDescription(),
-                    (float) w, StickyAxisAnnotation.SimpleAxis.Y_AXIS));
-
-            doAnnotations(new FLYSAnnotation(facet.getDescription(), xy),
-                    aandf, theme, visible);
-        }
-    }
-
-
-    /**
-     * Add series with discharge curve to diagram.
-     */
-    protected void doDischargeOut(
-            WINFOArtifact artifact,
-            Object        o,
-            String        description,
-            Document      theme,
-            boolean       visible)
-    {
-        WQKms wqkms = (WQKms) o;
-
-        String gaugeName = wqkms.getName();
-
-        River river = FLYSUtils.getRiver(artifact);
-
-        if (river == null) {
-            logger.debug("no river found");
-            return;
-        }
-
-        Gauge gauge = river.determineGaugeByName(gaugeName);
-
-        if (gauge == null) {
-            logger.debug("no gauge found");
-            return;
-        }
-
-        XYSeries series = new StyledXYSeries(description, theme);
-        StyledSeriesBuilder.addPointsQW(series, wqkms);
-        addAxisSeries(series, YAXIS.W.idx, visible);
-    }
-
-
-    /**
-     * Add WQ Data to plot.
-     * @param wqkms data as double[][]
-     */
-    protected void doWQOut(
-            Object           wqkms,
-            ArtifactAndFacet aaf,
-            Document         theme,
-            boolean          visible
-            ) {
-        logger.debug("FixWQCurveGenerator: doWQOut");
-        if (wqkms instanceof WQKms) {
-            // TODO As in doEventsOut, the value-searching should
-            // be delivered by the facet already (instead of in the Generator).
-            logger.debug("FixWQCurveGenerator: doWQOut: WQKms");
-
-            addPointFromWQKms((WQKms) aaf.getData(context), aaf.getFacetDescription(), theme, visible);
-        }
-        else {
-            logger.debug("FixWQCurveGenerator: doWQOut: double[][]");
-            double [][] data = (double [][]) wqkms;
-
-            XYSeries series = new StyledXYSeries(aaf.getFacetDescription(), theme);
-            StyledSeriesBuilder.addPoints(series, data, true);
-
-            addAxisSeries(series, YAXIS.W.idx, visible);
-        }
-    }
-
-
-    protected void addQWSeries(
-            QWI []           qws,
-            ArtifactAndFacet aaf,
-            Document         theme,
-            boolean          visible
-            ) {
-        if (qws == null) {
-            return;
-        }
-
-        XYSeries series = new StyledXYSeries(aaf.getFacetDescription(), theme);
-        List<XYTextAnnotation> textAnnos =
-                new ArrayList<XYTextAnnotation>(qws.length);
-
-        DateFormat dateFormat = DateFormat.getDateInstance(
-                DateFormat.SHORT);
-
-        for (QWI qw: qws) {
-            series.add(qw.getQ(), qw.getW());
-
-            XYTextAnnotation anno = new CollisionFreeXYTextAnnotation(
-                    dateFormat.format(qw.getDate()),
-                    qw.getQ(),
-                    qw.getW());
-            textAnnos.add(anno);
-        }
-
-        addAxisSeries(series, 0, visible);
-        if (visible && ThemeUtil.parseShowPointLabel(theme)) {
-            FLYSAnnotation flysAnno =
-                    new FLYSAnnotation(null, null, null, theme);
-            flysAnno.setTextAnnotations(textAnnos);
-            addAnnotations(flysAnno);
-        }
-    }
-
-    @Override
-    protected String getChartTitle() {
-        return Resources.format(
-                context.getMeta(),
-                I18N_CHART_TITLE,
-                I18N_CHART_TITLE_DEFAULT,
-                context.getContextValue(CURRENT_KM));
-    }
-
-    @Override
-    protected String getDefaultChartTitle() {
-        return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT);
-    }
-
-    @Override
-    protected String getDefaultChartSubtitle() {
-        FixAnalysisAccess access = new FixAnalysisAccess(artifact, context);
-        DateRange dateRange = access.getDateRange();
-        DateRange refRange  = access.getReferencePeriod();
-
-        if (dateRange != null && refRange != null) {
-            return Resources.format(
-                    context.getMeta(),
-                    I18N_CHART_SUBTITLE,
-                    "",
-                    access.getRiver(),
-                    dateRange.getFrom(),
-                    dateRange.getTo(),
-                    refRange.getFrom(),
-                    refRange.getTo());
-        }
-
-        return null;
-    }
-
-    @Override
-    protected void addSubtitles(JFreeChart chart) {
-        String defaultSubtitle = getDefaultChartSubtitle();
-
-        if (defaultSubtitle == null || defaultSubtitle.length() == 0) {
-            return;
-        }
-
-        chart.addSubtitle(new TextTitle(defaultSubtitle));
-
-        StringBuilder buf = new StringBuilder();
-
-        // Add analysis periods as additional subtitle
-        FixAnalysisAccess access = new FixAnalysisAccess(artifact, context);
-        DateRange[] aperiods = access.getAnalysisPeriods();
-        buf.append(msg("fix.analysis.periods"));
-        buf.append(": ");
-        for(int n = 0; n < aperiods.length; n++) {
-            buf.append(
-                    Resources.format(
-                            context.getMeta(),
-                            I18N_CHART_SUBTITLE1,
-                            "",
-                            aperiods[n].getFrom(),
-                            aperiods[n].getTo()));
-            if(n + 1 < aperiods.length) {
-                buf.append("; ");
-            }
-        }
-
-        chart.addSubtitle(new TextTitle(buf.toString()));
-    }
-
-    @Override
-    protected String getDefaultXAxisLabel() {
-        return msg(I18N_XAXIS_LABEL, I18N_XAXIS_LABEL_DEFAULT);
-    }
-
-    @Override
-    protected String getDefaultYAxisLabel(int pos) {
-        return msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL_DEFAULT);
-    }
-
-    @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/flys-artifacts/src/main/java/de/intevation/flys/exports/fixings/FixWQCurveInfoGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-package de.intevation.flys.exports.fixings;
-
-import de.intevation.flys.exports.ChartInfoGenerator;
-
-/**
- * A ChartInfoGenerator that generates meta information for specific fixing
- * analysis W/Q curves.
- *
- * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
- */
-public class FixWQCurveInfoGenerator extends ChartInfoGenerator {
-
-    public FixWQCurveInfoGenerator() {
-        super(new FixWQCurveGenerator());
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/fixings/ParametersExporter.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,115 +0,0 @@
-package de.intevation.flys.exports.fixings;
-
-import au.com.bytecode.opencsv.CSVWriter;
-
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.Parameters;
-
-import de.intevation.flys.artifacts.model.fixings.FixAnalysisResult;
-
-import de.intevation.flys.artifacts.resources.Resources;
-
-import de.intevation.flys.exports.AbstractExporter;
-
-import java.io.IOException;
-import java.io.OutputStream;
-
-import java.text.NumberFormat;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Document;
-
-public class ParametersExporter
-extends      AbstractExporter
-{
-    private static Logger log = Logger.getLogger(ParametersExporter.class);
-
-    protected List<Parameters> parametersList;
-
-    @Override
-    public void init(Document request, OutputStream out, CallContext context) {
-        log.debug("ParametersExporter.init");
-        super.init(request, out, context);
-        parametersList = new ArrayList<Parameters>();
-    }
-
-    @Override
-    protected void addData(Object d) {
-        log.debug("ParametersExporter.addData");
-        if (!(d instanceof CalculationResult)) {
-            log.warn("Invalid data type");
-            return;
-        }
-
-        Object data = ((CalculationResult)d).getData();
-        if (!(data instanceof FixAnalysisResult)) {
-            log.warn("Invalid data stored in result.");
-            return;
-        }
-
-        FixAnalysisResult result = (FixAnalysisResult)data;
-        parametersList.add(result.getParameters());
-    }
-
-    @Override
-    public void generate()
-    throws IOException
-    {
-        log.debug("ParametersExporter.generate");
-
-        if (facet == null) {
-            throw new IOException("invalid (null) facet for exporter");
-        }
-
-        if (facet.equals(FIX_PARAMETERS)) {
-            generateCSV();
-        }
-        else {
-            throw new IOException(
-                "invalid facet for exporter: '" + facet + "'");
-        }
-    }
-
-    @Override
-    protected void writeCSVData(final CSVWriter writer) throws IOException {
-
-        if (parametersList.isEmpty()) {
-            return;
-        }
-
-        Parameters parameters = parametersList.get(0);
-        writer.writeNext(parameters.getColumnNames());
-
-        final int numColumns = parameters.getNumberColumns();
-
-        parameters.visit(new Parameters.Visitor() {
-
-            String [] row = new String[numColumns];
-
-            NumberFormat format = NumberFormat.getInstance(
-                Resources.getLocale(context.getMeta()));
-
-            @Override
-            public void visit(double [] data) {
-                for (int i = 0; i < data.length; ++i) {
-                    row[i] = format.format(data[i]);
-                }
-                writer.writeNext(row);
-            }
-        }, new double[numColumns]);
-
-        writer.flush();
-    }
-
-    @Override
-    protected void writePDF(OutputStream out) {
-        // TODO: Implement me!
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/minfo/BedDiffEpochInfoGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,12 +0,0 @@
-package de.intevation.flys.exports.minfo;
-
-import de.intevation.flys.exports.ChartInfoGenerator;
-
-
-public class BedDiffEpochInfoGenerator
-extends ChartInfoGenerator
-{
-   public BedDiffEpochInfoGenerator() {
-        super(new BedDifferenceEpochGenerator());
-   }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/minfo/BedDiffHeightYearGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,163 +0,0 @@
-package de.intevation.flys.exports.minfo;
-
-import org.apache.log4j.Logger;
-import org.jfree.data.xy.XYSeries;
-import org.w3c.dom.Document;
-
-import de.intevation.artifactdatabase.state.ArtifactAndFacet;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.minfo.BedDiffYearResult;
-import de.intevation.flys.artifacts.model.minfo.BedDifferencesResult;
-import de.intevation.flys.exports.StyledSeriesBuilder;
-import de.intevation.flys.exports.fixings.FixChartGenerator;
-import de.intevation.flys.exports.process.KMIndexProcessor;
-import de.intevation.flys.exports.process.Processor;
-import de.intevation.flys.jfree.Bounds;
-import de.intevation.flys.jfree.DoubleBounds;
-import de.intevation.flys.jfree.FLYSAnnotation;
-import de.intevation.flys.jfree.StyledXYSeries;
-
-
-public class BedDiffHeightYearGenerator
-extends FixChartGenerator
-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 (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) {
-            BedDifferencesResult data = (BedDifferencesResult)bundle.getData(context);
-            context.putContextValue("startkm", data.getKms().min());
-            context.putContextValue("endkm", data.getKms().max());
-        }
-        else if (getXBounds(0) == null && getDomainAxisRange() != null){
-            BedDifferencesResult data = (BedDifferencesResult)bundle.getData(context);
-            Bounds b = new DoubleBounds(data.getKms().min(), data.getKms().max());
-            Bounds bounds =
-                calculateZoom(b, getDomainAxisRange());
-            context.putContextValue("startkm", bounds.getLower());
-            context.putContextValue("endkm", bounds.getUpper());
-        }
-        Processor processor = new KMIndexProcessor();
-        if (name.equals(BED_DIFFERENCE_HEIGHT_YEAR)) {
-            doBedDifferenceYearOut(
-                (BedDiffYearResult) bundle.getData(context),
-                bundle, attr, visible);
-        }
-        else if (name.equals(BED_DIFFERENCE_HEIGHT_YEAR_FILTERED)) {
-            doBedDifferenceYearOut(
-                (BedDiffYearResult) bundle.getData(context),
-                bundle, attr, visible);
-        }
-        else if (name.equals(LONGITUDINAL_ANNOTATION)) {
-            doAnnotations(
-                (FLYSAnnotation) bundle.getData(context),
-                 bundle,
-                 attr,
-                 visible);
-        }
-        else if (processor.canHandle(name)) {
-            processor.doOut(this, bundle, attr, visible, YAXIS.dW.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);
-    }
-
-    @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";
-    }
-
-    protected void doBedDifferenceYearOut(BedDiffYearResult data,
-        ArtifactAndFacet aandf, Document theme, boolean visible) {
-
-        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
-        StyledSeriesBuilder.addPoints(series, data.getHeightPerYearData(), true);
-
-        addAxisSeries(series, YAXIS.D.idx, visible);
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/minfo/BedDiffHeightYearInfoGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,12 +0,0 @@
-package de.intevation.flys.exports.minfo;
-
-import de.intevation.flys.exports.ChartInfoGenerator;
-
-
-public class BedDiffHeightYearInfoGenerator
-extends ChartInfoGenerator
-{
-    public BedDiffHeightYearInfoGenerator() {
-        super (new BedDiffHeightYearGenerator());
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/minfo/BedDiffYearInfoGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,12 +0,0 @@
-package de.intevation.flys.exports.minfo;
-
-import de.intevation.flys.exports.ChartInfoGenerator;
-
-
-public class BedDiffYearInfoGenerator
-extends ChartInfoGenerator
-{
-    public BedDiffYearInfoGenerator() {
-        super(new BedDifferenceYearGenerator());
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/minfo/BedDifferenceEpochGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,249 +0,0 @@
-package de.intevation.flys.exports.minfo;
-
-import org.apache.log4j.Logger;
-import org.jfree.data.xy.XYSeries;
-import org.w3c.dom.Document;
-
-import de.intevation.artifactdatabase.state.ArtifactAndFacet;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.access.FlowVelocityAccess;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.WKms;
-import de.intevation.flys.artifacts.model.minfo.BedDiffEpochResult;
-import de.intevation.flys.exports.StyledSeriesBuilder;
-import de.intevation.flys.exports.fixings.FixChartGenerator;
-import de.intevation.flys.exports.process.KMIndexProcessor;
-import de.intevation.flys.exports.process.Processor;
-import de.intevation.flys.exports.process.WOutProcessor;
-import de.intevation.flys.jfree.Bounds;
-import de.intevation.flys.jfree.DoubleBounds;
-import de.intevation.flys.jfree.FLYSAnnotation;
-import de.intevation.flys.jfree.StyledXYSeries;
-import de.intevation.flys.utils.DataUtil;
-
-
-public class BedDifferenceEpochGenerator
-extends FixChartGenerator
-implements FacetTypes
-{
-    public enum YAXIS {
-        D(0), H(1), dW(2), W(3);
-
-        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.beddifference.epoch.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.height";
-
-    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 = "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 (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) {
-            FLYSArtifact artifact = (FLYSArtifact)bundle.getArtifact();
-            FlowVelocityAccess access = new FlowVelocityAccess(artifact, context);
-            context.putContextValue("startkm", access.getLowerKM());
-            context.putContextValue("endkm", access.getUpperKM());
-        }
-        else if (getXBounds(0) == null && getDomainAxisRange() != null){
-            FLYSArtifact artifact = (FLYSArtifact)bundle.getArtifact();
-            FlowVelocityAccess access = new FlowVelocityAccess(artifact, context);
-            Bounds b = new DoubleBounds(access.getLowerKM(), access.getUpperKM());
-            Bounds bounds =
-                calculateZoom(b, getDomainAxisRange());
-            context.putContextValue("startkm", bounds.getLower());
-            context.putContextValue("endkm", bounds.getUpper());
-        }
-        Processor processor = new KMIndexProcessor();
-        Processor woutp = new WOutProcessor();
-        if (name.equals(BED_DIFFERENCE_EPOCH)) {
-            doBedDifferenceEpochOut(
-                (BedDiffEpochResult) bundle.getData(context),
-                bundle, attr, visible);
-        }
-        else if (name.equals(BED_DIFFERENCE_EPOCH_HEIGHT1)) {
-            doBedDifferenceHeightsOut((BedDiffEpochResult)bundle.getData(context),
-                bundle, attr, visible, 0);
-        }
-        else if (name.equals(BED_DIFFERENCE_EPOCH_HEIGHT2)) {
-            doBedDifferenceHeightsOut((BedDiffEpochResult)bundle.getData(context),
-                bundle, attr, visible, 1);
-        }
-        else if (name.equals(BED_DIFFERENCE_EPOCH_FILTERED)) {
-            doBedDifferenceEpochOut(
-                (BedDiffEpochResult) bundle.getData(context),
-                bundle, attr, visible);
-        }
-        else if (name.equals(BED_DIFFERENCE_EPOCH_HEIGHT1_FILTERED)) {
-            doBedDifferenceHeightsOut((BedDiffEpochResult)bundle.getData(context),
-                bundle, attr, visible, 0);
-        }
-        else if (name.equals(BED_DIFFERENCE_EPOCH_HEIGHT2_FILTERED)) {
-            doBedDifferenceHeightsOut((BedDiffEpochResult)bundle.getData(context),
-                bundle, attr, visible, 1);
-        }
-        else if (processor.canHandle(name)) {
-            processor.doOut(this, bundle, attr, visible, YAXIS.dW.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 if (name.equals(LONGITUDINAL_ANNOTATION)) {
-            doAnnotations(
-                (FLYSAnnotation) bundle.getData(context),
-                 bundle,
-                 attr,
-                 visible);
-        }
-        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);
-    }
-
-    @Override
-    protected String getDefaultYAxisLabel(int pos) {
-        String label = "default";
-        if (pos == YAXIS.D.idx) {
-            label = msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL_DEFAULT);
-        }
-        else if (pos == YAXIS.H.idx) {
-            label = msg(I18N_SECOND_YAXIS_LABEL, I18N_SECOND_YAXIS_LABEL_DEFAULT);
-        }
-        else if (pos == YAXIS.dW.idx) {
-            return 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);
-        }
-        return label;
-    }
-
-    protected void doBedDifferenceEpochOut(BedDiffEpochResult data,
-        ArtifactAndFacet aandf, Document theme, boolean visible) {
-
-        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
-        StyledSeriesBuilder.addPoints(series, data.getDifferencesData(), true);
-
-        addAxisSeries(series, YAXIS.D.idx, visible);
-    }
-
-    private void doBedDifferenceHeightsOut(
-        BedDiffEpochResult data,
-        ArtifactAndFacet bundle,
-        Document attr,
-        boolean visible,
-        int idx) {
-         logger.debug("doBedDifferenceHeightOut()");
-
-        XYSeries series = new StyledXYSeries(bundle.getFacetDescription(), attr);
-        if (idx == 0) {
-            StyledSeriesBuilder.addPoints(series, data.getHeights1Data(), true);
-        }
-        else {
-            StyledSeriesBuilder.addPoints(series, data.getHeights2Data(), true);
-        }
-
-        addAxisSeries(series, YAXIS.H.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);
-        }
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/minfo/BedDifferenceYearGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,291 +0,0 @@
-package de.intevation.flys.exports.minfo;
-
-import org.apache.log4j.Logger;
-import org.jfree.data.xy.XYSeries;
-import org.w3c.dom.Document;
-
-import de.intevation.artifactdatabase.state.ArtifactAndFacet;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.WKms;
-import de.intevation.flys.artifacts.model.minfo.BedDiffYearResult;
-import de.intevation.flys.artifacts.model.minfo.BedDifferencesResult;
-import de.intevation.flys.artifacts.model.minfo.MorphologicWidth;
-import de.intevation.flys.exports.StyledSeriesBuilder;
-import de.intevation.flys.exports.fixings.FixChartGenerator;
-import de.intevation.flys.exports.process.KMIndexProcessor;
-import de.intevation.flys.exports.process.Processor;
-import de.intevation.flys.exports.process.WOutProcessor;
-import de.intevation.flys.jfree.Bounds;
-import de.intevation.flys.jfree.DoubleBounds;
-import de.intevation.flys.jfree.FLYSAnnotation;
-import de.intevation.flys.jfree.StyledXYSeries;
-import de.intevation.flys.utils.DataUtil;
-
-
-public class BedDifferenceYearGenerator
-extends FixChartGenerator
-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 (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) {
-            BedDifferencesResult data = (BedDifferencesResult)bundle.getData(context);
-            context.putContextValue("startkm", data.getKms().min());
-            context.putContextValue("endkm", data.getKms().max());
-        }
-        else if (getXBounds(0) == null && getDomainAxisRange() != null){
-            BedDifferencesResult data = (BedDifferencesResult)bundle.getData(context);
-            Bounds b = new DoubleBounds(data.getKms().min(), data.getKms().max());
-            Bounds bounds =
-                calculateZoom(b, getDomainAxisRange());
-            context.putContextValue("startkm", bounds.getLower());
-            context.putContextValue("endkm", bounds.getUpper());
-        }
-        Processor processor = new KMIndexProcessor();
-        Processor woutp = new WOutProcessor();
-        if (name.equals(BED_DIFFERENCE_YEAR)) {
-            doBedDifferenceYearOut(
-                (BedDiffYearResult) bundle.getData(context),
-                bundle, attr, visible);
-        }
-        else if (name.equals(BED_DIFFERENCE_MORPH_WIDTH)) {
-            doBedDifferenceMorphWidthOut(
-                (BedDiffYearResult) bundle.getData(context),
-                bundle, attr, visible);
-        }
-        else if (name.equals(BED_DIFFERENCE_YEAR_HEIGHT1)) {
-            doBedDifferenceHeightsOut(
-                (BedDiffYearResult)bundle.getData(context),
-                bundle, attr, visible, 0);
-        }
-        else if (name.equals(BED_DIFFERENCE_YEAR_HEIGHT2)) {
-            doBedDifferenceHeightsOut(
-                (BedDiffYearResult)bundle.getData(context),
-                bundle, attr, visible, 1);
-        }
-        else if (name.equals(BED_DIFFERENCE_YEAR_FILTERED)) {
-            doBedDifferenceYearOut(
-                (BedDiffYearResult) bundle.getData(context),
-                bundle, attr, visible);
-        }
-        else if (name.equals(BED_DIFFERENCE_YEAR_HEIGHT1_FILTERED)) {
-            doBedDifferenceHeightsOut(
-                (BedDiffYearResult)bundle.getData(context),
-                bundle, attr, visible, 0);
-        }
-        else if (name.equals(BED_DIFFERENCE_YEAR_HEIGHT2_FILTERED)) {
-            doBedDifferenceHeightsOut(
-                (BedDiffYearResult)bundle.getData(context),
-                bundle, attr, visible, 1);
-        }
-        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(
-                (FLYSAnnotation) bundle.getData(context),
-                 bundle,
-                 attr,
-                 visible);
-        }
-        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);
-    }
-
-    private void doBedDifferenceHeightsOut(
-        BedDiffYearResult data,
-        ArtifactAndFacet bundle,
-        Document attr,
-        boolean visible,
-        int idx) {
-         logger.debug("doBedDifferenceYearOut()");
-
-        XYSeries series = new StyledXYSeries(bundle.getFacetDescription(), attr);
-        if (idx == 0) {
-            StyledSeriesBuilder.addPoints(series, data.getHeights1Data(), true);
-        }
-        else {
-            StyledSeriesBuilder.addPoints(series, data.getHeights2Data(), true);
-        }
-
-        addAxisSeries(series, YAXIS.H.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);
-    }
-
-    @Override
-    protected String getDefaultYAxisLabel(int pos) {
-        String label = "default";
-        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);
-        }
-        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);
-        }
-
-        return label;
-    }
-
-    protected void doBedDifferenceYearOut(BedDiffYearResult data,
-        ArtifactAndFacet aandf, Document theme, boolean visible) {
-        logger.debug("doBedDifferenceYearOut()");
-
-        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
-        StyledSeriesBuilder.addPoints(series, data.getDifferencesData(), true);
-
-        addAxisSeries(series, YAXIS.D.idx, visible);
-    }
-
-    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);
-        }
-    }
-}
\ No newline at end of file
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/minfo/BedQualityExporter.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,216 +0,0 @@
-package de.intevation.flys.exports.minfo;
-
-import gnu.trove.TDoubleArrayList;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.text.DateFormat;
-import java.text.NumberFormat;
-import java.util.Arrays;
-import java.util.LinkedList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-import org.w3c.dom.Document;
-
-import au.com.bytecode.opencsv.CSVWriter;
-import de.intevation.artifacts.CallContext;
-import de.intevation.flys.artifacts.model.CalculationResult;
-import de.intevation.flys.artifacts.model.minfo.BedDiameterResult;
-import de.intevation.flys.artifacts.model.minfo.BedParametersResult;
-import de.intevation.flys.artifacts.model.minfo.BedQualityResult;
-import de.intevation.flys.artifacts.model.minfo.BedloadDiameterResult;
-import de.intevation.flys.exports.AbstractExporter;
-import de.intevation.flys.utils.Formatter;
-
-
-public class BedQualityExporter
-extends AbstractExporter
-{
-    /** Private logger. */
-    private static Logger logger = Logger.getLogger(BedQualityExporter.class);
-
-    private static final String CSV_HEADER_KM = "export.minfo.bedquality.km";
-    private static final String CSV_HEADER_DENSITY_CAP =
-        "export.minfo.bedquality.density_cap";
-    private static final String CSV_HEADER_DENSITY_SUB =
-        "export.minfo.bedquality.density_sub";
-    private static final String CSV_HEADER_POROSITY_CAP =
-        "export.minfo.bedquality.porosity_cap";
-    private static final String CSV_HEADER_POROSITY_SUB =
-        "export.minfo.bedquality.porosity_sub";
-    private static final String CSV_HEADER_BEDLOAD =
-        "export.minfo.bedquality.bedload";
-    private static final String CSV_HEADER_BED_CAP =
-        "export.minfo.bedquality.bed_cap";
-    private static final String CSV_HEADER_BED_SUB =
-        "export.minfo.bedquality.bed_sub";
-
-    private BedQualityResult[] results;
-
-    @Override
-    public void init(Document request, OutputStream out, CallContext context) {
-        logger.debug("BedQualityExporter.init");
-        super.init(request, out, context);
-        results = new BedQualityResult[0];
-    }
-
-    @Override
-    protected void writeCSVData(CSVWriter writer) throws IOException {
-        // TODO Auto-generated method stub
-        writeCSVHeader(writer);
-
-        NumberFormat kmf = Formatter.getCalculationKm(context.getMeta());
-
-        TDoubleArrayList kms = new TDoubleArrayList();
-        int cols = 1;
-        for (int i = 0; i < results.length; i++) {
-            BedDiameterResult[] beds = results[i].getBedResults();
-            for (int j = 0; j < beds.length; j++) {
-                TDoubleArrayList bkms = beds[j].getKms();
-                for (int k = 0; k < bkms.size(); k++) {
-                    if (!kms.contains(bkms.get(k))) {
-                        kms.add(bkms.get(k));
-                    }
-                }
-            }
-            BedloadDiameterResult[] loads = results[i].getBedloadResults();
-            for (int j = 0; j < loads.length; j++) {
-                TDoubleArrayList lkms = loads[i].getKms();
-                for (int k = 0; k < lkms.size(); k++) {
-                    if (!kms.contains(lkms.get(k))) {
-                        kms.add(lkms.get(k));
-                    }
-                }
-            }
-            cols += beds.length * 2;
-            cols += loads.length;
-            if (beds.length > 0) {
-                cols += 4;
-            }
-        }
-
-        kms.sort();
-        List<double[]> rows = new LinkedList<double[]>();
-        for (int i = 0; i < kms.size(); i++) {
-            double[] row = new double[cols];
-            double km = kms.get(i);
-            row[0] = km;
-            for (int j = 0; j < results.length; j++) {
-                BedloadDiameterResult[] loads = results[j].getBedloadResults();
-
-                for(int k = 0; k < loads.length; k++) {
-                    // k + 1: shift km column.
-                    // j* loads.length: shift periods.
-                    row[(k + 1) + (j * loads.length)] =
-                        loads[k].getDiameter(km);
-                }
-                BedDiameterResult[] beds = results[j].getBedResults();
-                for (int k = 0; k < beds.length; k++) {
-                    // k + 1: shift km column.
-                    // j * beds.length: shift periods.
-                    // loads.length * results.length: shift bed load columns.
-                    int ndx = (k + 1) + (j * beds.length) + (loads.length * results.length);
-                    row[ndx] = beds[k].getDiameterCap(km);
-                    row[ndx + 1] = beds[k].getDiameterSub(km);
-                }
-                BedParametersResult[] params = results[j].getParameters();
-                for(int k = 0; k < params.length; k++) {
-                    // loads.length + (beds.lenght * 2) * (j + 1): shift bed and bedload columns.
-                    int ndx = 1 + (loads.length + (beds.length * 2) * (j + 1));
-                    row[ndx] = params[k].getLoadDensityCap(km);
-                    row[ndx + 1] = params[k].getLoadDensitySub(km);
-                    row[ndx + 2] = params[k].getPorosityCap(km);
-                    row[ndx + 3] = params[k].getPorositySub(km);
-                }
-            }
-            rows.add(row);
-        }
-        for (double[] d : rows) {
-            logger.debug(Arrays.toString(d));
-            List<String> cells = new LinkedList<String>();
-            for (int i = 0; i < d.length; i++) {
-                if (!Double.isNaN(d[i])) {
-                    NumberFormat nf = Formatter.getFormatter(context, 1, 3);
-                    cells.add(nf.format(d[i]));
-                }
-                else {
-                    cells.add("");
-                }
-            }
-            writer.writeNext(cells.toArray(new String[cells.size()]));
-        }
-    }
-
-    @Override
-    protected void writePDF(OutputStream out) {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    protected void addData(Object data) {
-        // TODO Auto-generated method stub
-        logger.debug("addData()");
-        if (!(data instanceof CalculationResult)) {
-            logger.warn("Invalid data type.");
-            return;
-        }
-        Object[] d = (Object[])((CalculationResult)data).getData();
-
-        if (!(d instanceof BedQualityResult[])) {
-            logger.warn("Invalid result object.");
-            return;
-        }
-        results = (BedQualityResult[])d;
-    }
-
-    protected void writeCSVHeader(CSVWriter writer) {
-        logger.debug("writeCSVHeader()");
-
-        List<String> header = new LinkedList<String>();
-        if (results != null)  {
-            header.add(msg(CSV_HEADER_KM, "km"));
-            for (int i = 0; i < results.length; i++) {
-                DateFormat df = Formatter.getDateFormatter(context.getMeta(), "dd.MM.yyyy");
-                String d1 = df.format(results[i].getDateRange().getFrom());
-                String d2 = df.format(results[i].getDateRange().getTo());
-                BedloadDiameterResult[] loads = results[i].getBedloadResults();
-                BedDiameterResult[] beds = results[i].getBedResults();
-                BedParametersResult[] params = results[i].getParameters();
-                for (int j = 0; j < loads.length; j++) {
-                    header.add(msg(CSV_HEADER_BEDLOAD, CSV_HEADER_BEDLOAD) +
-                        " - " +
-                        msg(loads[j].getType().toString(),
-                            loads[j].getType().toString()) + " - " +
-                        d1 + "-" + d2);
-                }
-                for (int j = 0; j < beds.length; j++) {
-                    header.add(msg(CSV_HEADER_BED_CAP, CSV_HEADER_BED_CAP) + " - " +
-                        msg(beds[j].getType().toString(),
-                            beds[j].getType().toString()) + " - " +
-                        d1 + "-" + d2);
-                    header.add(msg(CSV_HEADER_BED_SUB, CSV_HEADER_BED_SUB) + " - " +
-                        msg(beds[j].getType().toString(),
-                            beds[j].getType().toString()) + " - " +
-                        d1 + "-" + d2);
-                }
-                if (params.length > 0) {
-                    header.add(
-                        msg(CSV_HEADER_DENSITY_CAP, CSV_HEADER_DENSITY_CAP) +
-                        " - " + d1 + "-" + d2);
-                    header.add(
-                        msg(CSV_HEADER_DENSITY_SUB, CSV_HEADER_DENSITY_SUB) +
-                        " - " + d1 + "-" + d2);
-                    header.add(
-                        msg(CSV_HEADER_POROSITY_CAP, CSV_HEADER_POROSITY_CAP) +
-                        " - " + d1 + "-" + d2);
-                    header.add(
-                        msg(CSV_HEADER_POROSITY_SUB, CSV_HEADER_POROSITY_SUB) +
-                        " - " + d1 + "-" + d2);
-                }
-            }
-        }
-        writer.writeNext(header.toArray(new String[header.size()]));
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/minfo/BedQualityGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,281 +0,0 @@
-package de.intevation.flys.exports.minfo;
-
-import org.apache.log4j.Logger;
-import org.jfree.data.xy.XYSeries;
-import org.w3c.dom.Document;
-
-import de.intevation.artifactdatabase.state.ArtifactAndFacet;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.minfo.BedDiameterResult;
-import de.intevation.flys.artifacts.model.minfo.BedParametersResult;
-import de.intevation.flys.artifacts.model.minfo.BedloadDiameterResult;
-import de.intevation.flys.exports.StyledSeriesBuilder;
-import de.intevation.flys.exports.XYChartGenerator;
-import de.intevation.flys.jfree.FLYSAnnotation;
-import de.intevation.flys.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);
-    }
-
-    @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(LONGITUDINAL_ANNOTATION)) {
-            doAnnotations(
-                (FLYSAnnotation) 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;
-        }
-    }
-
-    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/flys-artifacts/src/main/java/de/intevation/flys/exports/minfo/BedQualityInfoGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,18 +0,0 @@
-package de.intevation.flys.exports.minfo;
-
-import de.intevation.flys.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/flys-artifacts/src/main/java/de/intevation/flys/exports/minfo/SedimentLoadLSGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,325 +0,0 @@
-package de.intevation.flys.exports.minfo;
-
-import java.util.Arrays;
-
-import org.apache.log4j.Logger;
-import org.jfree.data.xy.XYSeries;
-import org.w3c.dom.Document;
-
-import de.intevation.artifactdatabase.state.ArtifactAndFacet;
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.access.RangeAccess;
-import de.intevation.flys.artifacts.access.SedimentLoadAccess;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.FlowVelocityData;
-import de.intevation.flys.artifacts.model.WKms;
-import de.intevation.flys.artifacts.model.minfo.BedDiffEpochResult;
-import de.intevation.flys.artifacts.model.minfo.BedDiffYearResult;
-import de.intevation.flys.exports.StyledSeriesBuilder;
-import de.intevation.flys.exports.XYChartGenerator;
-import de.intevation.flys.jfree.Bounds;
-import de.intevation.flys.jfree.DoubleBounds;
-import de.intevation.flys.jfree.FLYSAnnotation;
-import de.intevation.flys.jfree.StyledXYSeries;
-import de.intevation.flys.utils.DataUtil;
-
-
-public class SedimentLoadLSGenerator
-extends XYChartGenerator
-implements FacetTypes
-{
-    public enum YAXIS {
-        L(0),
-        D(1),
-        DW(2),
-        V(3);
-
-        protected int idx;
-
-        private YAXIS(int c) {
-            idx = c;
-        }
-    }
-    /** The logger that is used in this generator. */
-    private static Logger logger = Logger.getLogger(SedimentLoadLSGenerator.class);
-
-    public static final String I18N_CHART_TITLE = "chart.sedimentload.ls.title";
-    public static final String I18N_XAXIS_LABEL = "chart.sedimentload.ls.xaxis.label";
-    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_D_LABEL = "chart.beddifference.yaxis.label.diff";
-    public static final String I18N_YAXIS_V_LABEL =
-        "chart.flow_velocity.section.yaxis.label";
-    public final static String I18N_WDIFF_YAXIS_LABEL =
-        "chart.w_differences.yaxis.label";
-
-    public final static String I18N_WDIFF_YAXIS_LABEL_DEFAULT = "m";
-    public static final String I18N_CHART_TITLE_DEFAULT = "Sedimentfracht";
-    public static final String I18N_XAXIS_LABEL_DEFAULT = "Fluss-Km";
-    public static final String I18N_YAXIS_LABEL_DEFAULT_1 = "[t/a]";
-    public static final String I18N_YAXIS_LABEL_DEFAULT_2 = "[m\u00b3/a]";
-    public static final String I18N_YAXIS_D_LABEL_DEFAULT = "delta S [m]";
-    public static final String I18N_YAXIS_V_LABEL_DEFAULT = "Geschwindigkeit v [m/s]";
-
-    private FLYSArtifact artifact;
-
-    @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();
-        artifact = (FLYSArtifact)bundle.getArtifact();
-
-        if (facet == null) {
-            return;
-        }
-        if (getXBounds(0) != null && getDomainAxisRange() != null) {
-            logger.debug(Arrays.toString(getDomainAxisRangeFromRequest()));
-            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) {
-            FLYSArtifact artifact = (FLYSArtifact)bundle.getArtifact();
-            RangeAccess access = new RangeAccess(artifact, context);
-            context.putContextValue("startkm", access.getFrom());
-            context.putContextValue("endkm", access.getTo());
-        }
-        else if (getXBounds(0) == null && getDomainAxisRange() != null){
-            FLYSArtifact artifact = (FLYSArtifact)bundle.getArtifact();
-            RangeAccess access = new RangeAccess(artifact, context);
-            Bounds b = new DoubleBounds(access.getFrom(), access.getTo());
-            Bounds bounds =
-                calculateZoom(b, getDomainAxisRange());
-            context.putContextValue("startkm", bounds.getLower());
-            context.putContextValue("endkm", bounds.getUpper());
-        }
-        if (FacetTypes.IS.SEDIMENT_LOAD(SEDIMENT_LOAD_COARSE)) {
-            doSedimentLoadOut(
-                (double[][]) bundle.getData(context),
-                bundle,
-                attr,
-                visible);
-        }
-        else if (name.equals(FLOW_VELOCITY_TOTALCHANNEL)) {
-            doFlowVelocityTotalOut(
-                (FlowVelocityData) bundle.getData(context),
-                bundle,
-                attr,
-                visible);
-        }
-        else if (name.equals(FLOW_VELOCITY_TOTALCHANNEL_FILTERED)) {
-            doFlowVelocityTotalOut(
-                (FlowVelocityData) bundle.getData(context),
-                bundle,
-                attr,
-                visible);
-        }
-        else if (name.equals(FLOW_VELOCITY_MAINCHANNEL)) {
-            doFlowVelocityMainOut(
-                (FlowVelocityData) bundle.getData(context),
-                bundle,
-                attr,
-                visible);
-        }
-        else if (name.equals(FLOW_VELOCITY_MAINCHANNEL_FILTERED)) {
-            doFlowVelocityMainOut(
-                (FlowVelocityData) bundle.getData(context),
-                bundle,
-                attr,
-                visible);
-        }
-        else if (name.equals(BED_DIFFERENCE_YEAR)) {
-            doBedDifferenceYearOut(
-                (BedDiffYearResult) bundle.getData(context),
-                bundle,
-                attr,
-                visible);
-        }
-        else if (name.equals(BED_DIFFERENCE_YEAR_FILTERED)) {
-            doBedDifferenceYearOut(
-                (BedDiffYearResult) bundle.getData(context),
-                bundle,
-                attr,
-                visible);
-        }
-        else if (name.equals(BED_DIFFERENCE_EPOCH)) {
-            doBedDifferenceEpochOut(
-                (BedDiffEpochResult) bundle.getData(context),
-                bundle,
-                attr,
-                visible);
-        }
-        else if (name.equals(W_DIFFERENCES)) {
-            doWDifferencesOut(
-                (WKms) bundle.getData(context),
-                bundle,
-                attr,
-                visible);
-        }
-        else if (name.equals(LONGITUDINAL_ANNOTATION)) {
-            doAnnotations(
-                (FLYSAnnotation) bundle.getData(context),
-                 bundle,
-                 attr,
-                 visible);
-        }
-        else if (FacetTypes.IS.MANUALPOINTS(name)) {
-            doPoints(
-                bundle.getData(context),
-                bundle,
-                attr,
-                visible,
-                YAXIS.L.idx);
-        }
-    }
-
-    @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) {
-        String label = "default";
-        if (pos == YAXIS.L.idx) {
-            SedimentLoadAccess access = new SedimentLoadAccess(artifact, context);
-            if (access.getUnit().equals("m3_per_a")) {
-                label = msg(I18N_YAXIS_LABEL_2, I18N_YAXIS_LABEL_DEFAULT_2);
-            }
-            else {
-                label = msg(I18N_YAXIS_LABEL_1, I18N_YAXIS_LABEL_DEFAULT_1);
-            }
-        }
-        else if (pos == YAXIS.V.idx) {
-            label = msg(I18N_YAXIS_V_LABEL, I18N_YAXIS_V_LABEL_DEFAULT);
-        }
-        else if (pos == YAXIS.D.idx) {
-            label = msg(I18N_YAXIS_D_LABEL, I18N_YAXIS_D_LABEL_DEFAULT);
-        }
-        else if (pos == YAXIS.DW.idx) {
-            label = msg(I18N_WDIFF_YAXIS_LABEL, I18N_WDIFF_YAXIS_LABEL_DEFAULT);
-        }
-
-        return label;
-    }
-
-    protected void doSedimentLoadOut(double[][] data,
-        ArtifactAndFacet aandf, Document theme, boolean visible) {
-
-        // Allow for gaps (NaNs).
-        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), false, theme);
-        StyledSeriesBuilder.addPoints(series, data, false);
-
-        addAxisSeries(series, YAXIS.L.idx, visible);
-    }
-
-    protected void doFlowVelocityMainOut(
-        FlowVelocityData data,
-        ArtifactAndFacet aandf,
-        Document         theme,
-        boolean          visible
-    ) {
-        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
-        StyledSeriesBuilder.addPoints(series, data.getMainChannelPoints(), true);
-
-        addAxisSeries(series, YAXIS.V.idx, visible);
-    }
-
-    protected void doFlowVelocityTotalOut(
-        FlowVelocityData data,
-        ArtifactAndFacet aandf,
-        Document         theme,
-        boolean          visible
-    ) {
-        if (data == null) {
-            logger.warn("No data to add to FlowVelocity chart.");
-            return;
-         }
-
-        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
-        StyledSeriesBuilder.addPoints(series, data.getTotalChannelPoints(), true);
-
-        addAxisSeries(series, YAXIS.V.idx, visible);
-    }
-
-    protected void doBedDifferenceYearOut(
-        BedDiffYearResult data,
-        ArtifactAndFacet aandf,
-        Document theme,
-        boolean visible
-    ) {
-        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
-        StyledSeriesBuilder.addPoints(series, data.getDifferencesData(), true);
-
-        addAxisSeries(series, YAXIS.D.idx, visible);
-    }
-
-    protected void doBedDifferenceEpochOut(
-        BedDiffEpochResult data,
-        ArtifactAndFacet aandf,
-        Document theme,
-        boolean visible
-    ) {
-        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
-        StyledSeriesBuilder.addPoints(series, data.getDifferencesData(), true);
-
-        addAxisSeries(series, YAXIS.D.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);
-        }
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/minfo/SedimentLoadLSInfoGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,12 +0,0 @@
-package de.intevation.flys.exports.minfo;
-
-import de.intevation.flys.exports.ChartInfoGenerator;
-
-
-public class SedimentLoadLSInfoGenerator
-extends ChartInfoGenerator
-{
-   public SedimentLoadLSInfoGenerator() {
-        super(new SedimentLoadLSGenerator());
-   }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/process/BedheightProcessor.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,79 +0,0 @@
-package de.intevation.flys.exports.process;
-
-import java.util.List;
-
-import org.apache.log4j.Logger;
-import org.jfree.data.xy.XYSeries;
-import org.w3c.dom.Document;
-
-import de.intevation.artifactdatabase.state.ArtifactAndFacet;
-import de.intevation.artifacts.CallContext;
-import de.intevation.flys.artifacts.model.minfo.BedHeightSingle;
-import de.intevation.flys.exports.XYChartGenerator;
-import de.intevation.flys.jfree.StyledXYSeries;
-import de.intevation.flys.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);
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/process/KMIndexProcessor.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,142 +0,0 @@
-package de.intevation.flys.exports.process;
-
-import org.apache.log4j.Logger;
-import org.jfree.data.xy.XYSeries;
-import org.jfree.data.xy.XYSeriesCollection;
-import org.w3c.dom.Document;
-
-import de.intevation.artifactdatabase.state.ArtifactAndFacet;
-import de.intevation.artifacts.CallContext;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.fixings.AnalysisPeriod;
-import de.intevation.flys.artifacts.model.fixings.QWD;
-import de.intevation.flys.exports.XYChartGenerator;
-import de.intevation.flys.jfree.StyledXYSeries;
-import de.intevation.flys.utils.KMIndex;
-
-public class KMIndexProcessor implements Processor {
-
-    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();
-        if (facettype.contains(FacetTypes.FIX_SECTOR_AVERAGE_LS)) {
-            doSectorAverageOut(generator, aandf, theme, visible, index);
-        }
-        else if (facettype.equals(FacetTypes.FIX_REFERENCE_EVENTS_LS)) {
-            doReferenceEventsOut(generator, aandf, theme, visible, index);
-        }
-        else if (facettype.equals(FacetTypes.FIX_ANALYSIS_EVENTS_LS)) {
-            doAnalysisEventsOut(generator, aandf, theme, visible, index);
-        }
-
-    }
-
-    @Override
-    public boolean canHandle(String facettype) {
-        if (facettype == null) {
-            return false;
-        }
-
-        if (facettype.contains(FacetTypes.FIX_SECTOR_AVERAGE_LS)
-                || facettype.equals(FacetTypes.FIX_REFERENCE_EVENTS_LS)
-                || facettype.equals(FacetTypes.FIX_ANALYSIS_EVENTS_LS))
-        {
-            return true;
-        }
-        return false;
-    }
-
-    private void doSectorAverageOut(XYChartGenerator generator, ArtifactAndFacet aaf,
-            Document doc, boolean visible, int idx) {
-        logger.debug("doSectorAverageOut" + aaf.getFacet().getIndex());
-
-        CallContext context = generator.getCallContext();
-        int index = aaf.getFacet().getIndex();
-        int sectorNdx = index & 3;
-
-        @SuppressWarnings("unchecked")
-        KMIndex<AnalysisPeriod> kms =
-                (KMIndex<AnalysisPeriod>)aaf.getData(context);
-
-        if(kms == null) {
-            return;
-        }
-
-        XYSeries series = new StyledXYSeries(aaf.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, idx, visible);
-    }
-
-    private void doReferenceEventsOut(XYChartGenerator generator,
-            ArtifactAndFacet aaf, Document doc, boolean visible, int idx) {
-        logger.debug("doReferenceEventOut");
-
-        CallContext context = generator.getCallContext();
-
-        @SuppressWarnings("unchecked")
-        KMIndex<QWD> kms =
-                (KMIndex<QWD>)aaf.getData(context);
-
-        if(kms == null) {
-            return;
-        }
-
-        XYSeriesCollection col = new XYSeriesCollection();
-
-        StyledXYSeries series = new StyledXYSeries(aaf.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, idx, visible);
-    }
-
-    private void doAnalysisEventsOut(XYChartGenerator generator,
-            ArtifactAndFacet aaf, Document doc, boolean visible, int idx) {
-        logger.debug("doAnalysisEventsOut");
-
-        CallContext context = generator.getCallContext();
-
-        @SuppressWarnings("unchecked")
-        KMIndex<QWD> kms =
-                (KMIndex<QWD>)aaf.getData(context);
-
-        if(kms == null) {
-            return;
-        }
-
-        XYSeriesCollection col = new XYSeriesCollection();
-
-        StyledXYSeries series = new StyledXYSeries(aaf.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, idx, visible);
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/process/Processor.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-package de.intevation.flys.exports.process;
-
-import org.w3c.dom.Document;
-
-import de.intevation.artifactdatabase.state.ArtifactAndFacet;
-import de.intevation.flys.exports.XYChartGenerator;
-
-/**
- * A processor is intended to generate an output e.g. curve in a chart diagramm from
- * arbitrary data input which can be reused in several generators.
- *
- * @author <a href="mailto:bjoern.ricks@intevation.de">Björn Ricks</a>
- */
-public interface Processor {
-
-    /**
-     * 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
-     */
-    public void doOut(
-            XYChartGenerator generator,
-            ArtifactAndFacet    aandf,
-            Document             theme,
-            boolean               visible,
-            int                         index);
-
-    /**
-     * 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
-     */
-    public boolean canHandle(String facettype);
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/process/WOutProcessor.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,116 +0,0 @@
-package de.intevation.flys.exports.process;
-
-import org.apache.log4j.Logger;
-import org.jfree.data.xy.XYSeries;
-import org.w3c.dom.Document;
-
-import de.intevation.artifactdatabase.state.ArtifactAndFacet;
-import de.intevation.artifacts.CallContext;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.WKms;
-import de.intevation.flys.exports.StyledSeriesBuilder;
-import de.intevation.flys.exports.XYChartGenerator;
-import de.intevation.flys.jfree.StyledAreaSeriesCollection;
-import de.intevation.flys.jfree.StyledXYSeries;
-import de.intevation.flys.utils.DataUtil;
-import de.intevation.flys.utils.ThemeUtil;
-
-/**
- * Add data to chart/generator.
- *
- * @author <a href="mailto:bjoern.ricks@intevation.de">Björn Ricks</a>
- */
-public class WOutProcessor implements Processor {
-
-    /** Private logger. */
-    private static final Logger logger =
-            Logger.getLogger(WOutProcessor.class);
-
-    @Override
-    public void doOut(
-            XYChartGenerator generator,
-            ArtifactAndFacet aaf,
-            Document         theme,
-            boolean          visible,
-            int              index)
-    {
-        CallContext context = generator.getCallContext();
-        WKms wkms = (WKms) aaf.getData(context);
-
-        logger.debug("doOut");
-
-        XYSeries series = new StyledXYSeries(aaf.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);
-        if (bandWidth > 0 ) {
-            XYSeries seriesDown = new StyledXYSeries(
-                "band " + aaf.getFacetDescription(), false, theme);
-            XYSeries seriesUp = new StyledXYSeries(
-                aaf.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, index, visible);
-        }
-
-        invertAxis(generator, wkms);
-    }
-
-    /**
-     * Returns true if facettype is longitutinal_section.w .
-     */
-    @Override
-    public boolean canHandle(String facetType) {
-        if (facetType == null) {
-            return false;
-        }
-
-        if (facetType.equals(FacetTypes.LONGITUDINAL_W)
-                || facetType.equals(FacetTypes.STATIC_WKMS)
-                || facetType.equals(FacetTypes.HEIGHTMARKS_POINTS)
-                || facetType.equals(FacetTypes.STATIC_WQKMS)
-                || facetType.equals(FacetTypes.STATIC_WQKMS_W)
-                || facetType.equals(FacetTypes.DISCHARGE_LONGITUDINAL_W))
-        {
-            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());
-        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);
-        }
-        generator.setInverted(inv);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/sq/SQOverviewGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,238 +0,0 @@
-package de.intevation.flys.exports.sq;
-
-import java.awt.image.BufferedImage;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.imageio.ImageIO;
-import javax.xml.xpath.XPathConstants;
-
-import org.apache.log4j.Logger;
-import org.jfree.chart.ChartRenderingInfo;
-import org.jfree.chart.JFreeChart;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-
-import de.intevation.artifactdatabase.state.ArtifactAndFacet;
-import de.intevation.artifactdatabase.state.Settings;
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.ArtifactDatabaseException;
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.common.ArtifactNamespaceContext;
-import de.intevation.artifacts.common.utils.XMLUtils;
-import de.intevation.flys.artifacts.context.FLYSContext;
-import de.intevation.flys.collections.FLYSArtifactCollection;
-import de.intevation.flys.exports.ChartGenerator;
-import de.intevation.flys.exports.OutGenerator;
-import de.intevation.flys.exports.OutputHelper;
-
-public class SQOverviewGenerator
-implements OutGenerator
-{
-    private static Logger logger = Logger.getLogger(SQOverviewGenerator.class);
-
-    public static final String XPATH_CHART_SIZE =
-        "/art:action/art:attributes/art:size";
-
-    protected FLYSArtifactCollection collection;
-
-    protected Artifact master;
-
-    protected Settings settings;
-
-    protected Document request;
-
-    protected OutputStream out;
-
-    protected CallContext context;
-
-    protected List<JFreeChart> charts;
-
-    /**
-     * Produce output.
-     * @param artifactAndFacet current facet and artifact.
-     * @param attr  theme for facet
-     */
-    @Override
-    public void doOut(
-        ArtifactAndFacet artifactAndFacet,
-        Document         attr,
-        boolean          visible
-    ) {
-        logger.debug("doOut()");
-
-        String name = artifactAndFacet.getData(context).toString();
-        if(name != null) {
-            logger.debug("name: " + name);
-            ChartGenerator g =
-                (ChartGenerator)FLYSContext.getOutGenerator(
-                    context,
-                    name,
-                    null);
-
-            if (g == null) {
-                logger.debug("generator is null.");
-                return;
-            }
-
-            OutputHelper helper = new OutputHelper(master.identifier());
-            Document collectionAttribute = collection.getAttribute();
-
-            try {
-                Document cAttr = getAttribute(context, collectionAttribute, name);
-                g.init(request, out, context);
-
-                helper.doOut(g, name, name, cAttr, context);
-                JFreeChart chart = g.generateChart();
-                chart.removeLegend();
-                charts.add(chart);
-            }
-            catch (IOException e) {
-                logger.warn(e);
-            }
-            catch (ArtifactDatabaseException e) {
-                logger.warn(e);
-            }
-        }
-    }
-
-    @Override
-    public void init(Document request, OutputStream out, CallContext context) {
-        this.request = request;
-        this.out = out;
-        this.context = context;
-        charts = new ArrayList<JFreeChart>();
-    }
-
-    @Override
-    public void setMasterArtifact(Artifact master) {
-        this.master = master;
-    }
-
-    @Override
-    public void setCollection(FLYSArtifactCollection collection) {
-        this.collection = collection;
-    }
-
-    @Override
-    public void generate() throws IOException {
-        logger.debug("SQOverviewGenerator.generate");
-
-        int[] size = getSize();
-
-        if (size == null) {
-            size = new int[] {400, 600};
-        }
-        BufferedImage result =
-            new BufferedImage(size[0], size[1], BufferedImage.TYPE_INT_RGB);
-        for (int i = 0; i < charts.size(); i++) {
-            logger.debug("index: " + i);
-            JFreeChart chart = charts.get(i);
-            ChartRenderingInfo info = new ChartRenderingInfo();
-            BufferedImage img =
-                chart.createBufferedImage(size[0]/2, size[1]/3, info);
-            int horPos = 0;
-            int vertPos = 0;
-            if (i % 2 == 1) {
-                horPos = size[0]/2;
-            }
-            if (i > 1) {
-                vertPos = (size[1] / 3) * (i / 2);
-            }
-            result.createGraphics().drawImage(img, horPos, vertPos, null);
-        }
-        ImageIO.write(result, "png", out);
-    }
-
-    @Override
-    public void setSettings(Settings settings) {
-        this.settings = settings;
-    }
-
-    @Override
-    public Settings getSettings() {
-        return this.settings;
-    }
-
-
-    /**
-     * Returns the "attribute" (part of description document) for a specific
-     * output type.
-     *
-     * @param context The CallContext object.
-     * @param attr The xml attribute saved at the collection.
-     * @param output The name of the desired output type.
-     *
-     * @return the attribute for the desired output type.
-     */
-    protected Document getAttribute(
-        CallContext context,
-        Document    attr,
-        String      output)
-    throws    ArtifactDatabaseException
-    {
-        logger.debug("find specific XML node for Output: " + output);
-
-        Map<String, String> vars = new HashMap<String, String>();
-        vars.put("output", output);
-
-        Node out = (Node) XMLUtils.xpath(
-            attr,
-            "art:attribute/art:outputs/art:output[@name=$output]",
-            XPathConstants.NODE,
-            ArtifactNamespaceContext.INSTANCE,
-            vars);
-
-        if (out != null) {
-            Document o = XMLUtils.newDocument();
-            o.appendChild(o.importNode(out, true));
-
-            return o;
-        }
-
-        return null;
-    }
-
-
-    /**
-     * 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;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/sq/SQRelationExporter.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,162 +0,0 @@
-package de.intevation.flys.exports.sq;
-
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.w3c.dom.Document;
-
-import au.com.bytecode.opencsv.CSVWriter;
-
-import de.intevation.artifacts.CallContext;
-
-import de.intevation.flys.artifacts.model.CalculationResult;
-
-import de.intevation.flys.artifacts.model.sq.SQFractionResult;
-import de.intevation.flys.artifacts.model.sq.SQResult;
-import de.intevation.flys.artifacts.model.Parameters;
-
-import de.intevation.flys.exports.AbstractExporter;
-
-import org.apache.log4j.Logger;
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class SQRelationExporter extends AbstractExporter {
-
-    /** Private logger. */
-    private static final Logger logger =
-        Logger.getLogger(SQRelationExporter.class);
-
-
-    public static final String CSV_PARAMETER =
-        "export.sqrelation.csv.header.parameter";
-
-    public static final String CSV_STATION =
-        "export.sqrelation.csv.header.station";
-
-    public static final String CSV_KM =
-        "export.sqrelation.csv.header.km";
-
-    public static final String CSV_FUNCTION =
-        "export.sqrelation.csv.header.function";
-
-    public static final String CSV_GAUGE =
-        "export.sqrelation.csv.header.gauge";
-
-    public static final String CSV_COEFF_A =
-        "export.sqrelation.csv.header.coeff.a";
-
-    public static final String CSV_COEFF_B =
-        "export.sqrelation.csv.header.coeff.b";
-
-    public static final String CSV_COEFF_Q =
-        "export.sqrelation.csv.header.coeff.q";
-
-    public static final String CSV_COEFF_R =
-        "export.sqrelation.csv.header.coeff.r";
-
-    public static final String CSV_N_TOTAL =
-        "export.sqrelation.csv.header.n.total";
-
-    public static final String CSV_N_OUTLIERS =
-        "export.sqrelation.csv.header.n.outliers";
-
-    public static final String CSV_C_DUAN =
-        "export.sqrelation.csv.header.c.duan";
-
-    public static final String CSV_C_FERGUSON =
-        "export.sqrelation.csv.header.c.ferguson";
-
-    public static final String CSV_VARIANCE =
-        "export.sqrelation.csv.header.variance";
-
-
-    protected List<SQResult []> data;
-
-
-    public void init(Document request, OutputStream out, CallContext cc) {
-        super.init(request, out, cc);
-        data = new ArrayList<SQResult []>();
-    }
-
-
-    @Override
-    protected void addData(Object d) {
-        if (d instanceof CalculationResult) {
-            d = ((CalculationResult)d).getData();
-            if (d instanceof SQResult []) {
-                data.add((SQResult [])d);
-            }
-        }
-    }
-
-    protected void writeCSVHeader(CSVWriter writer) {
-        writer.writeNext(new String[] {
-            msg(CSV_KM        , CSV_KM),
-            msg(CSV_PARAMETER,  CSV_PARAMETER),
-            msg(CSV_COEFF_A   , CSV_COEFF_A),
-            msg(CSV_COEFF_B   , CSV_COEFF_B),
-            msg(CSV_N_TOTAL   , CSV_N_TOTAL),
-            msg(CSV_N_OUTLIERS, CSV_N_OUTLIERS),
-            msg(CSV_VARIANCE  , CSV_VARIANCE)
-        });
-    }
-
-    @Override
-    protected void writeCSVData(CSVWriter writer) {
-        logger.debug("writeCSVData");
-
-        writeCSVHeader(writer);
-
-        for (SQResult [] results: data) {
-            for (SQResult result: results) {
-                data2CSV(writer, result);
-            }
-        }
-    }
-
-    protected void data2CSV(CSVWriter writer, SQResult result) {
-        logger.debug("data2CSV");
-
-        // TODO: i18n
-        String km = String.valueOf(result.getKm());
-
-        for (int i = 0; i < SQResult.NUMBER_FRACTIONS; ++i) {
-            SQFractionResult fraction = result.getFraction(i);
-
-            String name = result.getFractionName(i);
-
-            Parameters parameters = fraction.getParameters();
-
-            if (parameters == null) {
-                continue;
-            }
-
-            double a  = parameters.getValue(0, "a");
-            double b  = parameters.getValue(0, "b");
-            double sd = Math.sqrt(parameters.getValue(0, "std_dev"));
-            int    o  = fraction.totalNumOutliers();
-            int    t  = fraction.numMeasurements() + o;
-
-            writer.writeNext(new String[] {
-                km,
-                name,
-                String.valueOf(a),
-                String.valueOf(b),
-                String.valueOf(t),
-                String.valueOf(o),
-                String.valueOf(sd)
-            });
-        }
-    }
-
-
-    @Override
-    protected void writePDF(OutputStream out) {
-        logger.debug("writePDF");
-        logger.error("NOT IMPLEMENTED: writePDF");
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/sq/SQRelationGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,209 +0,0 @@
-package de.intevation.flys.exports.sq;
-
-import de.intevation.artifactdatabase.state.ArtifactAndFacet;
-import de.intevation.artifactdatabase.state.Facet;
-
-import de.intevation.flys.artifacts.model.FacetTypes;
-
-import de.intevation.flys.artifacts.model.sq.SQ;
-import de.intevation.flys.artifacts.model.sq.SQFunction;
-
-import de.intevation.flys.exports.XYChartGenerator;
-
-import de.intevation.flys.jfree.JFreeUtil;
-import de.intevation.flys.jfree.StyledXYSeries;
-
-import org.apache.log4j.Logger;
-
-import org.jfree.chart.axis.LogarithmicAxis;
-import org.jfree.chart.axis.NumberAxis;
-
-import org.jfree.data.xy.XYSeries;
-
-import org.w3c.dom.Document;
-
-/**
- * An OutGenerator that generates charts for MINFO sq relation.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class SQRelationGenerator
-extends      XYChartGenerator
-implements   FacetTypes
-{
-    public enum YAXIS {
-        S(0);
-        protected int idx;
-        private YAXIS(int c) {
-           idx = c;
-        }
-    }
-
-
-    public static final String I18N_XAXIS_LABEL =
-        "chart.sq_relation.xaxis.label";
-
-    public static final String I18N_YAXIS_LABEL =
-        "chart.sq_relation.yaxis.label";
-
-
-    /** The logger that is used in this generator. */
-    private static Logger logger = Logger.getLogger(SQRelationGenerator.class);
-
-
-    @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 String getDefaultChartTitle() {
-        return "TODO: CHART TITLE";
-    }
-
-
-    @Override
-    protected String getDefaultXAxisLabel() {
-        return msg(I18N_XAXIS_LABEL, I18N_XAXIS_LABEL);
-    }
-
-
-    @Override
-    protected String getDefaultYAxisLabel(int index) {
-        return msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL);
-    }
-
-
-    @Override
-    protected NumberAxis createXAxis(String label) {
-        return new LogarithmicAxis(label);
-    }
-
-
-    @Override
-    protected NumberAxis createYAxis(int index) {
-        return new LogarithmicAxis(getDefaultYAxisLabel(index));
-    }
-
-
-    @Override
-    public void doOut(
-        ArtifactAndFacet artifactAndFacet,
-        Document         attr,
-        boolean          visible
-    ) {
-        logger.debug("doOut");
-
-        Facet  facet = artifactAndFacet.getFacet();
-        String name  = facet != null ? facet.getName() : null;
-
-        if (name == null || name.length() == 0) {
-            logger.warn("Invalid facet with no name given!");
-            return;
-        }
-
-        if (IS.SQ_CURVE(name)) {
-            doSQCurveOut(artifactAndFacet, attr, visible);
-        }
-        else if (IS.SQ_MEASUREMENT(name)) {
-            doSQOut(artifactAndFacet, attr, visible);
-        }
-        else if (IS.SQ_OUTLIER(name)) {
-            doSQOut(artifactAndFacet, attr, visible);
-        }
-        else if (IS.MANUALPOINTS(name)) {
-            doPoints(
-                artifactAndFacet.getData(context),
-                artifactAndFacet,
-                attr,
-                visible,
-                YAXIS.S.idx);
-        }
-    }
-
-
-    protected void doSQCurveOut(
-        ArtifactAndFacet artifactAndFacet,
-        Document         attr,
-        boolean          visible
-    ) {
-        String desc = artifactAndFacet.getFacetDescription();
-        logger.debug("doSQCurveOut: " + desc);
-
-        SQFunction func = (SQFunction) artifactAndFacet.getData(context);
-
-        if (func == null) {
-            return;
-        }
-
-        XYSeries series = JFreeUtil.sampleFunction2DPositive(
-            func.getFunction(),
-            attr,
-            desc,
-            500,
-            Math.max(func.getMinQ(), 0.01),
-            Math.max(func.getMaxQ(), 0.02));
-
-        if (logger.isDebugEnabled()) {
-            logger.debug("Series '" + desc + "' has "
-                + series.getItemCount() + " items.");
-
-            logger.debug("   -> min x = " + series.getMinX());
-            logger.debug("   -> max x = " + series.getMaxX());
-            logger.debug("   -> min y = " + series.getMinY());
-            logger.debug("   -> max y = " + series.getMaxY());
-        }
-
-        addAxisSeries(series, YAXIS.S.idx, visible);
-    }
-
-
-    protected void doSQOut(
-        ArtifactAndFacet artifactAndFacet,
-        Document         attr,
-        boolean          visible
-    ) {
-        String desc = artifactAndFacet.getFacetDescription();
-        logger.debug("doSQOut: " + desc);
-
-        SQ[]     sqs    = (SQ[]) artifactAndFacet.getData(context);
-        if (sqs == null) {
-            logger.debug("No SQs found for facet");
-            return;
-        }
-        XYSeries series = new StyledXYSeries(desc, attr);
-
-        for (SQ sq: sqs) {
-            double q = sq.getQ();
-            double s = sq.getS();
-            if (s > 0d && q > 0d) {
-                series.add(q, s, false);
-            }
-        }
-
-        if (logger.isDebugEnabled()) {
-            logger.debug("Series '" + desc + "' has "
-                + series.getItemCount() + " items.");
-
-            logger.debug("   -> min x = " + series.getMinX());
-            logger.debug("   -> max x = " + series.getMaxX());
-            logger.debug("   -> min y = " + series.getMinY());
-            logger.debug("   -> max y = " + series.getMaxY());
-        }
-
-        addAxisSeries(series, YAXIS.S.idx, visible);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/sq/SQRelationGeneratorA.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@
-package de.intevation.flys.exports.sq;
-
-
-public class SQRelationGeneratorA extends SQRelationGenerator {
-
-    public static final String I18N_CHART_TITLE =
-        "chart.sq_relation_a.title";
-
-
-    @Override
-    public String getDefaultChartTitle() {
-        return msg(I18N_CHART_TITLE, I18N_CHART_TITLE);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/sq/SQRelationGeneratorB.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@
-package de.intevation.flys.exports.sq;
-
-
-public class SQRelationGeneratorB extends SQRelationGenerator {
-
-    public static final String I18N_CHART_TITLE =
-        "chart.sq_relation_b.title";
-
-
-    @Override
-    public String getDefaultChartTitle() {
-        return msg(I18N_CHART_TITLE, I18N_CHART_TITLE);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/sq/SQRelationGeneratorC.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@
-package de.intevation.flys.exports.sq;
-
-
-public class SQRelationGeneratorC extends SQRelationGenerator {
-
-    public static final String I18N_CHART_TITLE =
-        "chart.sq_relation_c.title";
-
-
-    @Override
-    public String getDefaultChartTitle() {
-        return msg(I18N_CHART_TITLE, I18N_CHART_TITLE);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/sq/SQRelationGeneratorD.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@
-package de.intevation.flys.exports.sq;
-
-
-public class SQRelationGeneratorD extends SQRelationGenerator {
-
-    public static final String I18N_CHART_TITLE =
-        "chart.sq_relation_d.title";
-
-
-    @Override
-    public String getDefaultChartTitle() {
-        return msg(I18N_CHART_TITLE, I18N_CHART_TITLE);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/sq/SQRelationGeneratorE.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@
-package de.intevation.flys.exports.sq;
-
-
-public class SQRelationGeneratorE extends SQRelationGenerator {
-
-    public static final String I18N_CHART_TITLE =
-        "chart.sq_relation_e.title";
-
-
-    @Override
-    public String getDefaultChartTitle() {
-        return msg(I18N_CHART_TITLE, I18N_CHART_TITLE);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/sq/SQRelationGeneratorF.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@
-package de.intevation.flys.exports.sq;
-
-
-public class SQRelationGeneratorF extends SQRelationGenerator {
-
-    public static final String I18N_CHART_TITLE =
-        "chart.sq_relation_f.title";
-
-
-    @Override
-    public String getDefaultChartTitle() {
-        return msg(I18N_CHART_TITLE, I18N_CHART_TITLE);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/sq/SQRelationInfoGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,19 +0,0 @@
-package de.intevation.flys.exports.sq;
-
-import de.intevation.flys.exports.ChartInfoGenerator;
-
-
-/**
- * A ChartInfoGenerator that generates meta information for specific
- * sq relation charts.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class SQRelationInfoGenerator
-extends      ChartInfoGenerator
-{
-    public SQRelationInfoGenerator() {
-        super(new SQRelationGenerator());
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/java2d/NOPGraphics2D.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,528 +0,0 @@
-package de.intevation.flys.java2d;
-
-import java.util.Map;
-
-import java.awt.Graphics;
-import java.awt.Graphics2D;
-import java.awt.Shape;
-import java.awt.Image;
-import java.awt.Color;
-import java.awt.Composite;
-import java.awt.Paint;
-import java.awt.GraphicsConfiguration;
-import java.awt.Stroke;
-import java.awt.Rectangle;
-import java.awt.Font;
-import java.awt.FontMetrics;
-
-import java.awt.image.RenderedImage;
-
-import java.awt.image.BufferedImage;
-import java.awt.image.BufferedImageOp;
-import java.awt.image.ImageObserver;
-
-import java.awt.image.renderable.RenderableImage;
-
-import java.awt.geom.AffineTransform;
-
-import java.text.AttributedCharacterIterator;
-
-import java.awt.font.GlyphVector;
-import java.awt.font.FontRenderContext;
-
-import java.awt.RenderingHints;
-
-public final class NOPGraphics2D
-extends            Graphics2D
-{
-    private Graphics2D parent;
-
-    public NOPGraphics2D(Graphics2D parent) {
-        this.parent = parent;
-    }
-
-    @Override
-    public final void addRenderingHints(Map<?,?> hints) {
-        parent.addRenderingHints(hints);
-    }
-
-    @Override
-    public final void clip(Shape s) {
-    }
-
-    @Override
-    public final void draw(Shape s) {
-    }
-
-    @Override
-    public final void drawGlyphVector(GlyphVector g, float x, float y) {
-    }
-
-    @Override
-    public final void drawImage(
-        BufferedImage   img,
-        BufferedImageOp op,
-        int x,
-        int y
-    ) {
-    }
-
-    @Override
-    public final boolean drawImage(
-        Image           img,
-        AffineTransform xform,
-        ImageObserver   obs
-    ) {
-        return true;
-    }
-
-    @Override
-    public final void drawRenderableImage(
-        RenderableImage img,
-        AffineTransform xform
-    ) {
-    }
-
-    @Override
-    public final void drawRenderedImage(
-        RenderedImage   img,
-        AffineTransform xform
-    ) {
-    }
-
-    @Override
-    public final void drawString(
-        AttributedCharacterIterator iterator,
-        float x,
-        float y
-    ) {
-    }
-
-    @Override
-    public final void drawString(
-        AttributedCharacterIterator iterator,
-        int x,
-        int y
-    ) {
-    }
-
-    @Override
-    public final void drawString(String str, float x, float y) {
-    }
-
-    @Override
-    public final void drawString(String str, int x, int y) {
-    }
-
-    @Override
-    public final void fill(Shape s) {
-    }
-
-    @Override
-    public final Color getBackground() {
-        return parent.getBackground();
-    }
-
-    @Override
-    public final Composite getComposite() {
-        return parent.getComposite();
-    }
-
-    @Override
-    public final GraphicsConfiguration getDeviceConfiguration() {
-        return parent.getDeviceConfiguration();
-    }
-
-    @Override
-    public final FontRenderContext getFontRenderContext() {
-        return parent.getFontRenderContext();
-    }
-
-    @Override
-    public final Paint getPaint() {
-        return parent.getPaint();
-    }
-
-    @Override
-    public final Object getRenderingHint(RenderingHints.Key hintKey) {
-        return parent.getRenderingHint(hintKey);
-    }
-
-    @Override
-    public final RenderingHints getRenderingHints() {
-        return parent.getRenderingHints();
-    }
-
-    @Override
-    public final Stroke getStroke() {
-        return parent.getStroke();
-    }
-
-    @Override
-    public final AffineTransform getTransform() {
-        return parent.getTransform();
-    }
-
-    @Override
-    public final boolean hit(Rectangle rect, Shape s, boolean onStroke) {
-        return parent.hit(rect, s, onStroke);
-    }
-
-    @Override
-    public final void rotate(double theta) {
-        parent.rotate(theta);
-    }
-
-    @Override
-    public final void rotate(double theta, double x, double y) {
-        parent.rotate(theta);
-    }
-
-    @Override
-    public final void scale(double sx, double sy) {
-        parent.scale(sx, sy);
-    }
-
-    @Override
-    public final void setBackground(Color color) {
-        parent.setBackground(color);
-    }
-
-    @Override
-    public final void setComposite(Composite comp) {
-        parent.setComposite(comp);
-    }
-
-    @Override
-    public final void setPaint(Paint paint) {
-        parent.setPaint(paint);
-    }
-
-    @Override
-    public final void setRenderingHint(
-        RenderingHints.Key hintKey,
-        Object             hintValue
-    ) {
-        parent.setRenderingHint(hintKey, hintValue);
-    }
-
-    @Override
-    public final void setRenderingHints(Map<?,?> hints) {
-        parent.setRenderingHints(hints);
-    }
-
-    @Override
-    public final void setStroke(Stroke s) {
-        parent.setStroke(s);
-    }
-
-    @Override
-    public final void setTransform(AffineTransform Tx) {
-        parent.setTransform(Tx);
-    }
-
-
-    @Override
-    public final void shear(double shx, double shy) {
-        parent.shear(shx, shy);
-    }
-
-    @Override
-    public final void transform(AffineTransform Tx) {
-        parent.transform(Tx);
-    }
-
-    @Override
-    public final void translate(double tx, double ty) {
-        parent.translate(tx, ty);
-    }
-
-    @Override
-    public final void translate(int tx, int ty) {
-        parent.translate(tx, ty);
-    }
-
-    @Override
-    public final void dispose() {
-        parent.dispose();
-    }
-
-    @Override
-    public final boolean drawImage(
-        Image img,
-        int x,
-        int y,
-        int width,
-        int height,
-        Color bgcolor,
-        ImageObserver observer
-    ) {
-        return true;
-    }
-
-    @Override
-    public final boolean drawImage(
-        Image img,
-        int dx1,
-        int dy1,
-        int dx2,
-        int dy2,
-        int sx1,
-        int sy1,
-        int sx2,
-        int sy2,
-        Color bgcolor,
-        ImageObserver observer
-    ) {
-        return true;
-    }
-
-    @Override
-    public final boolean drawImage(
-        Image img,
-        int dx1,
-        int dy1,
-        int dx2,
-        int dy2,
-        int sx1,
-        int sy1,
-        int sx2,
-        int sy2,
-        ImageObserver observer
-    ) {
-        return true;
-    }
-
-    @Override
-    public final boolean drawImage(
-        Image img,
-        int x,
-        int y,
-        Color bgcolor,
-        ImageObserver observer
-    ) {
-        return true;
-    }
-
-    @Override
-    public final boolean drawImage(
-        Image img,
-        int x,
-        int y,
-        int width,
-        int height,
-        ImageObserver observer
-    ) {
-        return true;
-    }
-
-    @Override
-    public final boolean drawImage(
-        Image img,
-        int x,
-        int y,
-        ImageObserver observer
-    ) {
-        return true;
-    }
-
-    @Override
-    public final void fillPolygon(
-        int [] xPoints,
-        int [] yPoints,
-        int    nPoints
-    ) {
-    }
-
-    @Override
-    public final void drawPolygon(
-        int [] xPoints,
-        int [] yPoints,
-        int    nPoints
-    ) {
-    }
-
-    @Override
-    public final void drawPolyline(
-        int [] xPoints,
-        int [] yPoints,
-        int    nPoints
-    ) {
-    }
-
-    @Override
-    public final void fillArc(
-        int x,
-        int y,
-        int width,
-        int height,
-        int startAngle,
-        int arcAngle
-    ) {
-    }
-
-    @Override
-    public final void drawArc(
-        int x,
-        int y,
-        int width,
-        int height,
-        int startAngle,
-        int arcAngle
-    ) {
-    }
-
-    @Override
-    public final void fillOval(
-        int x,
-        int y,
-        int width,
-        int height
-    ) {
-    }
-
-    @Override
-    public final void drawOval(
-        int x,
-        int y,
-        int width,
-        int height
-    ) {
-    }
-
-    @Override
-    public final void fillRoundRect(
-        int x,
-        int y,
-        int width,
-        int height,
-        int arcWidth,
-        int arcHeight
-    ) {
-    }
-
-    @Override
-    public final void drawRoundRect(
-        int x,
-        int y,
-        int width,
-        int height,
-        int arcWidth,
-        int arcHeight
-    ) {
-    }
-
-    @Override
-    public final void clearRect(
-        int x,
-        int y,
-        int width,
-        int height
-    ) {
-    }
-
-    @Override
-    public final void fillRect(
-        int x,
-        int y,
-        int width,
-        int height
-    ) {
-    }
-
-    @Override
-    public final void drawLine(
-        int x1,
-        int y1,
-        int x2,
-        int y2
-    ) {
-    }
-
-    @Override
-    public final void copyArea(
-        int x,
-        int y,
-        int width,
-        int height,
-        int dx,
-        int dy
-    ) {
-    }
-
-    @Override
-    public final void setClip(
-        int x,
-        int y,
-        int width,
-        int height
-    ) {
-        parent.setClip(x, y, width, height);
-    }
-
-    @Override
-    public final void setClip(Shape shape) {
-        parent.setClip(shape);
-    }
-
-    @Override
-    public final Shape getClip() {
-        return parent.getClip();
-    }
-
-    @Override
-    public final void clipRect(
-        int x,
-        int y,
-        int width,
-        int height
-    ) {
-        parent.clipRect(x, y, width, height);
-    }
-
-    @Override
-    public final Rectangle getClipBounds() {
-        return parent.getClipBounds();
-    }
-
-    @Override
-    public final FontMetrics getFontMetrics(Font f) {
-        return parent.getFontMetrics(f);
-    }
-
-    @Override
-    public final void setFont(Font font) {
-        parent.setFont(font);
-    }
-
-    @Override
-    public final Font getFont() {
-        return parent.getFont();
-    }
-
-    @Override
-    public final void setXORMode(Color c1) {
-        parent.setXORMode(c1);
-    }
-
-    @Override
-    public final void setPaintMode() {
-        parent.setPaintMode();
-    }
-
-    @Override
-    public final void setColor(Color c) {
-        parent.setColor(c);
-    }
-
-    @Override
-    public final Color getColor() {
-        return parent.getColor();
-    }
-
-    @Override
-    public final Graphics create() {
-        return new NOPGraphics2D((Graphics2D)parent.create());
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/java2d/ShapeUtils.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,83 +0,0 @@
-package de.intevation.flys.java2d;
-
-import java.awt.Shape;
-
-import java.awt.geom.AffineTransform;
-import java.awt.geom.Ellipse2D;
-import java.awt.geom.GeneralPath;
-import java.awt.geom.Rectangle2D;
-
-import java.util.HashMap;
-import java.util.Map;
-
-public class ShapeUtils
-{
-    // TODO: Use enum
-    public static final int MEASURED     = 0;
-    public static final int DIGITIZED    = 1;
-    public static final int INTERPOLATED = 2;
-
-    public static final boolean DIGITIZED_FILL    = false;
-    public static final boolean MEASURED_FILL     = true;
-    public static final boolean INTERPOLATED_FILL = false;
-
-   public static final Shape DIGITIZED_SHAPE =
-        createCross(4f);
-
-    public static final Shape MEASURED_SHAPE =
-        new Rectangle2D.Double(-2, -2, 4, 4);
-
-    public static final Shape INTERPOLATED_SHAPE =
-        new Ellipse2D.Double(-2, -2, 4, 4);
-
-    protected static Map<Long, Shape> scaledShapesCache =
-        new HashMap<Long, Shape>();
-
-    public static final Shape createCross(float size) {
-        float half = size * 0.5f;
-        GeneralPath p = new GeneralPath();
-        p.moveTo(-half, -half);
-        p.lineTo(half, half);
-        p.closePath();
-        p.moveTo(-half, half);
-        p.lineTo(half, -half);
-        p.closePath();
-        return p;
-    }
-
-    public static Shape scale(Shape shape, float factor) {
-        if (factor == 1f) {
-            return shape;
-        }
-        AffineTransform xform =
-            AffineTransform.getScaleInstance(factor, factor);
-
-        GeneralPath gp = new GeneralPath(shape);
-        return gp.createTransformedShape(xform);
-    }
-
-    public static synchronized Shape getScaledShape(int type, float size) {
-
-        Long hash = Long.valueOf(
-            (((long)type) << 32) | Float.floatToIntBits(size));
-
-        Shape shape = scaledShapesCache.get(hash);
-
-        if (shape == null) {
-            switch (type) {
-                case MEASURED:
-                    shape = MEASURED_SHAPE;
-                    break;
-                case DIGITIZED:
-                    shape = DIGITIZED_SHAPE;
-                    break;
-                default:
-                    shape = INTERPOLATED_SHAPE;
-            }
-            scaledShapesCache.put(hash, shape = scale(shape, size));
-        }
-
-        return shape;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/jfree/Bounds.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,24 +0,0 @@
-package de.intevation.flys.jfree;
-
-import java.io.Serializable;
-
-import org.jfree.chart.axis.ValueAxis;
-
-
-/**
- * Somewhat better Ranges.
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public interface Bounds extends Serializable {
-
-    Number getLower();
-
-    Number getUpper();
-
-    void applyBounds(ValueAxis axis);
-
-    void applyBounds(ValueAxis axis, int percent);
-
-    Bounds combine(Bounds bounds);
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/jfree/CollisionFreeLineLabelEntity.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,21 +0,0 @@
-package de.intevation.flys.jfree;
-
-import java.awt.Shape;
-
-import org.jfree.chart.entity.XYAnnotationEntity;
-
-/**
- * Chart Entity for Line Labels that should not collide.
- */
-public class CollisionFreeLineLabelEntity
-extends XYAnnotationEntity {
-    public CollisionFreeLineLabelEntity(
-        Shape hotspot,
-        int rendererIndex,
-        String toolTip,
-        String url
-    ) {
-        super(hotspot, rendererIndex, toolTip, url);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/jfree/CollisionFreeXYTextAnnotation.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,140 +0,0 @@
-package de.intevation.flys.jfree;
-
-import java.awt.Shape;
-import java.awt.geom.Rectangle2D;
-
-import org.jfree.chart.entity.EntityCollection;
-import org.jfree.chart.annotations.XYTextAnnotation;
-import org.jfree.chart.axis.ValueAxis;
-import org.jfree.chart.plot.PlotOrientation;
-import org.jfree.chart.plot.XYPlot;
-import org.jfree.chart.entity.XYAnnotationEntity;
-import org.jfree.chart.plot.PlotRenderingInfo;
-import org.jfree.chart.plot.Plot;
-
-import org.jfree.text.TextUtilities;
-
-import org.jfree.ui.RectangleEdge;
-
-/**
- * Custom Annotations class that is drawn only if no collisions with other
- * already drawn CustomAnnotations in current plot are found.
- */
-public class CollisionFreeXYTextAnnotation extends XYTextAnnotation {
-
-    public CollisionFreeXYTextAnnotation(String text, double x, double y) {
-        super(text, x, y);
-    }
-
-
-    /**
-     * Draw the Annotation only if it does not collide with other
-     * already drawn Annotations- texts.
-     *
-     * @param g2            the graphics device.
-     * @param plot          the plot.
-     * @param dataArea      the data area.
-     * @param domainAxis    the domain axis.
-     * @param rangeAxis     the range axis.
-     * @param rendererIndex the render index.
-     * @param info          state information, escpecially collects info about
-     *                      already drawn shapes (and thus annotations), used
-     *                      for collision detection.
-     */
-    @Override
-    public void draw(
-        java.awt.Graphics2D g2,
-        XYPlot plot,
-        java.awt.geom.Rectangle2D dataArea,
-        ValueAxis domainAxis,
-        ValueAxis rangeAxis,
-        int rendererIndex,
-        PlotRenderingInfo info
-    ) {
-        // From superclass, adjusted access only.
-        PlotOrientation orientation = plot.getOrientation();
-        RectangleEdge domainEdge = Plot.resolveDomainAxisLocation(
-                plot.getDomainAxisLocation(), orientation);
-        RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation(
-                plot.getRangeAxisLocation(), orientation);
-
-        float anchorX = (float) domainAxis.valueToJava2D(
-                this.getX(), dataArea, domainEdge);
-        float anchorY = (float) rangeAxis.valueToJava2D(
-                this.getY(), dataArea, rangeEdge);
-
-        if (orientation == PlotOrientation.HORIZONTAL) {
-            float tempAnchor = anchorX;
-            anchorX = anchorY;
-            anchorY = tempAnchor;
-        }
-
-        g2.setFont(getFont());
-        Shape hotspot = TextUtilities.calculateRotatedStringBounds(
-                getText(), g2, anchorX, anchorY, getTextAnchor(),
-                getRotationAngle(), getRotationAnchor());
-
-        // Deviation from superclass: prevent collision.
-        if (JFreeUtil.collides(hotspot, info.getOwner().getEntityCollection(),
-            XYAnnotationEntity.class)) {
-            return;
-        }
-
-        if (this.getBackgroundPaint() != null) {
-            g2.setPaint(this.getBackgroundPaint());
-            g2.fill(hotspot);
-        }
-        g2.setPaint(getPaint());
-        TextUtilities.drawRotatedString(getText(), g2, anchorX, anchorY,
-                getTextAnchor(), getRotationAngle(), getRotationAnchor());
-        if (this.isOutlineVisible()) {
-            g2.setStroke(this.getOutlineStroke());
-            g2.setPaint(this.getOutlinePaint());
-            g2.draw(hotspot);
-        }
-
-        //String toolTip = getToolTipText();
-        //String url = getURL();
-        String toolTip = "CollisionFreeXYTextAnnotation";
-        String url     = toolTip;
-
-        if (toolTip != null || url != null) {
-            addEntity(info, hotspot, rendererIndex, toolTip, url);
-        }
-        // XXX: DEAD CODE (as long as a hard value is assigned to toolTip
-        /*
-        else {
-            addEntity(info, hotspot, rendererIndex,
-                "CollisionFreeXYTextAnnotation",
-                "CollisionFreeXYTextAnnotation");
-        }
-        */
-    }
-
-    /**
-     * A utility method for adding an {@link CollisionFreeXYAnnotationEntity} to
-     * a {@link PlotRenderingInfo} instance.
-     *
-     * @param info  the plot rendering info (<code>null</code> permitted).
-     * @param hotspot  the hotspot area.
-     * @param rendererIndex  the renderer index.
-     * @param toolTipText  the tool tip text.
-     * @param urlText  the URL text.
-     */
-    protected void addEntity(PlotRenderingInfo info,
-                             Shape hotspot, int rendererIndex,
-                             String toolTipText, String urlText) {
-        if (info == null) {
-            return;
-        }
-        EntityCollection entities = info.getOwner().getEntityCollection();
-        if (entities == null) {
-            return;
-        }
-        CollisionFreeXYTextAnnotationEntity entity =
-            new CollisionFreeXYTextAnnotationEntity(hotspot,
-                rendererIndex, toolTipText, urlText);
-        entities.add(entity);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/jfree/CollisionFreeXYTextAnnotationEntity.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,21 +0,0 @@
-package de.intevation.flys.jfree;
-
-import java.awt.Shape;
-
-import org.jfree.chart.entity.XYAnnotationEntity;
-
-/**
- * Chart Entity for XYTextAnnotations that should not collide.
- */
-public class CollisionFreeXYTextAnnotationEntity
-extends XYAnnotationEntity {
-    public CollisionFreeXYTextAnnotationEntity(
-        Shape hotspot,
-        int rendererIndex,
-        String toolTip,
-        String url
-    ) {
-        super(hotspot, rendererIndex, toolTip, url);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/jfree/DoubleBounds.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,85 +0,0 @@
-package de.intevation.flys.jfree;
-
-
-import org.jfree.chart.axis.ValueAxis;
-import org.jfree.chart.axis.LogarithmicAxis;
-import org.jfree.data.Range;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class DoubleBounds implements Bounds {
-
-    protected double lower;
-    protected double upper;
-
-
-    /**
-     * Default constructor. <b>A DoubleBounds has always set lower &lt;
-     * upper!</b>
-     */
-    public DoubleBounds(double lower, double upper) {
-        this.lower = Math.min(lower, upper);
-        this.upper = Math.max(lower, upper);
-    }
-
-
-    @Override
-    public Number getLower() {
-        return Double.valueOf(lower);
-    }
-
-
-    @Override
-    public Number getUpper() {
-        return Double.valueOf(upper);
-    }
-
-
-    @Override
-    public void applyBounds(ValueAxis axis) {
-        axis.setRange(new Range(lower, upper));
-    }
-
-
-    /**
-     * Set extended range to ValueAxis.
-     * @param percent how many percent to extend (in each direction,
-     *        thus 10 percent on [0,100] -> [-10,110].
-     */
-    @Override
-    public void applyBounds(ValueAxis axis, int percent) {
-        double space = (upper - lower) / 100 * percent;
-        if (axis instanceof LogarithmicAxis) {
-            axis.setRange(new Range(Math.max(lower-space, 0.0001),
-                        Math.max(upper+space, 0.0002)));
-        } else {
-            axis.setRange(new Range(lower-space, upper+space));
-        }
-    }
-
-
-    @Override
-    public Bounds combine(Bounds bounds) {
-        if (bounds == null) {
-            return this;
-        }
-
-        DoubleBounds other = (DoubleBounds) bounds;
-
-        double otherLower = other.getLower().doubleValue();
-        double otherUpper = other.getUpper().doubleValue();
-
-        return new DoubleBounds(
-            otherLower < lower ? otherLower : lower,
-            otherUpper > upper ? otherUpper : upper);
-    }
-
-
-    @Override
-    public String toString() {
-        return "DoubleBounds=[" + lower + " ; " + upper + "]";
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/jfree/EnhancedLineAndShapeRenderer.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,576 +0,0 @@
-package de.intevation.flys.jfree;
-
-import java.awt.Color;
-import java.awt.Font;
-import java.awt.Graphics2D;
-import java.awt.Paint;
-import java.awt.Shape;
-import java.awt.geom.Rectangle2D;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.apache.log4j.Logger;
-import org.jfree.chart.axis.ValueAxis;
-import org.jfree.chart.entity.EntityCollection;
-import org.jfree.chart.plot.CrosshairState;
-import org.jfree.chart.plot.PlotOrientation;
-import org.jfree.chart.plot.XYPlot;
-import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
-import org.jfree.data.xy.XYDataset;
-import org.jfree.data.xy.XYSeries;
-import org.jfree.data.xy.XYSeriesCollection;
-import org.jfree.text.TextUtilities;
-import org.jfree.ui.RectangleEdge;
-import org.jfree.ui.TextAnchor;
-import org.jfree.util.BooleanList;
-import org.jfree.util.ShapeUtilities;
-
-/**
- * Renderer with additional the additional functionality of renderering minima
- * and/or maxima of dataseries contained in datasets.
- */
-public class EnhancedLineAndShapeRenderer extends XYLineAndShapeRenderer {
-
-    /**
-     *
-     */
-    private static final long serialVersionUID = 1L;
-
-    /** Own logger. */
-    private static final Logger logger =
-        Logger.getLogger(EnhancedLineAndShapeRenderer.class);
-
-    protected BooleanList isMinimumShapeVisible;
-    protected BooleanList isMaximumShapeVisible;
-    protected BooleanList showLineLabel;
-
-    protected Map<Integer, Double> seriesMinimum;
-    protected Map<Integer, Double> seriesMinimumX;
-    protected Map<Integer, Double> seriesMaximum;
-
-    protected Map<Integer, Font> lineLabelFonts;
-    protected Map<Integer, Color> lineLabelTextColors;
-    protected BooleanList showLineLabelBG;
-    protected Map<Integer, Color> lineLabelBGColors;
-
-
-    public EnhancedLineAndShapeRenderer(boolean lines, boolean shapes) {
-        super(lines, shapes);
-        this.isMinimumShapeVisible = new BooleanList();
-        this.isMaximumShapeVisible = new BooleanList();
-        this.showLineLabel         = new BooleanList();
-        this.showLineLabelBG       = new BooleanList();
-        this.seriesMinimum         = new HashMap<Integer, Double>();
-        this.seriesMaximum         = new HashMap<Integer, Double>();
-        this.seriesMinimumX        = new HashMap<Integer, Double>();
-        this.lineLabelFonts        = new HashMap<Integer, Font>();
-        this.lineLabelTextColors   = new HashMap<Integer, Color>();
-        this.lineLabelBGColors     = new HashMap<Integer, Color>();
-    }
-
-
-    /**
-     * Draw a background-box of a text to render.
-     * @param g2 graphics device to use
-     * @param text text to draw
-     * @param textX x-position for text
-     * @param textY y-position for text
-     * @param bgColor color to fill box with.
-     */
-    public static void drawTextBox(Graphics2D g2,
-        String text, float textX, float textY, Color bgColor
-    ) {
-        Rectangle2D hotspotBox = g2.getFontMetrics().getStringBounds(text, g2);
-        float w = (float) hotspotBox.getWidth(), h = (float) hotspotBox.getHeight();
-        hotspotBox.setRect(textX, textY-h, w, h);
-        Color oldColor = g2.getColor();
-        g2.setColor(bgColor);
-        g2.fill(hotspotBox);
-        g2.setColor(oldColor);
-    }
-
-
-    /**
-     * Whether or not a specific item in a series (maybe the maxima) should
-     * be rendered with shape.
-     */
-    public boolean getItemShapeVisible(XYDataset dataset, int series, int item){
-        if (super.getItemShapeVisible(series, item)) {
-            return true;
-        }
-
-        if (isMinimumShapeVisible(series) && isMinimum(dataset, series, item)) {
-            return true;
-        }
-
-        if (isMaximumShapeVisible(series) && isMaximum(dataset, series, item)) {
-            return true;
-        }
-
-        return false;
-    }
-
-
-    /**
-     * Rectangle used to draw maximums shape.
-     */
-    public Shape getMaximumShape(int series, int column) {
-        return new Rectangle2D.Double(-5d, -5d, 10d, 10d);
-    }
-
-
-    /**
-     * Rectangle used to draw minimums shape.
-     */
-    public Shape getMinimumShape(int series, int column) {
-        return new Rectangle2D.Double(-5d, -5d, 10d, 10d);
-    }
-
-
-    /** Get fill paint for the maximum indicators. */
-    public Paint getMaximumFillPaint(int series, int column) {
-        Paint p = getItemPaint(series, column);
-
-        if (p instanceof Color) {
-            Color c = (Color) p;
-            Color b = c;
-
-            for (int i = 0; i < 2; i++) {
-                b = b.darker();
-            }
-
-            return b;
-        }
-
-        logger.warn("Item paint is no instance of Color!");
-        return p;
-    }
-
-
-    /** Get fill paint for the minimum indicators. */
-    public Paint getMinimumFillPaint(int series, int column) {
-        Paint p = getItemPaint(series, column);
-
-        if (p instanceof Color) {
-            Color c = (Color) p;
-            Color b = c;
-
-            for (int i = 0; i < 2; i++) {
-                b = b.darker();
-            }
-
-            return b;
-        }
-
-        logger.warn("Item paint is no instance of Color!");
-        return p;
-    }
-
-
-    /**
-     * Overrides XYLineAndShapeRenderer.drawSecondaryPass() to call an adapted
-     * method getItemShapeVisible() which now takes an XYDataset. So, 99% of
-     * code equal the code in XYLineAndShapeRenderer.
-     */
-    @Override
-    protected void drawSecondaryPass(
-        Graphics2D       g2,
-        XYPlot           plot,
-        XYDataset        dataset,
-        int              pass,
-        int              series,
-        int              item,
-        ValueAxis        domainAxis,
-        Rectangle2D      dataArea,
-        ValueAxis        rangeAxis,
-        CrosshairState   crosshairState,
-        EntityCollection entities
-    ) {
-        Shape entityArea = null;
-
-        // get the data point...
-        double x1 = dataset.getXValue(series, item);
-        double y1 = dataset.getYValue(series, item);
-        if (Double.isNaN(y1) || Double.isNaN(x1)) {
-            return;
-        }
-
-        PlotOrientation orientation = plot.getOrientation();
-        RectangleEdge xAxisLocation = plot.getDomainAxisEdge();
-        RectangleEdge yAxisLocation = plot.getRangeAxisEdge();
-        double transX1 = domainAxis.valueToJava2D(x1, dataArea, xAxisLocation);
-        double transY1 = rangeAxis.valueToJava2D(y1, dataArea, yAxisLocation);
-
-        if (getItemShapeVisible(dataset, series, item)) {
-            Shape shape = null;
-
-            // OPTIMIZE: instead of calculating minimum and maximum for every
-            //           point, calculate it just once (assume that dataset
-            //           content does not change during rendering).
-            // NOTE:     Above OPTIMIZE might already be fulfilled to most extend.
-            boolean isMinimum = isMinimumShapeVisible(series)
-                && isMinimum(dataset, series, item);
-
-            boolean isMaximum = isMaximumShapeVisible(series)
-                && isMaximum(dataset, series, item);
-
-            if (isMinimum) {
-                logger.debug("Create a Minimum shape.");
-                shape = getMinimumShape(series, item);
-            }
-            else if (isMaximum) {
-                logger.debug("Create a Maximum shape.");
-                shape = getMaximumShape(series, item);
-            }
-            else {
-                shape = getItemShape(series, item);
-            }
-
-            if (orientation == PlotOrientation.HORIZONTAL) {
-                shape = ShapeUtilities.createTranslatedShape(shape, transY1,
-                        transX1);
-            }
-            else if (orientation == PlotOrientation.VERTICAL) {
-                shape = ShapeUtilities.createTranslatedShape(shape, transX1,
-                        transY1);
-            }
-            entityArea = shape;
-            if (shape.intersects(dataArea)) {
-                if (getItemShapeFilled(series, item)) {
-                    if (getUseFillPaint()) {
-                        g2.setPaint(getItemFillPaint(series, item));
-                    }
-                    else {
-                        g2.setPaint(getItemPaint(series, item));
-                    }
-                    g2.fill(shape);
-                }
-                if (getDrawOutlines()) {
-                    if (getUseOutlinePaint()) {
-                        g2.setPaint(getItemOutlinePaint(series, item));
-                    }
-                    else {
-                        g2.setPaint(getItemPaint(series, item));
-                    }
-                    g2.setStroke(getItemOutlineStroke(series, item));
-                    g2.draw(shape);
-                }
-
-                if (isMinimum) {
-                    g2.setPaint(getMinimumFillPaint(series, item));
-                    g2.fill(shape);
-                    g2.setPaint(getItemOutlinePaint(series, item));
-                    g2.setStroke(getItemOutlineStroke(series, item));
-                    g2.draw(shape);
-                }
-                else if (isMaximum) {
-                    g2.setPaint(getMaximumFillPaint(series, item));
-                    g2.fill(shape);
-                    g2.setPaint(getItemOutlinePaint(series, item));
-                    g2.setStroke(getItemOutlineStroke(series, item));
-                    g2.draw(shape);
-                }
-            }
-        } // if (getItemShapeVisible(dataset, series, item))
-
-        double xx = transX1;
-        double yy = transY1;
-        if (orientation == PlotOrientation.HORIZONTAL) {
-            xx = transY1;
-            yy = transX1;
-        }
-
-        // Draw the item label if there is one...
-        if (isItemLabelVisible(series, item)) {
-            drawItemLabel(g2, orientation, dataset, series, item, xx, yy,
-                    (y1 < 0.0));
-        }
-
-        // Draw label of line.
-        if (dataset instanceof XYSeriesCollection
-            && isShowLineLabel(series)
-            && isMinimumX (dataset, series, item)
-            ) {
-            XYSeries xYSeries = ((XYSeriesCollection) dataset).getSeries(series);
-            String waterlevelLabel = (xYSeries instanceof HasLabel)
-                ? ((HasLabel)xYSeries).getLabel()
-                : xYSeries.getKey().toString();
-            // TODO Force water of some German rivers to flow direction mountains.
-
-            Font oldFont = g2.getFont();
-
-            Color oldColor = g2.getColor();
-            g2.setFont(this.getLineLabelFont(series));
-            g2.setColor(this.getLineLabelTextColor(series));
-            g2.setBackground(Color.black);
-
-            // Try to always display label if the data is visible.
-            if (!isPointInRect(dataArea, xx, yy)) {
-                // Move into the data area.
-                xx = Math.max(xx, dataArea.getMinX());
-                xx = Math.min(xx, dataArea.getMaxX());
-                yy = Math.max(yy, dataArea.getMinY());
-                yy = Math.min(yy, dataArea.getMaxY());
-            }
-
-            // Move to right until no collisions exist anymore
-            Shape hotspot = TextUtilities.calculateRotatedStringBounds(
-                waterlevelLabel, g2, (float)xx, (float)yy-3f,
-                TextAnchor.CENTER_LEFT,
-                0f, TextAnchor.CENTER_LEFT);
-            while (JFreeUtil.collides(hotspot, entities,
-                                      CollisionFreeLineLabelEntity.class)) {
-                xx += 5f;
-                hotspot = TextUtilities.calculateRotatedStringBounds(
-                    waterlevelLabel, g2, (float)xx, (float)yy-3f, TextAnchor.CENTER_LEFT,
-                    0f, TextAnchor.CENTER_LEFT);
-            }
-
-            // Register to avoid collissions.
-            entities.add(new CollisionFreeLineLabelEntity(hotspot,
-                1, "", ""));
-
-            // Fill background.
-            if (isShowLineLabelBG(series)) {
-                drawTextBox(g2, waterlevelLabel, (float)xx, (float)yy-3f,
-                    getLineLabelBGColor(series));
-            }
-
-            g2.drawString(waterlevelLabel, (float)xx, (float)yy-3f);
-
-            g2.setFont(oldFont);
-            g2.setColor(oldColor);
-        }
-
-        int domainAxisIndex = plot.getDomainAxisIndex(domainAxis);
-        int rangeAxisIndex = plot.getRangeAxisIndex(rangeAxis);
-        updateCrosshairValues(crosshairState, x1, y1, domainAxisIndex,
-                rangeAxisIndex, transX1, transY1, orientation);
-
-        // Add an entity for the item, but only if it falls within the data
-        // area...
-        if (entities != null && isPointInRect(dataArea, xx, yy)) {
-            addEntity(entities, entityArea, dataset, series, item, xx, yy);
-        }
-    }
-
-
-    /**
-     * Sets whether or not the minimum should be rendered with shape.
-     */
-    public void setIsMinimumShapeVisisble(int series, boolean isVisible) {
-        this.isMinimumShapeVisible.setBoolean(series, isVisible);
-    }
-
-
-    /**
-     * Whether or not the minimum should be rendered with shape.
-     */
-    public boolean isMinimumShapeVisible(int series) {
-        if (this.isMinimumShapeVisible.size() <= series) {
-            return false;
-        }
-
-        return isMinimumShapeVisible.getBoolean(series);
-    }
-
-
-    /**
-     * Sets whether or not the maximum should be rendered with shape.
-     */
-    public void setIsMaximumShapeVisible(int series, boolean isVisible) {
-        this.isMaximumShapeVisible.setBoolean(series, isVisible);
-    }
-
-
-    /**
-     * Whether or not the maximum should be rendered with shape.
-     */
-    public boolean isMaximumShapeVisible(int series) {
-        if (this.isMaximumShapeVisible.size() <= series) {
-            return false;
-        }
-
-        return isMaximumShapeVisible.getBoolean(series);
-    }
-
-    /** Whether or not a label should be shown for series. */
-    public boolean isShowLineLabel(int series) {
-        if (this.showLineLabel.size() <= series) {
-            return false;
-        }
-
-        return showLineLabel.getBoolean(series);
-    }
-
-
-    /** Sets whether or not a label should be shown for series. */
-    public void setShowLineLabel(boolean showLineLabel, int series) {
-        this.showLineLabel.setBoolean(series, showLineLabel);
-    }
-
-
-    /** Whether or not a label should be shown for series. */
-    public boolean isShowLineLabelBG(int series) {
-        if (this.showLineLabelBG.size() <= series) {
-            return false;
-        }
-
-        return showLineLabelBG.getBoolean(series);
-    }
-
-
-    public void setShowLineLabelBG(int series, boolean doShow) {
-        this.showLineLabelBG.setBoolean(series, doShow);
-    }
-
-    public Color getLineLabelBGColor(int series) {
-        if (this.lineLabelBGColors.size() <= series) {
-            return null;
-        }
-
-        return this.lineLabelBGColors.get(series);
-    }
-
-    public void setLineLabelBGColor(int series, Color color) {
-        this.lineLabelBGColors.put(series, color);
-    }
-
-    public Color getLineLabelTextColor(int series) {
-        if (this.lineLabelTextColors.size() <= series) {
-            return null;
-        }
-
-        return this.lineLabelTextColors.get(series);
-    }
-
-    public void setLineLabelTextColor(int series, Color color) {
-        this.lineLabelTextColors.put(series, color);
-    }
-
-    public void setLineLabelFont(Font font, int series) {
-        this.lineLabelFonts.put(series, font);
-    }
-
-    public Font getLineLabelFont(int series) {
-        return this.lineLabelFonts.get(series);
-    }
-
-
-    /**
-     * True if the given item of given dataset has the smallest
-     * X value within this set.
-     */
-    public boolean isMinimumX(XYDataset dataset, int series, int item) {
-        return dataset.getXValue(series, item) == getMinimumX(dataset, series);
-    }
-
-
-    /**
-     * Get Minimum X Value of a given series in a dataset.
-     * The value is stored for later use if queried the first time.
-     */
-    public double getMinimumX(XYDataset dataset, int series) {
-        Integer key = Integer.valueOf(series);
-        Double  old = seriesMinimumX.get(key);
-
-        if (old != null) {
-            return old.doubleValue();
-        }
-
-        logger.debug("Compute minimum of Series: " + series);
-
-        double min = Double.MAX_VALUE;
-
-        for (int i = 0, n = dataset.getItemCount(series); i < n; i++) {
-            double tmpValue = dataset.getXValue(series, i);
-
-            if (tmpValue < min) {
-                min = tmpValue;
-            }
-        }
-
-        seriesMinimumX.put(key, Double.valueOf(min));
-
-        return min;
-    }
-
-
-    /**
-     * True if the given item of given dataset has the smallest
-     * Y value within this set.
-     */
-    public boolean isMinimum(XYDataset dataset, int series, int item) {
-        return dataset.getYValue(series, item) == getMinimum(dataset, series);
-    }
-
-
-    /**
-     * Get Minimum Y Value of a given series in a dataset.
-     * The value is stored for later use if queried the first time.
-     */
-    public double getMinimum(XYDataset dataset, int series) {
-        Integer key = Integer.valueOf(series);
-        Double  old = seriesMinimum.get(key);
-
-        if (old != null) {
-            return old.doubleValue();
-        }
-
-        logger.debug("Compute minimum of Series: " + series);
-
-        double min = Double.MAX_VALUE;
-
-        for (int i = 0, n = dataset.getItemCount(series); i < n; i++) {
-            double tmpValue = dataset.getYValue(series, i);
-
-            if (tmpValue < min) {
-                min = tmpValue;
-            }
-        }
-
-        seriesMinimum.put(key, Double.valueOf(min));
-
-        return min;
-    }
-
-
-    /**
-     * True if the given item of given dataset has the biggest
-     * Y value within this set.
-     */
-    public boolean isMaximum(XYDataset dataset, int series, int item) {
-        return dataset.getYValue(series, item) == getMaximum(dataset, series);
-    }
-
-
-    /**
-     * Get maximum Y Value of a given series in a dataset.
-     * The value is stored for later use if queried the first time.
-     */
-    public double getMaximum(XYDataset dataset, int series) {
-        Integer key = Integer.valueOf(series);
-        Double  old = seriesMaximum.get(key);
-
-        if (old != null) {
-            return old.doubleValue();
-        }
-
-        logger.debug("Compute maximum of Series: " + series);
-
-        double max = -Double.MAX_VALUE;
-
-        for (int i = 0, n = dataset.getItemCount(series); i < n; i++) {
-            double tmpValue = dataset.getYValue(series, i);
-
-            if (tmpValue > max) {
-                max = tmpValue;
-            }
-        }
-
-        seriesMaximum.put(key, Double.valueOf(max));
-
-        return max;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/jfree/FLYSAnnotation.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,107 +0,0 @@
-package de.intevation.flys.jfree;
-
-import de.intevation.flys.artifacts.model.HYKFactory;
-
-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)
- * and 'HYK'-Annotations (rectangles/areas) with name and theme.
- */
-public class FLYSAnnotation {
-
-    /** 'Other' Text Annotations. */
-    protected List<XYTextAnnotation> textAnnotations;
-
-    /** Annotations at axis. */
-    protected List<StickyAxisAnnotation> axisTextAnnotations;
-
-    /** Areas at axis. */
-    protected List<HYKFactory.Zone> boxes;
-
-    /** Styling information. */
-    protected Document theme;
-
-    /** Chart-legend information. */
-    protected String label;
-
-
-    public FLYSAnnotation(String label, List<StickyAxisAnnotation> annotations) {
-        this(label, annotations, null, null);
-    }
-
-
-    /** Create annotations, parameter might be null. */
-    public FLYSAnnotation(String label, List<StickyAxisAnnotation> annotations,
-        List<HYKFactory.Zone> bAnnotations
-    ) {
-        this(label, annotations, bAnnotations, null);
-    }
-
-
-    /** Create annotations, parameter might be null. */
-    public FLYSAnnotation(String label, List<StickyAxisAnnotation> annotations,
-        List<HYKFactory.Zone> bAnnotations, Document theme
-    ) {
-        this.label = label;
-        this.axisTextAnnotations = (annotations != null)
-                                   ? annotations
-                                   : Collections.<StickyAxisAnnotation>emptyList();
-        this.boxes = (bAnnotations != null)
-                     ? bAnnotations
-                     : Collections.<HYKFactory.Zone>emptyList();
-        this.textAnnotations = Collections.<XYTextAnnotation>emptyList();
-        this.setTheme(theme);
-    }
-
-
-    public void setLabel(String label) {
-        this.label = label;
-    }
-
-    public String getLabel() {
-        return label;
-    }
-
-    public List<StickyAxisAnnotation> getAxisTextAnnotations() {
-        return axisTextAnnotations;
-    }
-
-    public void setTextAnnotations(List<XYTextAnnotation> annotations) {
-        this.textAnnotations = annotations;
-    }
-
-    /** Set the "other" Text Annotations. */
-    public List<XYTextAnnotation> getTextAnnotations() {
-        return textAnnotations;
-    }
-
-    public List<HYKFactory.Zone> getBoxes() {
-        return boxes;
-    }
-
-    public void setTheme(Document theme) {
-        this.theme = theme;
-    }
-
-    public Document getTheme() {
-        return theme;
-    }
-
-    /**
-     * Set sticky axis of all axisTextAnnotations
-     * to the X axis if it is currently Y, and vice versa.
-     * @return this
-     */
-    public FLYSAnnotation flipStickyAxis() {
-        for (StickyAxisAnnotation saa: axisTextAnnotations) {
-            saa.flipStickyAxis();
-        }
-        return this;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/jfree/HasLabel.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,8 +0,0 @@
-package de.intevation.flys.jfree;
-
-/** Interface to say something is labelled. */
-public interface HasLabel {
-    public String getLabel();
-    public void setLabel(String label);
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/jfree/JFreeUtil.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,143 +0,0 @@
-package de.intevation.flys.jfree;
-
-import java.awt.Shape;
-import java.awt.geom.Rectangle2D;
-import java.util.Iterator;
-import java.util.Random;
-
-import org.apache.log4j.Logger;
-import org.jfree.chart.entity.ChartEntity;
-import org.jfree.chart.entity.EntityCollection;
-import org.w3c.dom.Document;
-
-import de.intevation.flys.artifacts.math.Function;
-
-public class JFreeUtil {
-
-    private static final Logger logger = Logger.getLogger(JFreeUtil.class);
-
-    /** Do not instantiate. */
-    private JFreeUtil() {
-    }
-
-
-    /**
-     * True if \param hotspot collides with a Entity in \param entities.
-     * @param hotspot Shape to compare against other shapes (bounds only).
-     * @param entities entities against which to compare shape.
-     * @param exclusiveEntityClass If not null, consider only entities of
-     *        given class.
-     * @return true if a collision (non-zero intersection) exists between
-     *        shapes.
-     */
-    public static boolean collides(Shape hotspot, EntityCollection entities,
-        Class exclusiveEntityClass) {
-        if (entities == null) return false;
-
-        Rectangle2D hotspotBox = hotspot.getBounds2D();
-
-        for (Iterator i = entities.iterator(); i.hasNext(); ) {
-            Object next = i.next();
-            ChartEntity entity = (ChartEntity) next;
-            if (exclusiveEntityClass == null
-                || exclusiveEntityClass.isInstance(entity))
-                {
-                if (entity.getArea().intersects(hotspotBox)) {
-                    // Found collision, early stop.
-                    return true;
-                }
-            }
-        }
-
-        return false;
-    }
-
-
-    /**
-     * This function samples a randomized line that contains of x and y values
-     * between <i>startX</i>, <i>endX</i>, <i>startY</i> and <i>endY</i>. The
-     * number of points in the line is specified by <i>num</i>.
-     *
-     * @param num The number of points in the line.
-     * @param startX The min value of the x values.
-     * @param endX The max value of the x values.
-     * @param startY The min value of the y values.
-     * @param endY The max value of the y values.
-     * @return an array with [allX-values, allY-values].
-     * @throws IllegalArgumentException
-     */
-    public static double[][] randomizeLine(
-        int    num,
-        double startX,
-        double endX,
-        double startY,
-        double endY
-    ) throws IllegalArgumentException
-    {
-        if (num <= 0) {
-            throw new IllegalArgumentException("Parameter 'num' has to be > 0");
-        }
-
-        Random random = new Random();
-
-        double[] x = new double[num];
-        double[] y = new double[num];
-
-        for (int i = 0; i < num; i++) {
-            double xFac = random.nextDouble();
-            double yFac = random.nextDouble();
-
-            x[i] = startX + xFac * (endX - startX);
-            y[i] = startY + yFac * (endY - startY);
-
-            logger.debug("Created new point: " + x[i] + "|" + y[i]);
-        }
-
-        return new double[][] { x, y };
-    }
-
-
-    public static StyledXYSeries sampleFunction2D(
-        Function func,
-        Document theme,
-        String   seriesKey,
-        int      samples,
-        double   start,
-        double   end
-    ) {
-        StyledXYSeries series = new StyledXYSeries(seriesKey, theme);
-
-        double step = (end - start) / (samples - 1);
-
-        for (int i = 0; i < samples; i++) {
-            double x = start + (step * i);
-            series.add(x, func.value(x));
-        }
-
-        return series;
-    }
-
-    public static StyledXYSeries sampleFunction2DPositive(
-        Function func,
-        Document theme,
-        String   seriesKey,
-        int      samples,
-        double   start,
-        double   end
-    ) {
-        StyledXYSeries series = new StyledXYSeries(seriesKey, theme);
-
-        double step = (end - start) / (samples - 1);
-
-        for (int i = 0; i < samples; i++) {
-            double x = start + (step * i);
-            double v = func.value(x);
-            if (x > 0d && v > 0d) {
-                series.add(x, v);
-            }
-        }
-
-        return series;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/jfree/ShapeRenderer.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,357 +0,0 @@
-package de.intevation.flys.jfree;
-
-/**
- * Copyright (c) 2006, 2012 by Intevation GmbH
- *
- * @author Sascha L. Teichmann (teichmann@intevation.de)
- *
- * This program is free software under the LGPL (&gt;=v2.1)
- * Read the file LGPL coming with FLYS for details.
- */
-
-import java.awt.Font;
-import java.awt.Graphics2D;
-import java.awt.Paint;
-import java.awt.Shape;
-
-import java.awt.geom.Point2D;
-import java.awt.geom.Rectangle2D;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-import org.jfree.chart.axis.ValueAxis;
-
-import org.jfree.chart.labels.ItemLabelPosition;
-import org.jfree.chart.labels.XYItemLabelGenerator;
-
-import org.jfree.chart.plot.CrosshairState;
-import org.jfree.chart.plot.PlotOrientation;
-import org.jfree.chart.plot.PlotRenderingInfo;
-import org.jfree.chart.plot.XYPlot;
-
-import org.jfree.chart.renderer.xy.StandardXYItemRenderer;
-import org.jfree.chart.renderer.xy.XYItemRendererState;
-
-import org.jfree.data.xy.XYDataset;
-
-import org.jfree.text.TextUtilities;
-
-import org.jfree.ui.RectangleEdge;
-
-public class ShapeRenderer
-extends      StandardXYItemRenderer {
-
-    public static class Entry {
-        protected Shape   shape;
-        protected Shape   frame;
-        protected Paint   paint;
-        protected boolean filled;
-
-        public Entry(
-            Shape shape,
-            Paint paint,
-            boolean filled
-        ) {
-            this.shape = shape;
-            this.paint = paint;
-            this.filled = filled;
-        }
-
-        public Entry(
-            Shape   shape,
-            Shape   frame,
-            Paint   paint,
-            boolean filled
-        ) {
-            this.shape  = shape;
-            this.frame  = frame;
-            this.paint  = paint;
-            this.filled = filled;
-        }
-
-        public Shape getShape() {
-            return shape;
-        }
-
-        public void setShape(Shape shape) {
-            this.shape = shape;
-        }
-
-
-        public Paint getPaint() {
-            return paint;
-        }
-
-        public void setPaint(Paint paint) {
-            this.paint = paint;
-        }
-
-        public boolean getFilled() {
-            return filled;
-        }
-
-        public void setFilled(boolean filled) {
-            this.filled = filled;
-        }
-
-        public boolean equals(Object other) {
-            Entry entry = (Entry)other;
-            return filled == entry.filled
-                   &&   paint.equals(entry.paint)
-                   &&   shape.equals(entry.shape);
-        }
-
-        public int hashCode() {
-            return
-                shape.hashCode() ^
-                paint.hashCode() ^
-                (filled ? 1231 : 1237);
-        }
-    } // class Entry
-
-    public interface LabelGenerator {
-        String createLabel(Entry entry);
-    } // interface EntryLabelGenerator
-
-    protected Entry []  entries;
-
-    protected List<Rectangle2D> labelBoundingBoxes;
-
-    protected Rectangle2D area;
-
-    public ShapeRenderer() {
-        this(SHAPES);
-    }
-
-    public ShapeRenderer(int type) {
-        super(type);
-    }
-
-    public ShapeRenderer(Map<Entry, Integer> map) {
-        super(SHAPES);
-        setEntries(map);
-    }
-
-    public void setEntries(Entry [] entries) {
-        this.entries = entries;
-    }
-
-    public void setEntries(Map<Entry, Integer> map) {
-        Entry [] entries = new Entry[map.size()];
-
-        for (Map.Entry<Entry, Integer> entry: map.entrySet()) {
-            entries[entry.getValue()] = entry.getKey();
-        }
-
-        setEntries(entries);
-    }
-
-    @Override
-    public Shape getSeriesShape(int series) {
-        return entries[series].shape;
-    }
-
-    public Shape getSeriesFrame(int series) {
-        return entries[series].frame;
-    }
-
-    @Override
-    public Paint getSeriesPaint(int series) {
-        return entries[series].paint;
-    }
-
-    @Override
-    public boolean getItemShapeFilled(int series, int item) {
-        return entries[series].filled;
-    }
-
-    @Override
-    public XYItemRendererState initialise(
-        Graphics2D        g2,
-        Rectangle2D       dataArea,
-        XYPlot            plot,
-        XYDataset         data,
-        PlotRenderingInfo info
-    ) {
-        if (labelBoundingBoxes == null) {
-            labelBoundingBoxes = new ArrayList<Rectangle2D>(32);
-        }
-        else {
-            labelBoundingBoxes.clear();
-        }
-
-        area = dataArea;
-
-        return super.initialise(g2, dataArea, plot, data, info);
-    }
-
-    @Override
-    public void drawItem(
-        Graphics2D          g2,
-        XYItemRendererState state,
-        Rectangle2D         dataArea,
-        PlotRenderingInfo   info,
-        XYPlot              plot,
-        ValueAxis           domainAxis,
-        ValueAxis           rangeAxis,
-        XYDataset           dataset,
-        int                 series,
-        int                 item,
-        CrosshairState      crosshairState,
-        int                 pass
-    ) {
-        if (!getItemVisible(series, item)) {
-            return;
-        }
-
-        // get the data point...
-        double x1 = dataset.getXValue(series, item);
-        double y1 = dataset.getYValue(series, item);
-        if (Double.isNaN(x1) || Double.isNaN(y1)) {
-            return;
-        }
-
-        RectangleEdge xAxisLocation = plot.getDomainAxisEdge();
-        RectangleEdge yAxisLocation = plot.getRangeAxisEdge();
-        double x = domainAxis.valueToJava2D(x1, dataArea, xAxisLocation);
-        double y = rangeAxis.valueToJava2D(y1, dataArea, yAxisLocation);
-
-        if (dataArea.contains(x, y))
-            super.drawItem(
-                g2,
-                state,
-                dataArea,
-                info,
-                plot,
-                domainAxis,
-                rangeAxis,
-                dataset,
-                series,
-                item,
-                crosshairState,
-                pass);
-    }
-
-    protected Point2D shiftBox(Rectangle2D box) {
-
-        double cx1 = area.getX();
-        double cy1 = area.getY();
-        double cx2 = cx1 + area.getWidth();
-        double cy2 = cy1 + area.getHeight();
-
-        double bx1 = box.getX();
-        double by1 = box.getY();
-        double bx2 = bx1 + box.getWidth();
-        double by2 = by1 + box.getHeight();
-
-        double dx;
-        double dy;
-
-        if (bx1 < cx1) {
-            dx = cx1 - bx1;
-        }
-        else if (bx2 > cx2) {
-            dx = cx2 - bx2;
-        }
-        else {
-            dx = 0d;
-        }
-
-        if (by1 < cy1) {
-            dy = cy1 - by1;
-        }
-        else if (by2 > cy2) {
-            dy = cy2 - by2;
-        }
-        else {
-            dy = 0d;
-        }
-
-        return new Point2D.Double(dx, dy);
-    }
-
-    @Override
-    protected void drawItemLabel(
-        Graphics2D      g2,
-        PlotOrientation orientation,
-        XYDataset       dataset,
-        int             series,
-        int             item,
-        double          x,
-        double          y,
-        boolean         negative
-    ) {
-        XYItemLabelGenerator generator = getItemLabelGenerator(series, item);
-        if (generator == null) {
-            return;
-        }
-
-        Font labelFont = getItemLabelFont(series, item);
-
-        Paint paint = getItemLabelPaint(series, item);
-
-        g2.setFont(labelFont);
-        g2.setPaint(paint);
-
-        String label = generator.generateLabel(dataset, series, item);
-
-        ATTEMPS: for (int attempt = 0; attempt < 2; ++attempt) {
-            // get the label position..
-            ItemLabelPosition position = null;
-
-            boolean pos;
-            switch (attempt) {
-                case 0: pos = negative; break;
-                case 1: pos = !negative; break;
-                default: break ATTEMPS;
-            }
-
-            if (pos) {
-                position = getNegativeItemLabelPosition(series, item);
-            }
-            else {
-                position = getPositiveItemLabelPosition(series, item);
-            }
-
-            // work out the label anchor point...
-            Point2D anchorPoint = calculateLabelAnchorPoint(
-                position.getItemLabelAnchor(), x, y, orientation);
-
-            Shape labelShape = TextUtilities.calculateRotatedStringBounds(
-                label, g2,
-                (float)anchorPoint.getX(), (float)anchorPoint.getY(),
-                position.getTextAnchor(), position.getAngle(),
-                position.getRotationAnchor());
-
-            Rectangle2D bbox = labelShape.getBounds2D();
-
-            Point2D shift = shiftBox(bbox);
-
-            bbox = new Rectangle2D.Double(
-                bbox.getX() + shift.getX(),
-                bbox.getY() + shift.getY(),
-                bbox.getWidth(),
-                bbox.getHeight());
-
-            if (labelBoundingBoxes != null) {
-                for (Rectangle2D old: labelBoundingBoxes) {
-                    if (old.intersects(bbox)) {
-                        continue ATTEMPS;
-                    }
-                }
-                labelBoundingBoxes.add(bbox);
-            }
-
-            TextUtilities.drawRotatedString(
-                label, g2,
-                (float)(anchorPoint.getX() + shift.getX()),
-                (float)(anchorPoint.getY() + shift.getY()),
-                position.getTextAnchor(), position.getAngle(),
-                position.getRotationAnchor());
-            break;
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/jfree/StableXYDifferenceRenderer.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1856 +0,0 @@
-/* ===========================================================
- * JFreeChart : a free chart library for the Java(tm) platform
- * ===========================================================
- *
- * (C) Copyright 2000-2008, by Object Refinery Limited and Contributors.
- *
- * Project Info:  http://www.jfree.org/jfreechart/index.html
- *
- * This library is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2.1 of the License, or
- * (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
- * License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
- * USA.
- *
- * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
- * in the United States and other countries.]
- *
- * -------------------------
- * StableXYDifferenceRenderer.java
- * -------------------------
- * (C) Copyright 2003-2008, by Object Refinery Limited and Contributors.
- *
- * Original Author:  David Gilbert (for Object Refinery Limited);
- * Contributor(s):   Richard West, Advanced Micro Devices, Inc. (major rewrite
- *                   of difference drawing algorithm);
- *
- * Changes:
- * --------
- * 30-Apr-2003 : Version 1 (DG);
- * 30-Jul-2003 : Modified entity constructor (CZ);
- * 20-Aug-2003 : Implemented Cloneable and PublicCloneable (DG);
- * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
- * 09-Feb-2004 : Updated to support horizontal plot orientation (DG);
- * 10-Feb-2004 : Added default constructor, setter methods and updated
- *               Javadocs (DG);
- * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState (DG);
- * 30-Mar-2004 : Fixed bug in getNegativePaint() method (DG);
- * 15-Jul-2004 : Switched getX() with getXValue() and getY() with
- *               getYValue() (DG);
- * 25-Aug-2004 : Fixed a bug preventing the use of crosshairs (DG);
- * 11-Nov-2004 : Now uses ShapeUtilities to translate shapes (DG);
- * 19-Jan-2005 : Now accesses only primitive values from dataset (DG);
- * 22-Feb-2005 : Override getLegendItem(int, int) to return "line" items (DG);
- * 13-Apr-2005 : Fixed shape positioning bug (id = 1182062) (DG);
- * 20-Apr-2005 : Use generators for legend tooltips and URLs (DG);
- * 04-May-2005 : Override equals() method, renamed get/setPlotShapes() -->
- *               get/setShapesVisible (DG);
- * 09-Jun-2005 : Updated equals() to handle GradientPaint (DG);
- * 16-Jun-2005 : Fix bug (1221021) affecting stroke used for each series (DG);
- * ------------- JFREECHART 1.0.x ---------------------------------------------
- * 24-Jan-2007 : Added flag to allow rounding of x-coordinates, and fixed
- *               bug in clone() (DG);
- * 05-Feb-2007 : Added an extra call to updateCrosshairValues() in
- *               drawItemPass1(), to fix bug 1564967 (DG);
- * 06-Feb-2007 : Fixed bug 1086307, crosshairs with multiple axes (DG);
- * 08-Mar-2007 : Fixed entity generation (DG);
- * 20-Apr-2007 : Updated getLegendItem() for renderer change (DG);
- * 23-Apr-2007 : Rewrite of difference drawing algorithm to allow use of
- *               series with disjoint x-values (RW);
- * 04-May-2007 : Set processVisibleItemsOnly flag to false (DG);
- * 17-May-2007 : Set datasetIndex and seriesIndex in getLegendItem() (DG);
- * 18-May-2007 : Set dataset and seriesKey for LegendItem (DG);
- * 05-Nov-2007 : Draw item labels if visible (RW);
- * 17-Jun-2008 : Apply legend shape, font and paint attributes (DG);
- */
-/*
- * For further changes within the FLYS project, refer to the ChangeLog.
- */
-package de.intevation.flys.jfree;
-
-import java.awt.BasicStroke;
-import java.awt.Color;
-import java.awt.Graphics2D;
-import java.awt.Font;
-import java.awt.Paint;
-import java.awt.geom.Point2D;
-import java.awt.Shape;
-import java.awt.Stroke;
-import java.awt.geom.GeneralPath;
-import java.awt.geom.Line2D;
-import java.awt.geom.Rectangle2D;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.LinkedList;
-import java.util.List;
-
-import org.jfree.chart.LegendItem;
-import org.jfree.chart.axis.ValueAxis;
-import org.jfree.chart.entity.EntityCollection;
-import org.jfree.chart.entity.XYItemEntity;
-import org.jfree.chart.event.RendererChangeEvent;
-import org.jfree.chart.labels.XYToolTipGenerator;
-import org.jfree.chart.plot.CrosshairState;
-import org.jfree.chart.plot.PlotOrientation;
-import org.jfree.chart.plot.PlotRenderingInfo;
-import org.jfree.chart.plot.XYPlot;
-import org.jfree.chart.urls.XYURLGenerator;
-import org.jfree.data.xy.XYDataset;
-import org.jfree.data.xy.DefaultXYDataset;
-import org.jfree.io.SerialUtilities;
-import org.jfree.ui.RectangleEdge;
-import org.jfree.util.PaintUtilities;
-import org.jfree.util.PublicCloneable;
-import org.jfree.util.ShapeUtilities;
-
-import org.jfree.chart.renderer.xy.AbstractXYItemRenderer;
-import org.jfree.chart.renderer.xy.XYItemRenderer;
-import org.jfree.chart.renderer.xy.XYItemRendererState;
-
-import gnu.trove.TDoubleArrayList;
-
-import de.intevation.flys.artifacts.math.Linear;
-
-import java.text.NumberFormat;
-
-import org.apache.log4j.Logger;
-
-/**
- * A renderer for an {@link XYPlot} that highlights the differences between two
- * series.  The example shown here is generated by the
- * <code>DifferenceChartDemo1.java</code> program included in the JFreeChart
- * demo collection:
- * <br><br>
- * <img src="../../../../../images/StableXYDifferenceRendererSample.png"
- * alt="StableXYDifferenceRendererSample.png" />
- */
-public class StableXYDifferenceRenderer extends AbstractXYItemRenderer
-        implements XYItemRenderer, PublicCloneable {
-
-    private static Logger log = Logger.getLogger(StableXYDifferenceRenderer.class);
-
-    public static final int CALCULATE_POSITIVE_AREA = 1;
-    public static final int CALCULATE_NEGATIVE_AREA = 2;
-    public static final int CALCULATE_ALL_AREA =
-                            CALCULATE_POSITIVE_AREA | CALCULATE_NEGATIVE_AREA;
-
-    /** For serialization. */
-    private static final long serialVersionUID = -8447915602375584857L;
-
-    /** The paint used to highlight positive differences (y(0) > y(1)). */
-    private transient Paint positivePaint;
-
-    /** The paint used to highlight negative differences (y(0) < y(1)). */
-    private transient Paint negativePaint;
-
-    /** Display shapes at each point? */
-    private boolean shapesVisible;
-
-    /** Display shapes at each point? */
-    protected boolean drawOutline;
-
-    /** Which stroke to draw outline with? */
-    protected Stroke outlineStroke;
-
-    /** Which paint to draw outline with? */
-    protected Paint outlinePaint;
-
-    /** The shape to display in the legend item. */
-    private transient Shape legendShape;
-
-    protected boolean drawOriginalSeries;
-
-    /** The color of the label showing the calculated area. */
-    protected Color labelColor;
-
-    /** The background color of the label showing the calculated area. */
-    protected Color labelBGColor;
-
-    /** Font to draw label of calculated area with. */
-    protected Font labelFont;
-
-    /** Template to create i18ned label for area. */
-    protected String areaLabelTamplate;
-
-    /** NumberFormat to use for area. */
-    protected NumberFormat areaLabelNumberFormat;
-
-    protected int areaCalculationMode;
-
-    protected double positiveArea;
-    protected double negativeArea;
-
-    /** Whether or not to draw a label in the area. */
-    protected boolean labelArea = true;
-
-
-    /** Arithmetic centroid of drawn polygons. */
-    protected Point2D.Double centroid;
-
-
-    /** Number of points that contributed to the centroid. */
-    protected int centroidNPoints = 0;
-
-
-    /**
-     * This flag controls whether or not the x-coordinates (in Java2D space)
-     * are rounded to integers.  When set to true, this can avoid the vertical
-     * striping that anti-aliasing can generate.  However, the rounding may not
-     * be appropriate for output in high resolution formats (for example,
-     * vector graphics formats such as SVG and PDF).
-     *
-     * @since 1.0.4
-     */
-    private boolean roundXCoordinates;
-
-    /**
-     * Creates a new renderer with default attributes.
-     */
-    public StableXYDifferenceRenderer() {
-        this(Color.green, Color.red, false /*,  null */);
-    }
-
-    public StableXYDifferenceRenderer(Paint positivePaint, Paint negativePaint,
-                                boolean shapes) {
-        this(positivePaint, negativePaint, shapes, CALCULATE_ALL_AREA);
-    }
-
-    /**
-     * Creates a new renderer.
-     *
-     * @param positivePaint  the highlight color for positive differences
-     *                       (<code>null</code> not permitted).
-     * @param negativePaint  the highlight color for negative differences
-     *                       (<code>null</code> not permitted).
-     * @param shapes  draw shapes?
-     */
-    public StableXYDifferenceRenderer(Paint positivePaint, Paint negativePaint,
-                                boolean shapes, int areaCalculationMode) {
-        if (positivePaint == null) {
-            throw new IllegalArgumentException(
-                    "Null 'positivePaint' argument.");
-        }
-        if (negativePaint == null) {
-            throw new IllegalArgumentException(
-                    "Null 'negativePaint' argument.");
-        }
-        this.positivePaint = positivePaint;
-        this.negativePaint = negativePaint;
-        this.shapesVisible = shapes;
-        this.legendShape   = new Rectangle2D.Double(-3.0, -3.0, 10.0, 10.0);
-        this.roundXCoordinates = false;
-        this.drawOutline   = true;
-        this.outlineStroke = new BasicStroke(1);
-        this.outlinePaint  = Color.black;
-        this.drawOriginalSeries = false;
-        this.areaCalculationMode = areaCalculationMode;
-        this.labelBGColor  = null;
-        this.centroid      = new Point2D.Double(0,0);
-    }
-
-    public int getAreaCalculationMode() {
-        return areaCalculationMode;
-    }
-
-    public void setAreaCalculationMode(int areaCalculationMode) {
-        this.areaCalculationMode = areaCalculationMode;
-    }
-
-
-    /** Set template to use to create area label (e.g. 'Area=%dm2'). */
-    public void setAreaLabelTemplate(String areaTemplate) {
-        this.areaLabelTamplate = areaTemplate;
-    }
-
-
-    public void setAreaLabelNumberFormat(NumberFormat nf) {
-        this.areaLabelNumberFormat = nf;
-    }
-
-
-    public boolean isLabelArea() {
-        return this.labelArea;
-    }
-
-    public void setLabelArea(boolean label) {
-        this.labelArea = label;
-    }
-
-
-    /** Set font to paint label with. */
-    public void setLabelFont(Font font) {
-        this.labelFont = font;
-    }
-
-
-    /** Get font with which label is painted. */
-    public Font getLabelFont() {
-        return this.labelFont;
-    }
-
-
-    /** Set color with which to paint label. */
-    public void setLabelColor(Color color) {
-        this.labelColor = color;
-    }
-
-
-    /** Get color with which label is painted. */
-    public Color getLabelColor() {
-        return this.labelColor;
-    }
-
-
-    /** Set color with which to paint label bg. */
-    public void setLabelBGColor(Color color) {
-        this.labelBGColor = color;
-    }
-
-
-    /** Get color with which label is painted. */
-    public Color getLabelBGColor() {
-        return this.labelBGColor;
-    }
-
-
-    public double getCalculatedArea() {
-        return positiveArea + negativeArea;
-    }
-
-    /**
-     * Sets color that is used if drawOutline is true.
-     */
-    public void setOutlinePaint(Paint outlinePaint) {
-        this.outlinePaint = outlinePaint;
-    }
-
-
-    /**
-     * Gets color which is used if drawOutline is true.
-     */
-    public Paint getOutlinePaint() {
-        return this.outlinePaint;
-    }
-
-
-    /**
-     * Sets Stroke that is used if drawOutline is true.
-     */
-    public void setOutlineStroke(Stroke stroke) {
-        this.outlineStroke = stroke;
-    }
-
-
-    /**
-     * Returns Stroke that is used if drawOutline is true.
-     */
-    public Stroke getOutlineStroke() {
-        return this.outlineStroke;
-    }
-
-
-    /**
-     * Whether or not to draw the 'Shape' of the area (in contrast to
-     * shapes at data items).
-     */
-    public void setDrawOutline(boolean doDrawOutline) {
-        this.drawOutline = doDrawOutline;
-    }
-
-
-    /**
-     * Returns whether or not to draw the shape of the outline.
-     */
-    public boolean getDrawOutline() {
-        return this.drawOutline;
-    }
-
-
-    /**
-     * Returns the paint used to highlight positive differences.
-     *
-     * @return The paint (never <code>null</code>).
-     *
-     * @see #setPositivePaint(Paint)
-     */
-    public Paint getPositivePaint() {
-        return this.positivePaint;
-    }
-
-    /**
-     * Sets the paint used to highlight positive differences and sends a
-     * {@link RendererChangeEvent} to all registered listeners.
-     *
-     * @param paint  the paint (<code>null</code> not permitted).
-     *
-     * @see #getPositivePaint()
-     */
-    public void setPositivePaint(Paint paint) {
-        if (paint == null) {
-            throw new IllegalArgumentException("Null 'paint' argument.");
-        }
-        this.positivePaint = paint;
-        fireChangeEvent();
-    }
-
-    /**
-     * Returns the paint used to highlight negative differences.
-     *
-     * @return The paint (never <code>null</code>).
-     *
-     * @see #setNegativePaint(Paint)
-     */
-    public Paint getNegativePaint() {
-        return this.negativePaint;
-    }
-
-    /**
-     * Sets the paint used to highlight negative differences.
-     *
-     * @param paint  the paint (<code>null</code> not permitted).
-     *
-     * @see #getNegativePaint()
-     */
-    public void setNegativePaint(Paint paint) {
-        if (paint == null) {
-            throw new IllegalArgumentException("Null 'paint' argument.");
-        }
-        this.negativePaint = paint;
-        notifyListeners(new RendererChangeEvent(this));
-    }
-
-    /**
-     * Returns a flag that controls whether or not shapes are drawn for each
-     * data value.
-     *
-     * @return A boolean.
-     *
-     * @see #setShapesVisible(boolean)
-     */
-    public boolean getShapesVisible() {
-        return this.shapesVisible;
-    }
-
-    /**
-     * Sets a flag that controls whether or not shapes are drawn for each
-     * data value, and sends a {@link RendererChangeEvent} to all registered
-     * listeners.
-     *
-     * @param flag  the flag.
-     *
-     * @see #getShapesVisible()
-     */
-    public void setShapesVisible(boolean flag) {
-        this.shapesVisible = flag;
-        fireChangeEvent();
-    }
-
-    /**
-     * Returns the shape used to represent a line in the legend.
-     *
-     * @return The legend line (never <code>null</code>).
-     *
-     * @see #setLegendLine(Shape)
-     */
-    public Shape getLegendLine() {
-        return this.legendShape;
-    }
-
-    /**
-     * Sets the shape used as a line in each legend item and sends a
-     * {@link RendererChangeEvent} to all registered listeners.
-     *
-     * @param line  the line (<code>null</code> not permitted).
-     *
-     * @see #getLegendLine()
-     */
-    public void setLegendLine(Shape line) {
-        if (line == null) {
-            throw new IllegalArgumentException("Null 'line' argument.");
-        }
-        this.legendShape = line;
-        fireChangeEvent();
-    }
-
-    /**
-     * Returns the flag that controls whether or not the x-coordinates (in
-     * Java2D space) are rounded to integer values.
-     *
-     * @return The flag.
-     *
-     * @since 1.0.4
-     *
-     * @see #setRoundXCoordinates(boolean)
-     */
-    public boolean getRoundXCoordinates() {
-        return this.roundXCoordinates;
-    }
-
-    /**
-     * Sets the flag that controls whether or not the x-coordinates (in
-     * Java2D space) are rounded to integer values, and sends a
-     * {@link RendererChangeEvent} to all registered listeners.
-     *
-     * @param round  the new flag value.
-     *
-     * @since 1.0.4
-     *
-     * @see #getRoundXCoordinates()
-     */
-    public void setRoundXCoordinates(boolean round) {
-        this.roundXCoordinates = round;
-        fireChangeEvent();
-    }
-
-    /**
-     * Initialises the renderer and returns a state object that should be
-     * passed to subsequent calls to the drawItem() method.  This method will
-     * be called before the first item is rendered, giving the renderer an
-     * opportunity to initialise any state information it wants to maintain.
-     * The renderer can do nothing if it chooses.
-     *
-     * @param g2  the graphics device.
-     * @param dataArea  the (visible) area inside the axes.
-     * @param plot  the plot.
-     * @param data  the data.
-     * @param info  an optional info collection object to return data back to
-     *              the caller.
-     *
-     * @return A state object.
-     */
-    public XYItemRendererState initialise(Graphics2D g2,
-                                          Rectangle2D dataArea,
-                                          XYPlot plot,
-                                          XYDataset data,
-                                          PlotRenderingInfo info) {
-
-        XYItemRendererState state = super.initialise(g2, dataArea, plot, data,
-                info);
-        state.setProcessVisibleItemsOnly(false);
-        return state;
-    }
-
-    /**
-     * Returns <code>2</code>, the number of passes required by the renderer.
-     * The {@link XYPlot} will run through the dataset this number of times.
-     *
-     * @return The number of passes required by the renderer.
-     */
-    public int getPassCount() {
-        return 2;
-    }
-
-
-    /**
-     * Adds x/y data to series.
-     */
-    private static final void addSeries(
-        DefaultXYDataset ds,
-        Comparable       key,
-        TDoubleArrayList xs,
-        TDoubleArrayList ys
-    ) {
-        ds.addSeries(
-            key,
-            new double [][] {
-                xs.toNativeArray(),
-                ys.toNativeArray()
-            });
-    }
-
-    protected static List<XYDataset> splitByNaNsOneSeries(
-        XYDataset dataset
-    ) {
-        List<XYDataset> datasets = new ArrayList<XYDataset>();
-
-        int N = dataset.getItemCount(0);
-        TDoubleArrayList xs = new TDoubleArrayList(N);
-        TDoubleArrayList ys = new TDoubleArrayList(N);
-        for (int i = 0; i < N; ++i) {
-            double x = dataset.getXValue(0, i);
-            double y = dataset.getYValue(0, i);
-            if (Double.isNaN(x) || Double.isNaN(y)) {
-                if (!xs.isEmpty()) {
-                    DefaultXYDataset ds = new DefaultXYDataset();
-                    addSeries(ds, dataset.getSeriesKey(0), xs, ys);
-                    datasets.add(ds);
-                    xs.resetQuick();
-                    ys.resetQuick();
-                }
-            }
-            else {
-                xs.add(x);
-                ys.add(y);
-            }
-        }
-        if (!xs.isEmpty()) {
-            DefaultXYDataset ds = new DefaultXYDataset();
-            addSeries(ds, dataset.getSeriesKey(0), xs, ys);
-            datasets.add(ds);
-        }
-
-        return datasets;
-    }
-
-    private static final boolean add(TDoubleArrayList xs, double x) {
-        int N = xs.size();
-        if (N == 0 || xs.getQuick(N-1) < x) {
-            xs.add(x);
-            return true;
-        }
-        log.debug("pushed smaller");
-        return false;
-    }
-
-    protected static List<XYDataset> splitByNaNsTwoSeries(
-        XYDataset dataset
-    ) {
-        boolean debug = log.isDebugEnabled();
-
-        List<XYDataset> datasets = new ArrayList<XYDataset>();
-
-        int N = dataset.getItemCount(0);
-        int M = dataset.getItemCount(1);
-
-        int i = 0, j = 0;
-        // ignore leading NaNs
-        for (; i < N; ++i) {
-            double x = dataset.getXValue(0, i);
-            double y = dataset.getYValue(0, i);
-            if (!Double.isNaN(x) && !Double.isNaN(y)) {
-                break;
-            }
-        }
-
-        for (; j < M; ++j) {
-            double x = dataset.getXValue(1, j);
-            double y = dataset.getYValue(1, j);
-            if (!Double.isNaN(x) && !Double.isNaN(y)) {
-                break;
-            }
-        }
-
-        TDoubleArrayList six = new TDoubleArrayList();
-        TDoubleArrayList siy = new TDoubleArrayList();
-        TDoubleArrayList sjx = new TDoubleArrayList();
-        TDoubleArrayList sjy = new TDoubleArrayList();
-
-        while (i < N && j < M) {
-            int ni = i+1;
-            for (; ni < N && !Double.isNaN(dataset.getXValue(0, ni)); ++ni);
-            for (; ni < N &&  Double.isNaN(dataset.getXValue(0, ni)); ++ni);
-
-            int nj = j+1;
-            for (; nj < M && !Double.isNaN(dataset.getXValue(1, nj)); ++nj);
-            for (; nj < M &&  Double.isNaN(dataset.getXValue(1, nj)); ++nj);
-
-            if (ni == N && nj == M) { // no more splits
-                log.debug("no more splits ....");
-                for (; i < ni; ++i) {
-                    double x = dataset.getXValue(0, i);
-                    double y = dataset.getYValue(0, i);
-                    if (!Double.isNaN(x)
-                    &&  !Double.isNaN(y)
-                    &&  add(six, x)) {
-                        siy.add(y);
-                    }
-                }
-                for (; j < nj; ++j) {
-                    double x = dataset.getXValue(1, j);
-                    double y = dataset.getYValue(1, j);
-                    if (!Double.isNaN(x)
-                    &&  !Double.isNaN(y)
-                    &&  add(sjx, x)) {
-                        sjy.add(y);
-                    }
-                }
-                if (!six.isEmpty() && !sjx.isEmpty()) {
-                    DefaultXYDataset ds = new DefaultXYDataset();
-                    addSeries(ds, dataset.getSeriesKey(0), six, siy);
-                    addSeries(ds, dataset.getSeriesKey(1), sjx, sjy);
-                    datasets.add(ds);
-                }
-                break;
-            }
-
-            if (debug) {
-                log.debug("ni: " + ni + " " + N);
-                log.debug("nj: " + nj + " " + M);
-            }
-
-            double xni = ni < N
-                ? dataset.getXValue(0, ni)
-                : Double.MAX_VALUE;
-
-            double xnj = nj < M
-                ? dataset.getXValue(1, nj)
-                : Double.MAX_VALUE;
-
-            double xns = Math.min(xni, xnj);
-
-            double pushxi = Double.NaN;
-            double pushyi = Double.NaN;
-            double pushxj = Double.NaN;
-            double pushyj = Double.NaN;
-
-            for (; i < ni; ++i) {
-                double x = dataset.getXValue(0, i);
-                double y = dataset.getYValue(0, i);
-                if (Double.isNaN(x) || Double.isNaN(y)) {
-                    continue;
-                }
-                if (x < xns) {
-                    if (add(six, x)) {
-                        siy.add(y);
-                    }
-                    continue;
-                }
-                if (x == xns) { // exact match
-                    if (add(six, x)) {
-                        siy.add(y);
-                    }
-                    pushxi = x; pushyi = y;
-                }
-                else { // x > xns: intersection
-                    if (debug) {
-                        log.debug("xns: " + xns);
-                        log.debug("x/y: " + x + " / " + y);
-                    }
-                    int SIX = six.size();
-                    if (SIX > 0) { // should always be true
-                        double yns = Linear.linear(
-                            xns,
-                            six.getQuick(SIX-1), x,
-                            siy.getQuick(SIX-1), y);
-                        if (debug) {
-                            log.debug("intersection at: " + yns);
-                        }
-                        if (add(six, xns)) {
-                            siy.add(yns);
-                        }
-                        pushxi = xns;
-                        pushyi = yns;
-                    }
-                }
-                break; // Split point reached.
-            }
-
-            for (; j < nj; ++j) {
-                double x = dataset.getXValue(1, j);
-                double y = dataset.getYValue(1, j);
-                if (Double.isNaN(x) || Double.isNaN(y)) {
-                    continue;
-                }
-                if (x < xns) {
-                    if (add(sjx, x)) {
-                        sjy.add(y);
-                    }
-                    continue;
-                }
-                if (x == xns) { // exact match
-                    if (add(sjx, x)) {
-                        sjy.add(y);
-                    }
-                    pushxj = x; pushyj = y;
-                }
-                else { // x > xns: intersection
-                    int SJX = sjx.size();
-                    if (SJX > 0) { // should always be true
-                        double yns = Linear.linear(
-                            xns,
-                            sjx.getQuick(SJX-1), x,
-                            sjy.getQuick(SJX-1), y);
-                        if (debug) {
-                            log.debug("intersection at: " + yns);
-                        }
-                        if (add(sjx, xns)) {
-                            sjy.add(yns);
-                        }
-                        pushxj = xns; pushyj = yns;
-                    }
-                }
-                break; // Split point reached.
-            }
-
-            if (!six.isEmpty() && !sjx.isEmpty()) {
-                DefaultXYDataset ds = new DefaultXYDataset();
-                addSeries(ds, dataset.getSeriesKey(0), six, siy);
-                addSeries(ds, dataset.getSeriesKey(1), sjx, sjy);
-                datasets.add(ds);
-            }
-
-            six.resetQuick(); siy.resetQuick();
-            sjx.resetQuick(); sjy.resetQuick();
-
-            // Push split points.
-            if (!Double.isNaN(pushxi)) {
-                six.add(pushxi);
-                siy.add(pushyi);
-            }
-
-            if (!Double.isNaN(pushxj)) {
-                sjx.add(pushxj);
-                sjy.add(pushyj);
-            }
-        }
-
-        // Copy the rest.
-        for (; i < N; ++i) {
-            double x = dataset.getXValue(0, i);
-            double y = dataset.getXValue(0, i);
-            if (!Double.isNaN(x)
-            &&  !Double.isNaN(y)
-            &&  add(six, x)) {
-                siy.add(y);
-            }
-        }
-
-        for (; j < M; ++j) {
-            double x = dataset.getXValue(1, j);
-            double y = dataset.getXValue(1, j);
-            if (!Double.isNaN(x)
-            &&  !Double.isNaN(y)
-            &&  add(sjx, x)) {
-                sjy.add(y);
-            }
-        }
-
-        // Build final dataset.
-        if (!six.isEmpty() && !sjx.isEmpty()) {
-            DefaultXYDataset ds = new DefaultXYDataset();
-            addSeries(ds, dataset.getSeriesKey(0), six, siy);
-            addSeries(ds, dataset.getSeriesKey(1), sjx, sjy);
-            datasets.add(ds);
-        }
-
-        if (debug) {
-            log.debug("datasets after split: " + datasets.size());
-        }
-
-        return datasets;
-    }
-
-    public static List<XYDataset> splitByNaNs(XYDataset dataset)  {
-
-        switch (dataset.getSeriesCount()) {
-            case 0:
-                return Collections.<XYDataset>emptyList();
-            case 1:
-                return splitByNaNsOneSeries(dataset);
-            default: // two or more
-                return splitByNaNsTwoSeries(dataset);
-        }
-    }
-
-
-    /**
-     * Draws the visual representation of a single data item.
-     *
-     * @param g2  the graphics device.
-     * @param state  the renderer state.
-     * @param dataArea  the area within which the data is being drawn.
-     * @param info  collects information about the drawing.
-     * @param plot  the plot (can be used to obtain standard color
-     *              information etc).
-     * @param domainAxis  the domain (horizontal) axis.
-     * @param rangeAxis  the range (vertical) axis.
-     * @param dataset  the dataset.
-     * @param series  the series index (zero-based).
-     * @param item  the item index (zero-based).
-     * @param crosshairState  crosshair information for the plot
-     *                        (<code>null</code> permitted).
-     * @param pass  the pass index.
-     */
-    public void drawItem(Graphics2D g2,
-                         XYItemRendererState state,
-                         Rectangle2D dataArea,
-                         PlotRenderingInfo info,
-                         XYPlot plot,
-                         ValueAxis domainAxis,
-                         ValueAxis rangeAxis,
-                         XYDataset dataset,
-                         int series,
-                         int item,
-                         CrosshairState crosshairState,
-                         int pass) {
-        switch (pass) {
-            case 0:
-                for (XYDataset ds: splitByNaNs(dataset)) {
-                    drawItemPass0(g2, dataArea, info,
-                        plot, domainAxis, rangeAxis,
-                        ds, series, item, crosshairState);
-                }
-                break;
-            case 1:
-                drawItemPass1(g2, dataArea, info,
-                    plot, domainAxis, rangeAxis,
-                    dataset, series, item, crosshairState);
-        }
-
-        // Find geometric middle, calculate area and paint a string with it here.
-        if (pass == 1 && this.labelArea && areaLabelNumberFormat != null && areaLabelTamplate != null) {
-            double center_x = centroid.getX();
-            double center_y = centroid.getY();
-            center_x = domainAxis.valueToJava2D(center_x, dataArea,
-                plot.getDomainAxisEdge());
-            center_y = rangeAxis.valueToJava2D(center_y, dataArea,
-                plot.getRangeAxisEdge());
-
-            // Respect text-extend if text should appear really centered.
-
-            float area = 0f;
-            if (areaCalculationMode == CALCULATE_POSITIVE_AREA
-               || areaCalculationMode == CALCULATE_ALL_AREA) {
-                area += Math.abs(positiveArea);
-            }
-            if (areaCalculationMode == CALCULATE_NEGATIVE_AREA
-               || areaCalculationMode == CALCULATE_ALL_AREA) {
-                area += Math.abs(negativeArea);
-            }
-            if (area != 0f) {
-                Color oldColor = g2.getColor();
-                Font oldFont = g2.getFont();
-                g2.setFont(labelFont);
-                String labelText = String.format(this.areaLabelTamplate,
-                    areaLabelNumberFormat.format(area));
-                if (labelBGColor != null) {
-                    EnhancedLineAndShapeRenderer.drawTextBox(g2, labelText,
-                        (float)center_x, (float)center_y, labelBGColor);
-                }
-                g2.setColor(labelColor);
-                g2.drawString(labelText, (float)center_x, (float)center_y);
-                g2.setFont(oldFont);
-                g2.setColor(oldColor);
-            }
-        }
-    }
-
-    /**
-     * Draws the visual representation of a single data item, first pass.
-     *
-     * @param x_graphics  the graphics device.
-     * @param x_dataArea  the area within which the data is being drawn.
-     * @param x_info  collects information about the drawing.
-     * @param x_plot  the plot (can be used to obtain standard color
-     *                information etc).
-     * @param x_domainAxis  the domain (horizontal) axis.
-     * @param x_rangeAxis  the range (vertical) axis.
-     * @param x_dataset  the dataset.
-     * @param x_series  the series index (zero-based).
-     * @param x_item  the item index (zero-based).
-     * @param x_crosshairState  crosshair information for the plot
-     *                          (<code>null</code> permitted).
-     */
-    protected void drawItemPass0(Graphics2D x_graphics,
-                                 Rectangle2D x_dataArea,
-                                 PlotRenderingInfo x_info,
-                                 XYPlot x_plot,
-                                 ValueAxis x_domainAxis,
-                                 ValueAxis x_rangeAxis,
-                                 XYDataset x_dataset,
-                                 int x_series,
-                                 int x_item,
-                                 CrosshairState x_crosshairState) {
-
-        if (!((0 == x_series) && (0 == x_item))) {
-            return;
-        }
-
-        boolean b_impliedZeroSubtrahend = (1 == x_dataset.getSeriesCount());
-
-        // check if either series is a degenerate case (i.e. less than 2 points)
-        if (isEitherSeriesDegenerate(x_dataset, b_impliedZeroSubtrahend)) {
-            return;
-        }
-
-        // check if series are disjoint (i.e. domain-spans do not overlap)
-        if (!b_impliedZeroSubtrahend && areSeriesDisjoint(x_dataset)) {
-            return;
-        }
-
-        // polygon definitions
-        LinkedList l_minuendXs    = new LinkedList();
-        LinkedList l_minuendYs    = new LinkedList();
-        LinkedList l_subtrahendXs = new LinkedList();
-        LinkedList l_subtrahendYs = new LinkedList();
-        LinkedList l_polygonXs    = new LinkedList();
-        LinkedList l_polygonYs    = new LinkedList();
-
-        // state
-        int l_minuendItem      = 0;
-        int l_minuendItemCount = x_dataset.getItemCount(0);
-        Double l_minuendCurX   = null;
-        Double l_minuendNextX  = null;
-        Double l_minuendCurY   = null;
-        Double l_minuendNextY  = null;
-        double l_minuendMaxY   = Double.NEGATIVE_INFINITY;
-        double l_minuendMinY   = Double.POSITIVE_INFINITY;
-
-        int l_subtrahendItem      = 0;
-        int l_subtrahendItemCount = 0; // actual value set below
-        Double l_subtrahendCurX   = null;
-        Double l_subtrahendNextX  = null;
-        Double l_subtrahendCurY   = null;
-        Double l_subtrahendNextY  = null;
-        double l_subtrahendMaxY   = Double.NEGATIVE_INFINITY;
-        double l_subtrahendMinY   = Double.POSITIVE_INFINITY;
-
-        // if a subtrahend is not specified, assume it is zero
-        if (b_impliedZeroSubtrahend) {
-            l_subtrahendItem      = 0;
-            l_subtrahendItemCount = 2;
-            l_subtrahendCurX      = new Double(x_dataset.getXValue(0, 0));
-            l_subtrahendNextX     = new Double(x_dataset.getXValue(0,
-                    (l_minuendItemCount - 1)));
-            l_subtrahendCurY      = new Double(0.0);
-            l_subtrahendNextY     = new Double(0.0);
-            l_subtrahendMaxY      = 0.0;
-            l_subtrahendMinY      = 0.0;
-
-            l_subtrahendXs.add(l_subtrahendCurX);
-            l_subtrahendYs.add(l_subtrahendCurY);
-        }
-        else {
-            l_subtrahendItemCount = x_dataset.getItemCount(1);
-        }
-
-        boolean b_minuendDone           = false;
-        boolean b_minuendAdvanced       = true;
-        boolean b_minuendAtIntersect    = false;
-        boolean b_minuendFastForward    = false;
-        boolean b_subtrahendDone        = false;
-        boolean b_subtrahendAdvanced    = true;
-        boolean b_subtrahendAtIntersect = false;
-        boolean b_subtrahendFastForward = false;
-        boolean b_colinear              = false;
-
-        boolean b_positive;
-
-        // coordinate pairs
-        double l_x1 = 0.0, l_y1 = 0.0; // current minuend point
-        double l_x2 = 0.0, l_y2 = 0.0; // next minuend point
-        double l_x3 = 0.0, l_y3 = 0.0; // current subtrahend point
-        double l_x4 = 0.0, l_y4 = 0.0; // next subtrahend point
-
-        // fast-forward through leading tails
-        boolean b_fastForwardDone = false;
-        while (!b_fastForwardDone) {
-            // get the x and y coordinates
-            l_x1 = x_dataset.getXValue(0, l_minuendItem);
-            l_y1 = x_dataset.getYValue(0, l_minuendItem);
-            l_x2 = x_dataset.getXValue(0, l_minuendItem + 1);
-            l_y2 = x_dataset.getYValue(0, l_minuendItem + 1);
-
-            l_minuendCurX  = new Double(l_x1);
-            l_minuendCurY  = new Double(l_y1);
-            l_minuendNextX = new Double(l_x2);
-            l_minuendNextY = new Double(l_y2);
-
-            if (b_impliedZeroSubtrahend) {
-                l_x3 = l_subtrahendCurX.doubleValue();
-                l_y3 = l_subtrahendCurY.doubleValue();
-                l_x4 = l_subtrahendNextX.doubleValue();
-                l_y4 = l_subtrahendNextY.doubleValue();
-            }
-            else {
-                l_x3 = x_dataset.getXValue(1, l_subtrahendItem);
-                l_y3 = x_dataset.getYValue(1, l_subtrahendItem);
-                l_x4 = x_dataset.getXValue(1, l_subtrahendItem + 1);
-                l_y4 = x_dataset.getYValue(1, l_subtrahendItem + 1);
-
-                l_subtrahendCurX  = new Double(l_x3);
-                l_subtrahendCurY  = new Double(l_y3);
-                l_subtrahendNextX = new Double(l_x4);
-                l_subtrahendNextY = new Double(l_y4);
-            }
-
-            if (l_x2 <= l_x3) {
-                // minuend needs to be fast forwarded
-                l_minuendItem++;
-                b_minuendFastForward = true;
-                continue;
-            }
-
-            if (l_x4 <= l_x1) {
-                // subtrahend needs to be fast forwarded
-                l_subtrahendItem++;
-                b_subtrahendFastForward = true;
-                continue;
-            }
-
-            // check if initial polygon needs to be clipped
-            if ((l_x3 < l_x1) && (l_x1 < l_x4)) {
-                // project onto subtrahend
-                double l_slope   = (l_y4 - l_y3) / (l_x4 - l_x3);
-                l_subtrahendCurX = l_minuendCurX;
-                l_subtrahendCurY = new Double((l_slope * l_x1)
-                        + (l_y3 - (l_slope * l_x3)));
-
-                l_subtrahendXs.add(l_subtrahendCurX);
-                l_subtrahendYs.add(l_subtrahendCurY);
-            }
-
-            if ((l_x1 < l_x3) && (l_x3 < l_x2)) {
-                // project onto minuend
-                double l_slope = (l_y2 - l_y1) / (l_x2 - l_x1);
-                l_minuendCurX  = l_subtrahendCurX;
-                l_minuendCurY  = new Double((l_slope * l_x3)
-                        + (l_y1 - (l_slope * l_x1)));
-
-                l_minuendXs.add(l_minuendCurX);
-                l_minuendYs.add(l_minuendCurY);
-            }
-
-            l_minuendMaxY    = l_minuendCurY.doubleValue();
-            l_minuendMinY    = l_minuendCurY.doubleValue();
-            l_subtrahendMaxY = l_subtrahendCurY.doubleValue();
-            l_subtrahendMinY = l_subtrahendCurY.doubleValue();
-
-            b_fastForwardDone = true;
-        }
-
-        // start of algorithm
-        while (!b_minuendDone && !b_subtrahendDone) {
-            if (!b_minuendDone && !b_minuendFastForward && b_minuendAdvanced) {
-                l_x1 = x_dataset.getXValue(0, l_minuendItem);
-                l_y1 = x_dataset.getYValue(0, l_minuendItem);
-                l_minuendCurX = new Double(l_x1);
-                l_minuendCurY = new Double(l_y1);
-
-                if (!b_minuendAtIntersect) {
-                    l_minuendXs.add(l_minuendCurX);
-                    l_minuendYs.add(l_minuendCurY);
-                }
-
-                l_minuendMaxY = Math.max(l_minuendMaxY, l_y1);
-                l_minuendMinY = Math.min(l_minuendMinY, l_y1);
-
-                l_x2 = x_dataset.getXValue(0, l_minuendItem + 1);
-                l_y2 = x_dataset.getYValue(0, l_minuendItem + 1);
-                l_minuendNextX = new Double(l_x2);
-                l_minuendNextY = new Double(l_y2);
-            }
-
-            // never updated the subtrahend if it is implied to be zero
-            if (!b_impliedZeroSubtrahend && !b_subtrahendDone
-                    && !b_subtrahendFastForward && b_subtrahendAdvanced) {
-                l_x3 = x_dataset.getXValue(1, l_subtrahendItem);
-                l_y3 = x_dataset.getYValue(1, l_subtrahendItem);
-                l_subtrahendCurX = new Double(l_x3);
-                l_subtrahendCurY = new Double(l_y3);
-
-                if (!b_subtrahendAtIntersect) {
-                    l_subtrahendXs.add(l_subtrahendCurX);
-                    l_subtrahendYs.add(l_subtrahendCurY);
-                }
-
-                l_subtrahendMaxY = Math.max(l_subtrahendMaxY, l_y3);
-                l_subtrahendMinY = Math.min(l_subtrahendMinY, l_y3);
-
-                l_x4 = x_dataset.getXValue(1, l_subtrahendItem + 1);
-                l_y4 = x_dataset.getYValue(1, l_subtrahendItem + 1);
-                l_subtrahendNextX = new Double(l_x4);
-                l_subtrahendNextY = new Double(l_y4);
-            }
-
-            // deassert b_*FastForward (only matters for 1st time through loop)
-            b_minuendFastForward    = false;
-            b_subtrahendFastForward = false;
-
-            Double l_intersectX = null;
-            Double l_intersectY = null;
-            boolean b_intersect = false;
-
-            b_minuendAtIntersect    = false;
-            b_subtrahendAtIntersect = false;
-
-            // check for intersect
-            if ((l_x2 == l_x4) && (l_y2 == l_y4)) {
-                // check if line segments are colinear
-                if ((l_x1 == l_x3) && (l_y1 == l_y3)) {
-                    b_colinear = true;
-                }
-                else {
-                    // the intersect is at the next point for both the minuend
-                    // and subtrahend
-                    l_intersectX = new Double(l_x2);
-                    l_intersectY = new Double(l_y2);
-
-                    b_intersect             = true;
-                    b_minuendAtIntersect    = true;
-                    b_subtrahendAtIntersect = true;
-                 }
-            }
-            else {
-                // compute common denominator
-                double l_denominator = ((l_y4 - l_y3) * (l_x2 - l_x1))
-                        - ((l_x4 - l_x3) * (l_y2 - l_y1));
-
-                // compute common deltas
-                double l_deltaY = l_y1 - l_y3;
-                double l_deltaX = l_x1 - l_x3;
-
-                // compute numerators
-                double l_numeratorA = ((l_x4 - l_x3) * l_deltaY)
-                        - ((l_y4 - l_y3) * l_deltaX);
-                double l_numeratorB = ((l_x2 - l_x1) * l_deltaY)
-                        - ((l_y2 - l_y1) * l_deltaX);
-
-                // check if line segments are colinear
-                if ((0 == l_numeratorA) && (0 == l_numeratorB)
-                        && (0 == l_denominator)) {
-                    b_colinear = true;
-                }
-                else {
-                    // check if previously colinear
-                    if (b_colinear) {
-                        // clear colinear points and flag
-                        l_minuendXs.clear();
-                        l_minuendYs.clear();
-                        l_subtrahendXs.clear();
-                        l_subtrahendYs.clear();
-                        l_polygonXs.clear();
-                        l_polygonYs.clear();
-
-                        b_colinear = false;
-
-                        // set new starting point for the polygon
-                        boolean b_useMinuend = ((l_x3 <= l_x1)
-                                && (l_x1 <= l_x4));
-                        l_polygonXs.add(b_useMinuend ? l_minuendCurX
-                                : l_subtrahendCurX);
-                        l_polygonYs.add(b_useMinuend ? l_minuendCurY
-                                : l_subtrahendCurY);
-                    }
-
-                    // compute slope components
-                    double l_slopeA = l_numeratorA / l_denominator;
-                    double l_slopeB = l_numeratorB / l_denominator;
-
-                    // check if the line segments intersect
-                    if ((0 < l_slopeA) && (l_slopeA <= 1) && (0 < l_slopeB)
-                            && (l_slopeB <= 1)) {
-                        // compute the point of intersection
-                        double l_xi = l_x1 + (l_slopeA * (l_x2 - l_x1));
-                        double l_yi = l_y1 + (l_slopeA * (l_y2 - l_y1));
-
-                        l_intersectX            = new Double(l_xi);
-                        l_intersectY            = new Double(l_yi);
-                        b_intersect             = true;
-                        b_minuendAtIntersect    = ((l_xi == l_x2)
-                                && (l_yi == l_y2));
-                        b_subtrahendAtIntersect = ((l_xi == l_x4)
-                                && (l_yi == l_y4));
-
-                        // advance minuend and subtrahend to intesect
-                        l_minuendCurX    = l_intersectX;
-                        l_minuendCurY    = l_intersectY;
-                        l_subtrahendCurX = l_intersectX;
-                        l_subtrahendCurY = l_intersectY;
-                    }
-                }
-            }
-
-            if (b_intersect) {
-                // create the polygon
-                // add the minuend's points to polygon
-                l_polygonXs.addAll(l_minuendXs);
-                l_polygonYs.addAll(l_minuendYs);
-
-                // add intersection point to the polygon
-                l_polygonXs.add(l_intersectX);
-                l_polygonYs.add(l_intersectY);
-
-                // add the subtrahend's points to the polygon in reverse
-                Collections.reverse(l_subtrahendXs);
-                Collections.reverse(l_subtrahendYs);
-                l_polygonXs.addAll(l_subtrahendXs);
-                l_polygonYs.addAll(l_subtrahendYs);
-
-                // create an actual polygon
-                b_positive = (l_subtrahendMaxY <= l_minuendMaxY)
-                        && (l_subtrahendMinY <= l_minuendMinY);
-                createPolygon(x_graphics, x_dataArea, x_plot, x_domainAxis,
-                        x_rangeAxis, b_positive, l_polygonXs, l_polygonYs);
-
-                // clear the point vectors
-                l_minuendXs.clear();
-                l_minuendYs.clear();
-                l_subtrahendXs.clear();
-                l_subtrahendYs.clear();
-                l_polygonXs.clear();
-                l_polygonYs.clear();
-
-                // set the maxY and minY values to intersect y-value
-                double l_y       = l_intersectY.doubleValue();
-                l_minuendMaxY    = l_y;
-                l_subtrahendMaxY = l_y;
-                l_minuendMinY    = l_y;
-                l_subtrahendMinY = l_y;
-
-                // add interection point to new polygon
-                l_polygonXs.add(l_intersectX);
-                l_polygonYs.add(l_intersectY);
-            }
-
-            // advance the minuend if needed
-            if (l_x2 <= l_x4) {
-                l_minuendItem++;
-                b_minuendAdvanced = true;
-            }
-            else {
-                b_minuendAdvanced = false;
-            }
-
-            // advance the subtrahend if needed
-            if (l_x4 <= l_x2) {
-                l_subtrahendItem++;
-                b_subtrahendAdvanced = true;
-            }
-            else {
-                b_subtrahendAdvanced = false;
-            }
-
-            b_minuendDone    = (l_minuendItem == (l_minuendItemCount - 1));
-            b_subtrahendDone = (l_subtrahendItem == (l_subtrahendItemCount
-                    - 1));
-        }
-
-        // check if the final polygon needs to be clipped
-        if (b_minuendDone && (l_x3 < l_x2) && (l_x2 < l_x4)) {
-            // project onto subtrahend
-            double l_slope    = (l_y4 - l_y3) / (l_x4 - l_x3);
-            l_subtrahendNextX = l_minuendNextX;
-            l_subtrahendNextY = new Double((l_slope * l_x2)
-                    + (l_y3 - (l_slope * l_x3)));
-        }
-
-        if (b_subtrahendDone && (l_x1 < l_x4) && (l_x4 < l_x2)) {
-            // project onto minuend
-            double l_slope = (l_y2 - l_y1) / (l_x2 - l_x1);
-            l_minuendNextX = l_subtrahendNextX;
-            l_minuendNextY = new Double((l_slope * l_x4)
-                    + (l_y1 - (l_slope * l_x1)));
-        }
-
-        // consider last point of minuend and subtrahend for determining
-        // positivity
-        l_minuendMaxY    = Math.max(l_minuendMaxY,
-                l_minuendNextY.doubleValue());
-        l_subtrahendMaxY = Math.max(l_subtrahendMaxY,
-                l_subtrahendNextY.doubleValue());
-        l_minuendMinY    = Math.min(l_minuendMinY,
-                l_minuendNextY.doubleValue());
-        l_subtrahendMinY = Math.min(l_subtrahendMinY,
-                l_subtrahendNextY.doubleValue());
-
-        // add the last point of the minuned and subtrahend
-        l_minuendXs.add(l_minuendNextX);
-        l_minuendYs.add(l_minuendNextY);
-        l_subtrahendXs.add(l_subtrahendNextX);
-        l_subtrahendYs.add(l_subtrahendNextY);
-
-        // create the polygon
-        // add the minuend's points to polygon
-        l_polygonXs.addAll(l_minuendXs);
-        l_polygonYs.addAll(l_minuendYs);
-
-        // add the subtrahend's points to the polygon in reverse
-        Collections.reverse(l_subtrahendXs);
-        Collections.reverse(l_subtrahendYs);
-        l_polygonXs.addAll(l_subtrahendXs);
-        l_polygonYs.addAll(l_subtrahendYs);
-
-        // create an actual polygon
-        b_positive = (l_subtrahendMaxY <= l_minuendMaxY)
-                && (l_subtrahendMinY <= l_minuendMinY);
-        createPolygon(x_graphics, x_dataArea, x_plot, x_domainAxis,
-                x_rangeAxis, b_positive, l_polygonXs, l_polygonYs);
-    }
-
-    /**
-     * Draws the visual representation of a single data item, second pass.  In
-     * the second pass, the renderer draws the lines and shapes for the
-     * individual points in the two series.
-     *
-     * @param x_graphics  the graphics device.
-     * @param x_dataArea  the area within which the data is being drawn.
-     * @param x_info  collects information about the drawing.
-     * @param x_plot  the plot (can be used to obtain standard color
-     *         information etc).
-     * @param x_domainAxis  the domain (horizontal) axis.
-     * @param x_rangeAxis  the range (vertical) axis.
-     * @param x_dataset  the dataset.
-     * @param x_series  the series index (zero-based).
-     * @param x_item  the item index (zero-based).
-     * @param x_crosshairState  crosshair information for the plot
-     *                          (<code>null</code> permitted).
-     */
-    protected void drawItemPass1(Graphics2D x_graphics,
-                                 Rectangle2D x_dataArea,
-                                 PlotRenderingInfo x_info,
-                                 XYPlot x_plot,
-                                 ValueAxis x_domainAxis,
-                                 ValueAxis x_rangeAxis,
-                                 XYDataset x_dataset,
-                                 int x_series,
-                                 int x_item,
-                                 CrosshairState x_crosshairState) {
-
-        Shape l_entityArea = null;
-        EntityCollection l_entities = null;
-        if (null != x_info) {
-            l_entities = x_info.getOwner().getEntityCollection();
-        }
-
-        Paint l_seriesPaint   = getItemPaint(x_series, x_item);
-        Stroke l_seriesStroke = getItemStroke(x_series, x_item);
-        x_graphics.setPaint(l_seriesPaint);
-        x_graphics.setStroke(l_seriesStroke);
-
-        PlotOrientation l_orientation      = x_plot.getOrientation();
-        RectangleEdge l_domainAxisLocation = x_plot.getDomainAxisEdge();
-        RectangleEdge l_rangeAxisLocation  = x_plot.getRangeAxisEdge();
-
-        double l_x0 = x_dataset.getXValue(x_series, x_item);
-        double l_y0 = x_dataset.getYValue(x_series, x_item);
-        double l_x1 = x_domainAxis.valueToJava2D(l_x0, x_dataArea,
-                l_domainAxisLocation);
-        double l_y1 = x_rangeAxis.valueToJava2D(l_y0, x_dataArea,
-                l_rangeAxisLocation);
-
-        // These are the shapes of the series items.
-        if (getShapesVisible()) {
-            Shape l_shape = getItemShape(x_series, x_item);
-            if (l_orientation == PlotOrientation.HORIZONTAL) {
-                l_shape = ShapeUtilities.createTranslatedShape(l_shape,
-                        l_y1, l_x1);
-            }
-            else {
-                l_shape = ShapeUtilities.createTranslatedShape(l_shape,
-                        l_x1, l_y1);
-            }
-            if (l_shape.intersects(x_dataArea)) {
-                x_graphics.setPaint(getItemPaint(x_series, x_item));
-                x_graphics.fill(l_shape);
-                /* TODO We could draw the shapes of single items here.
-                if (drawOutline) {
-                    x_graphics.setPaint(this.outlinePaint);
-                    x_graphics.setStroke(this.outlineStroke);
-                    x_graphics.draw(l_shape);
-                }
-                */
-            }
-            l_entityArea = l_shape;
-        } // if (getShapesVisible())
-
-        // add an entity for the item...
-        if (null != l_entities) {
-            if (null == l_entityArea) {
-                l_entityArea = new Rectangle2D.Double((l_x1 - 2), (l_y1 - 2),
-                        4, 4);
-            }
-            String l_tip = null;
-            XYToolTipGenerator l_tipGenerator = getToolTipGenerator(x_series,
-                    x_item);
-            if (null != l_tipGenerator) {
-                l_tip = l_tipGenerator.generateToolTip(x_dataset, x_series,
-                        x_item);
-            }
-            String l_url = null;
-            XYURLGenerator l_urlGenerator = getURLGenerator();
-            if (null != l_urlGenerator) {
-                l_url = l_urlGenerator.generateURL(x_dataset, x_series,
-                        x_item);
-            }
-            XYItemEntity l_entity = new XYItemEntity(l_entityArea, x_dataset,
-                    x_series, x_item, l_tip, l_url);
-            l_entities.add(l_entity);
-        }
-
-        // draw the item label if there is one...
-        if (isItemLabelVisible(x_series, x_item)) {
-            drawItemLabel(x_graphics, l_orientation, x_dataset, x_series,
-                          x_item, l_x1, l_y1, (l_y1 < 0.0));
-        }
-
-        int l_domainAxisIndex = x_plot.getDomainAxisIndex(x_domainAxis);
-        int l_rangeAxisIndex  = x_plot.getRangeAxisIndex(x_rangeAxis);
-        updateCrosshairValues(x_crosshairState, l_x0, l_y0, l_domainAxisIndex,
-                              l_rangeAxisIndex, l_x1, l_y1, l_orientation);
-
-        if (0 == x_item) {
-            return;
-        }
-
-        double l_x2 = x_domainAxis.valueToJava2D(x_dataset.getXValue(x_series,
-                (x_item - 1)), x_dataArea, l_domainAxisLocation);
-        double l_y2 = x_rangeAxis.valueToJava2D(x_dataset.getYValue(x_series,
-                (x_item - 1)), x_dataArea, l_rangeAxisLocation);
-
-        Line2D l_line = null;
-        if (PlotOrientation.HORIZONTAL == l_orientation) {
-            l_line = new Line2D.Double(l_y1, l_x1, l_y2, l_x2);
-        }
-        else if (PlotOrientation.VERTICAL == l_orientation) {
-            l_line = new Line2D.Double(l_x1, l_y1, l_x2, l_y2);
-        }
-
-        if ((null != l_line) && l_line.intersects(x_dataArea)) {
-            x_graphics.setPaint(getItemPaint(x_series, x_item));
-            x_graphics.setStroke(getItemStroke(x_series, x_item));
-            if (drawOriginalSeries) {
-                x_graphics.setPaint(this.outlinePaint);
-                x_graphics.setStroke(this.outlineStroke);
-                x_graphics.draw(l_line);
-            }
-        }
-    }
-
-    /**
-     * Determines if a dataset is degenerate.  A degenerate dataset is a
-     * dataset where either series has less than two (2) points.
-     *
-     * @param x_dataset  the dataset.
-     * @param x_impliedZeroSubtrahend  if false, do not check the subtrahend
-     *
-     * @return true if the dataset is degenerate.
-     */
-    private boolean isEitherSeriesDegenerate(XYDataset x_dataset,
-            boolean x_impliedZeroSubtrahend) {
-
-        if (x_impliedZeroSubtrahend) {
-            return (x_dataset.getItemCount(0) < 2);
-        }
-
-        return ((x_dataset.getItemCount(0) < 2)
-                || (x_dataset.getItemCount(1) < 2));
-    }
-
-    /**
-     * Determines if the two (2) series are disjoint.
-     * Disjoint series do not overlap in the domain space.
-     *
-     * @param x_dataset  the dataset.
-     *
-     * @return true if the dataset is degenerate.
-     */
-    private boolean areSeriesDisjoint(XYDataset x_dataset) {
-
-        int l_minuendItemCount = x_dataset.getItemCount(0);
-        double l_minuendFirst  = x_dataset.getXValue(0, 0);
-        double l_minuendLast   = x_dataset.getXValue(0, l_minuendItemCount - 1);
-
-        int l_subtrahendItemCount = x_dataset.getItemCount(1);
-        double l_subtrahendFirst  = x_dataset.getXValue(1, 0);
-        double l_subtrahendLast   = x_dataset.getXValue(1,
-                l_subtrahendItemCount - 1);
-
-        return ((l_minuendLast < l_subtrahendFirst)
-                || (l_subtrahendLast < l_minuendFirst));
-    }
-
-
-    public void updateCentroid(Object [] xValues, Object [] yValues) {
-        double x = 0d, y = 0d;
-
-        for (int i = 0, N = xValues.length; i < N; ++i) {
-            x += ((Double)xValues[i]).doubleValue();
-            y += ((Double)yValues[i]).doubleValue();
-        }
-
-        x /= xValues.length;
-        y /= yValues.length;
-
-        centroidNPoints++;
-        double factorNew = 1d / centroidNPoints;
-        double factorOld = 1d - factorNew;
-
-        centroid = new Point2D.Double((factorNew * x + factorOld * centroid.x),
-            (factorNew * y + factorOld * centroid.y));
-    }
-
-
-    public static double calculateArea(Object [] xValues, Object [] yValues) {
-        double area = 0d;
-
-        for (int i = 0, N = xValues.length; i < N; ++i) {
-            int k = (i + 1) % N;
-            double xi = ((Double)xValues[i]).doubleValue();
-            double yi = ((Double)yValues[i]).doubleValue();
-            double xk = ((Double)xValues[k]).doubleValue();
-            double yk = ((Double)yValues[k]).doubleValue();
-
-            area += xi*yk;
-            area -= xk*yi;
-            // TODO centroid calculation here?
-        }
-
-        return 0.5d*area;
-    }
-
-    /**
-     * Draws the visual representation of a polygon
-     *
-     * @param x_graphics  the graphics device.
-     * @param x_dataArea  the area within which the data is being drawn.
-     * @param x_plot  the plot (can be used to obtain standard color
-     *                information etc).
-     * @param x_domainAxis  the domain (horizontal) axis.
-     * @param x_rangeAxis  the range (vertical) axis.
-     * @param x_positive  indicates if the polygon is positive (true) or
-     *                    negative (false).
-     * @param x_xValues  a linked list of the x values (expects values to be
-     *                   of type Double).
-     * @param x_yValues  a linked list of the y values (expects values to be
-     *                   of type Double).
-     */
-    private void createPolygon (Graphics2D x_graphics,
-                                Rectangle2D x_dataArea,
-                                XYPlot      x_plot,
-                                ValueAxis   x_domainAxis,
-                                ValueAxis   x_rangeAxis,
-                                boolean     x_positive,
-                                LinkedList  x_xValues,
-                                LinkedList  x_yValues) {
-
-        PlotOrientation l_orientation      = x_plot.getOrientation();
-        RectangleEdge l_domainAxisLocation = x_plot.getDomainAxisEdge();
-        RectangleEdge l_rangeAxisLocation  = x_plot.getRangeAxisEdge();
-
-        Object[] l_xValues = x_xValues.toArray();
-        Object[] l_yValues = x_yValues.toArray();
-
-        double area = calculateArea(l_xValues, l_yValues)/2d;
-        if (x_positive) positiveArea += area;
-        else            negativeArea += area;
-        updateCentroid(l_xValues, l_yValues);
-
-        GeneralPath l_path = new GeneralPath();
-
-        if (PlotOrientation.VERTICAL == l_orientation) {
-            double l_x = x_domainAxis.valueToJava2D((
-                    (Double) l_xValues[0]).doubleValue(), x_dataArea,
-                    l_domainAxisLocation);
-            if (this.roundXCoordinates) {
-                l_x = Math.rint(l_x);
-            }
-
-            double l_y = x_rangeAxis.valueToJava2D((
-                    (Double) l_yValues[0]).doubleValue(), x_dataArea,
-                    l_rangeAxisLocation);
-
-            l_path.moveTo((float) l_x, (float) l_y);
-            for (int i = 1; i < l_xValues.length; i++) {
-                l_x = x_domainAxis.valueToJava2D((
-                        (Double) l_xValues[i]).doubleValue(), x_dataArea,
-                        l_domainAxisLocation);
-                if (this.roundXCoordinates) {
-                    l_x = Math.rint(l_x);
-                }
-
-                l_y = x_rangeAxis.valueToJava2D((
-                        (Double) l_yValues[i]).doubleValue(), x_dataArea,
-                        l_rangeAxisLocation);
-                l_path.lineTo((float) l_x, (float) l_y);
-            }
-            l_path.closePath();
-        }
-        else {
-            double l_x = x_domainAxis.valueToJava2D((
-                    (Double) l_xValues[0]).doubleValue(), x_dataArea,
-                    l_domainAxisLocation);
-            if (this.roundXCoordinates) {
-                l_x = Math.rint(l_x);
-            }
-
-            double l_y = x_rangeAxis.valueToJava2D((
-                    (Double) l_yValues[0]).doubleValue(), x_dataArea,
-                    l_rangeAxisLocation);
-
-            l_path.moveTo((float) l_y, (float) l_x);
-            for (int i = 1; i < l_xValues.length; i++) {
-                l_x = x_domainAxis.valueToJava2D((
-                        (Double) l_xValues[i]).doubleValue(), x_dataArea,
-                        l_domainAxisLocation);
-                if (this.roundXCoordinates) {
-                    l_x = Math.rint(l_x);
-                }
-
-                l_y = x_rangeAxis.valueToJava2D((
-                        (Double) l_yValues[i]).doubleValue(), x_dataArea,
-                        l_rangeAxisLocation);
-                l_path.lineTo((float) l_y, (float) l_x);
-            }
-            l_path.closePath();
-        }
-
-        if (l_path.intersects(x_dataArea)) {
-            x_graphics.setPaint(x_positive ? getPositivePaint()
-                    : getNegativePaint());
-            x_graphics.fill(l_path);
-            if (drawOutline) {
-                x_graphics.setStroke(this.outlineStroke);
-                x_graphics.setPaint(this.outlinePaint);
-                x_graphics.draw(l_path);
-            }
-        }
-    }
-
-    /**
-     * Returns a default legend item for the specified series.  Subclasses
-     * should override this method to generate customised items.
-     *
-     * @param datasetIndex  the dataset index (zero-based).
-     * @param series  the series index (zero-based).
-     *
-     * @return A legend item for the series.
-     */
-    public LegendItem getLegendItem(int datasetIndex, int series) {
-        LegendItem result = null;
-        XYPlot p = getPlot();
-        if (p != null) {
-            XYDataset dataset = p.getDataset(datasetIndex);
-            if (dataset != null) {
-                if (getItemVisible(series, 0)) {
-                    String label = getLegendItemLabelGenerator().generateLabel(
-                            dataset, series);
-                    String description = label;
-                    String toolTipText = null;
-                    if (getLegendItemToolTipGenerator() != null) {
-                        toolTipText
-                            = getLegendItemToolTipGenerator().generateLabel(
-                                    dataset, series);
-                    }
-                    String urlText = null;
-                    if (getLegendItemURLGenerator() != null) {
-                        urlText = getLegendItemURLGenerator().generateLabel(
-                                dataset, series);
-                    }
-                    // Individualized Paints:
-                    //Paint paint = lookupSeriesPaint(series);
-
-                    // "Area-Style"- Paint.
-                    Paint paint = getPositivePaint();
-                    Stroke stroke = lookupSeriesStroke(series);
-                    Shape line = getLegendLine();
-                    // Not-filled Shape:
-                    //result = new LegendItem(label, description,
-                    //        toolTipText, urlText, line, stroke, paint);
-
-                    if (drawOutline) {
-                        // TODO Include outline style in legenditem (there is a constructor for that)
-                    }
-
-                    // Filled Shape ("Area-Style").
-                    result = new LegendItem(label, description,
-                            toolTipText, urlText, line, paint);
-                    result.setLabelFont(lookupLegendTextFont(series));
-                    Paint labelPaint = lookupLegendTextPaint(series);
-                    if (labelPaint != null) {
-                        result.setLabelPaint(labelPaint);
-                    }
-                    result.setDataset(dataset);
-                    result.setDatasetIndex(datasetIndex);
-                    result.setSeriesKey(dataset.getSeriesKey(series));
-                    result.setSeriesIndex(series);
-                }
-            }
-
-        }
-
-        return result;
-    }
-
-    /**
-     * Tests this renderer for equality with an arbitrary object.
-     *
-     * @param obj  the object (<code>null</code> permitted).
-     *
-     * @return A boolean.
-     */
-    public boolean equals(Object obj) {
-        if (obj == this) {
-            return true;
-        }
-        if (!(obj instanceof StableXYDifferenceRenderer)) {
-            return false;
-        }
-        if (!super.equals(obj)) {
-            return false;
-        }
-        StableXYDifferenceRenderer that = (StableXYDifferenceRenderer) obj;
-        if (!PaintUtilities.equal(this.positivePaint, that.positivePaint)) {
-            return false;
-        }
-        if (!PaintUtilities.equal(this.negativePaint, that.negativePaint)) {
-            return false;
-        }
-        if (this.shapesVisible != that.shapesVisible) {
-            return false;
-        }
-        if (!ShapeUtilities.equal(this.legendShape, that.legendShape)) {
-            return false;
-        }
-        if (this.roundXCoordinates != that.roundXCoordinates) {
-            return false;
-        }
-        return true;
-    }
-
-    /**
-     * Returns a clone of the renderer.
-     *
-     * @return A clone.
-     *
-     * @throws CloneNotSupportedException  if the renderer cannot be cloned.
-     */
-    public Object clone() throws CloneNotSupportedException {
-        StableXYDifferenceRenderer clone = (StableXYDifferenceRenderer) super.clone();
-        clone.legendShape = ShapeUtilities.clone(this.legendShape);
-        return clone;
-    }
-
-    /**
-     * Provides serialization support.
-     *
-     * @param stream  the output stream.
-     *
-     * @throws IOException  if there is an I/O error.
-     */
-    private void writeObject(ObjectOutputStream stream) throws IOException {
-        stream.defaultWriteObject();
-        SerialUtilities.writePaint(this.positivePaint, stream);
-        SerialUtilities.writePaint(this.negativePaint, stream);
-        SerialUtilities.writeShape(this.legendShape, stream);
-    }
-
-    /**
-     * Provides serialization support.
-     *
-     * @param stream  the input stream.
-     *
-     * @throws IOException  if there is an I/O error.
-     * @throws ClassNotFoundException  if there is a classpath problem.
-     */
-    private void readObject(ObjectInputStream stream)
-        throws IOException, ClassNotFoundException {
-        stream.defaultReadObject();
-        this.positivePaint = SerialUtilities.readPaint(stream);
-        this.negativePaint = SerialUtilities.readPaint(stream);
-        this.legendShape = SerialUtilities.readShape(stream);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/jfree/StickyAxisAnnotation.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,133 +0,0 @@
-package de.intevation.flys.jfree;
-
-/**
- * Text, position on axis, and maybe a hit-point in a class.
- *
- * Idea is to draw a given text and a line to it from either axis.
- * This class just keeps the info.
- */
-public class StickyAxisAnnotation {
-
-    /** Simplified view on axes. */
-    public static enum SimpleAxis {
-        X_AXIS, /** Usually "horizontal". */
-        Y_AXIS, /** Usually "vertical". */
-        Y_AXIS2
-    }
-
-    /** The "symbolic" integer representing which axis to stick to. */
-    protected int axisSymbol;
-
-    /** Which axis to stick to. */
-    protected SimpleAxis stickyAxis = SimpleAxis.X_AXIS;
-
-    /** The 1-dimensional position of this annotation. */
-    protected float pos;
-
-    /**
-     * Optional field used when from axis a line should be drawn that
-     * hits a curve or something similar (current scenario: duration curves).
-     * This value is in the "other" dimension than the pos - field.
-     */
-    protected float hitPoint;
-
-    /** The text to display at axis. */
-    String text;
-
-
-    /**
-     * Constructor with implicit sticky x-axis.
-     * @param text the text to display.
-     * @param pos  the position at which to draw the text and mark.
-     */
-    public StickyAxisAnnotation(String text, float pos) {
-        this(text, pos, SimpleAxis.X_AXIS);
-    }
-
-
-    /**
-     * Constructor with given explicit axis.
-     * @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
-     *                   relative).
-     */
-    public StickyAxisAnnotation(String text, float pos, SimpleAxis stickAxis
-    ) {
-        this(text, pos, stickAxis, 0);
-    }
-
-
-    /**
-     * 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
-     *                   relative).
-     */
-    public StickyAxisAnnotation(String text, float pos, SimpleAxis stickAxis,
-            int axisSymbol
-    ) {
-        setStickyAxis(stickAxis);
-        this.text   = text;
-        this.pos    = pos;
-        this.axisSymbol = axisSymbol;
-        this.hitPoint = Float.NaN;
-    }
-
-
-    /**
-     * Sets the "sticky axis" (whether to draw annotations at the
-     * X- or the Y-Axis.
-     *
-     * @param stickyAxis axis to stick to.
-     */
-    public void setStickyAxis(SimpleAxis stickyAxis) {
-        this.stickyAxis = stickyAxis;
-    }
-
-
-    public float getPos() {
-        return this.pos;
-    }
-
-    public SimpleAxis getStickyAxis() {
-        return this.stickyAxis;
-    }
-
-    public boolean atX() {
-        return this.getStickyAxis() == SimpleAxis.X_AXIS;
-    }
-
-    /** Get text to be displayed at axis. */
-    public String getText() {
-        return this.text;
-    }
-
-
-    public int getAxisSymbol() {
-        return this.axisSymbol;
-    }
-
-
-    /** Set where to hit a curve (if any). */
-    public void setHitPoint(float pos) {
-        this.hitPoint = pos;
-    }
-
-    /** Get where to hit a curve (if any). */
-    public float getHitPoint() {
-        return this.hitPoint;
-    }
-
-    /** Set sticky axis to the X axis if it is currently Y, and vice versa. */
-    public void flipStickyAxis() {
-        if (this.getStickyAxis() == SimpleAxis.X_AXIS) {
-            this.setStickyAxis(SimpleAxis.Y_AXIS);
-        }
-        else {
-            this.setStickyAxis(SimpleAxis.X_AXIS);
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/jfree/Style.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@
-package de.intevation.flys.jfree;
-
-import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public interface Style {
-
-    XYLineAndShapeRenderer applyTheme(XYLineAndShapeRenderer r, int idx);
-
-    XYLineAndShapeRenderer getRenderer();
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/jfree/StyledAreaSeriesCollection.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,162 +0,0 @@
-package de.intevation.flys.jfree;
-
-import java.awt.BasicStroke;
-import java.awt.Color;
-import java.awt.Stroke;
-
-import org.jfree.data.xy.XYSeriesCollection;
-import org.w3c.dom.Document;
-
-import de.intevation.flys.themes.ThemeAccess;
-import de.intevation.flys.utils.ThemeUtil;
-
-
-/**
- * One or more dataseries to draw a polygon (either "open up/downwards", or
- * the area between two curves), a theme-document and further display options.
- * The theme-document will later "style" the graphical representation.
- * The display options can be used to control the z-order and the axis of the
- * dataset.
- */
-public class StyledAreaSeriesCollection extends XYSeriesCollection {
-    private static final long serialVersionUID = 5274940965666948237L;
-
-    /** Mode, how to draw/which areas to fill. */
-    public enum FILL_MODE {UNDER, ABOVE, BETWEEN};
-
-    /** MODE in use. */
-    protected FILL_MODE mode;
-
-    /** The theme-document with attributes about actual visual representation. */
-    protected Document theme;
-
-
-    /**
-     * @param theme the theme-document.
-     */
-    public StyledAreaSeriesCollection(Document theme) {
-        this.theme = theme;
-        this.mode = FILL_MODE.BETWEEN;
-   }
-
-
-    /** Gets the Fill mode. */
-    public FILL_MODE getMode() {
-        return this.mode;
-    }
-
-
-    /** Sets the Fill mode. */
-    public void setMode(FILL_MODE fMode) {
-        this.mode = fMode;
-    }
-
-
-    /**
-     * Applies line color, size and type attributes to renderer, also
-     * whether to draw lines and/or points.
-     * @param renderer Renderer to apply theme to.
-     * @return \param renderer
-     */
-    public StableXYDifferenceRenderer applyTheme(
-        StableXYDifferenceRenderer renderer
-    ) {
-        applyFillColor(renderer);
-        applyShowShape(renderer);
-        applyOutlineColor(renderer);
-        applyOutlineStyle(renderer);
-        applyShowArea(renderer);
-        if (mode == FILL_MODE.UNDER) {
-            renderer.setAreaCalculationMode(StableXYDifferenceRenderer.CALCULATE_NEGATIVE_AREA);
-        }
-        else if (mode == FILL_MODE.ABOVE) {
-            renderer.setAreaCalculationMode(StableXYDifferenceRenderer.CALCULATE_POSITIVE_AREA);
-        }
-        else {
-            renderer.setAreaCalculationMode(StableXYDifferenceRenderer.CALCULATE_ALL_AREA);
-        }
-
-        // Apply text style.
-        new ThemeAccess(theme).parseTextStyle().apply(renderer);
-        return renderer;
-    }
-
-
-    protected void applyFillColor(StableXYDifferenceRenderer renderer) {
-        Color paint = ThemeUtil.parseColor(
-                ThemeUtil.getAreaBackgroundColorString(theme));
-
-        int transparency = ThemeUtil.parseAreaTransparency(theme);
-        if (transparency > 0 && paint != null) {
-            paint = new Color(
-                        paint.getRed(),
-                        paint.getGreen(),
-                        paint.getBlue(),
-                        (int)((100 - transparency) * 2.55f));
-        }
-
-        if (paint != null && this.getMode() == FILL_MODE.ABOVE) {
-            renderer.setPositivePaint(paint);
-            renderer.setNegativePaint(new Color(0,0,0,0));
-        }
-        else if (paint != null && this.getMode() == FILL_MODE.UNDER) {
-            renderer.setNegativePaint(paint);
-            renderer.setPositivePaint(new Color(0,0,0,0));
-        }
-        else {
-            if (paint == null)
-                paint = new Color(177, 117, 102);
-            renderer.setPositivePaint(paint);
-            renderer.setNegativePaint(paint);
-        }
-    }
-
-
-    protected void applyShowShape(StableXYDifferenceRenderer renderer) {
-        boolean show = ThemeUtil.parseAreaShowBorder(theme);
-        renderer.setDrawOutline(show);
-    }
-
-
-    protected void applyShowLine(StableXYDifferenceRenderer renderer) {
-        boolean show = ThemeUtil.parseShowLine(theme);
-        renderer.setShapesVisible(show);
-    }
-
-
-    protected void applyOutlineColor(StableXYDifferenceRenderer renderer) {
-        Color c = ThemeUtil.parseLineColorField(theme);
-        renderer.setOutlinePaint(c);
-    }
-
-    protected void applyOutlineWidth(StableXYDifferenceRenderer renderer) {
-        int size = ThemeUtil.parseLineWidth(theme);
-    }
-
-    /** Inform renderer whether it should draw a label. */
-    protected void applyShowArea(StableXYDifferenceRenderer renderer) {
-        renderer.setLabelArea(ThemeUtil.parseShowArea(theme));
-    }
-
-    protected void applyOutlineStyle(StableXYDifferenceRenderer renderer) {
-        float[] dashes = ThemeUtil.parseLineStyle(theme);
-        int size       = ThemeUtil.parseLineWidth(theme);
-
-        Stroke stroke = null;
-
-        if (dashes.length <= 1) {
-            stroke = new BasicStroke(Integer.valueOf(size));
-        }
-        else {
-            stroke = new BasicStroke(Integer.valueOf(size),
-                BasicStroke.CAP_BUTT,
-                BasicStroke.JOIN_ROUND,
-                1.0f,
-                dashes,
-                0.0f);
-        }
-
-        renderer.setOutlineStroke(stroke);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/jfree/StyledDomainMarker.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-package de.intevation.flys.jfree;
-
-import java.awt.Color;
-
-import org.jfree.chart.plot.IntervalMarker;
-import org.w3c.dom.Document;
-
-import de.intevation.flys.utils.ThemeUtil;
-
-/**
- * Marker that represents a highlighted interval.
- *
- * @author <a href="mailto:christian.lins@intevation.de">Christian Lins</a>
- */
-public class StyledDomainMarker extends IntervalMarker {
-
-    private static final long serialVersionUID = -4369417661339512342L;
-
-    private final Color backgroundColor, backgroundColor2;
-
-    public StyledDomainMarker(double start, double end, Document theme) {
-        super(start, end);
-
-        backgroundColor = ThemeUtil.parseColor(
-                ThemeUtil.getAreaBackgroundColorString(theme));
-        backgroundColor2 = new Color(
-            255 - backgroundColor.getRed(),
-            255 - backgroundColor.getGreen(),
-            255 - backgroundColor.getBlue());
-        useSecondColor(false);
-
-        int alpha = 100 - ThemeUtil.parseInteger(
-                ThemeUtil.getAreaTransparencyString(theme), 50);
-        setAlpha(alpha / 100.0f);
-    }
-
-    /**
-     * To properly differentiate several styled domain markers side by side,
-     * we can use this switch to toggle between two colors.
-     * @param secondColor
-     */
-    public void useSecondColor(boolean secondColor) {
-        if(!secondColor) {
-            if(backgroundColor != null)
-                setPaint(backgroundColor);
-        }
-        else {
-            if(backgroundColor2 != null)
-                setPaint(backgroundColor2);
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/jfree/StyledSeries.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,13 +0,0 @@
-package de.intevation.flys.jfree;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public interface StyledSeries {
-
-    void setStyle(Style style);
-
-    Style getStyle();
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/jfree/StyledTimeSeries.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-package de.intevation.flys.jfree;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Document;
-
-import org.jfree.data.time.TimeSeries;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class StyledTimeSeries extends TimeSeries implements StyledSeries {
-
-    private static final Logger logger =
-        Logger.getLogger(StyledTimeSeries.class);
-
-
-    protected Style style;
-
-
-    public StyledTimeSeries(String key, Document theme) {
-        super(key);
-        setStyle(new XYStyle(theme));
-    }
-
-
-    @Override
-    public void setStyle(Style style) {
-        this.style = style;
-    }
-
-
-    @Override
-    public Style getStyle() {
-        return style;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/jfree/StyledValueMarker.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,32 +0,0 @@
-package de.intevation.flys.jfree;
-
-import de.intevation.flys.utils.ThemeUtil;
-
-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.
- * @author <a href="mailto:christian.lins@intevation.de">Christian Lins</a>
- */
-public class StyledValueMarker extends ValueMarker {
-
-    private static final long serialVersionUID = -3607777705307785140L;
-
-    public StyledValueMarker(double value, Document theme) {
-        super(value);
-
-        Color color = ThemeUtil.parseAreaBackgroundColor(theme);
-        if(color == null) {
-            color = Color.BLACK;
-        }
-        this.setPaint(color);
-
-        int size = ThemeUtil.parsePointWidth(theme);
-        setStroke(new BasicStroke(size));
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/jfree/StyledXYSeries.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,82 +0,0 @@
-package de.intevation.flys.jfree;
-
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Document;
-
-import org.jfree.data.xy.XYDataItem;
-import org.jfree.data.xy.XYSeries;
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class StyledXYSeries extends XYSeries implements StyledSeries, HasLabel {
-
-    private static final Logger logger = Logger.getLogger(StyledXYSeries.class);
-
-    protected Style style;
-
-    /** If this Series is to be labelled, use this String as label. */
-    protected String label;
-
-
-    public StyledXYSeries(String key, Document theme) {
-        this(key, true, theme);
-        this.label = key.toString();
-    }
-
-
-    public StyledXYSeries(String key, Document theme, XYSeries unstyledSeries) {
-        this(key, theme);
-        add(unstyledSeries);
-    }
-
-
-    /**
-     * @param sorted whether or not to sort the points. Sorting will move NANs
-     *               to one extrema which can cause problems in certain
-     *               algorithms.
-     */
-    public StyledXYSeries(String key, boolean sorted, Document theme) {
-        super(key, sorted);
-        setStyle(new XYStyle(theme));
-        this.label = key.toString();
-    }
-
-
-    @Override
-    public void setStyle(Style style) {
-        this.style = style;
-    }
-
-
-    @Override
-    public Style getStyle() {
-        return style;
-    }
-
-
-    @Override
-    public String getLabel() {
-        return label;
-    }
-
-    @Override
-    public void setLabel(String label) {
-        this.label = label;
-    }
-
-    protected void add(XYSeries series) {
-        List<XYDataItem> items = series.getItems();
-        add(items);
-    }
-
-    protected void add(List<XYDataItem> items) {
-        for(XYDataItem item : items) {
-            add(item.getXValue(), item.getYValue());
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/jfree/TimeBounds.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,88 +0,0 @@
-package de.intevation.flys.jfree;
-
-import java.util.Date;
-
-import org.jfree.chart.axis.DateAxis;
-import org.jfree.chart.axis.ValueAxis;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class TimeBounds implements Bounds {
-
-    protected long lower;
-    protected long upper;
-
-
-    public TimeBounds(long lower, long upper) {
-        this.lower = lower;
-        this.upper = upper;
-    }
-
-
-    @Override
-    public Number getLower() {
-        return Long.valueOf(lower);
-    }
-
-
-    public Date getLowerAsDate() {
-        return new Date(lower);
-    }
-
-
-    @Override
-    public Number getUpper() {
-        return Long.valueOf(upper);
-    }
-
-
-    public Date getUpperAsDate() {
-        return new Date(upper);
-    }
-
-
-    @Override
-    public void applyBounds(ValueAxis axis) {
-        DateAxis dateAxis = (DateAxis) axis;
-
-        dateAxis.setMinimumDate(new Date(lower));
-        dateAxis.setMaximumDate(new Date(upper));
-    }
-
-
-    @Override
-    public void applyBounds(ValueAxis axis, int percent) {
-        DateAxis dateAxis = (DateAxis) axis;
-
-        long space = (upper - lower) / 100 * percent;
-
-        dateAxis.setMinimumDate(new Date(lower-space));
-        dateAxis.setMaximumDate(new Date(upper+space));
-    }
-
-
-    @Override
-    public Bounds combine(Bounds bounds) {
-        if (bounds == null) {
-            return this;
-        }
-
-        TimeBounds other = (TimeBounds) bounds;
-
-        long otherLower = other.getLower().longValue();
-        long otherUpper = other.getUpper().longValue();
-
-        return new TimeBounds(
-            otherLower < lower ? otherLower : lower,
-            otherUpper > upper ? otherUpper : upper);
-    }
-
-
-    @Override
-    public String toString() {
-        return "TimeBounds=["+ getLowerAsDate() + " ; " + getUpperAsDate() +"]";
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/jfree/XYStyle.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,229 +0,0 @@
-package de.intevation.flys.jfree;
-
-import de.intevation.flys.utils.ThemeUtil;
-
-import java.awt.BasicStroke;
-import java.awt.Color;
-import java.awt.geom.Ellipse2D;
-
-import org.apache.log4j.Logger;
-import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
-import org.w3c.dom.Document;
-
-
-/**
- * Utility to apply theme-settings to a renderer.
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class XYStyle implements Style {
-
-    private static Logger logger = Logger.getLogger(XYStyle.class);
-
-    protected Document theme;
-
-    protected XYLineAndShapeRenderer renderer;
-
-
-    public XYStyle(Document theme) {
-        this.theme = theme;
-        this.renderer = null;
-    }
-
-
-    /**
-     * Applies line color, size and type attributes to renderer, also
-     * whether to draw lines and/or points.
-     */
-    @Override
-    public XYLineAndShapeRenderer applyTheme(XYLineAndShapeRenderer r, int idx){
-        this.renderer = r;
-        applyLineColor(r, idx);
-        applyLineSize(r, idx);
-        applyLineType(r, idx);
-        applyShowLine(r, idx);
-        applyShowPoints(r, idx);
-        applyPointSize(r, idx);
-        applyPointColor(r, idx);
-        applyShowMinimum(r, idx);
-        applyShowMaximum(r, idx);
-
-        // Line label styles
-        applyShowLineLabel(r, idx);
-        applyShowLineLabelBG(r, idx);
-        applyLineLabelFont(r, idx);
-        applyLineLabelColor(r, idx);
-        applyLineLabelBGColor(r, idx);
-
-        // Point label styles
-        // TODO: Currently point label are annotations and are not drawn this way
-        /*
-        applyShowPointLabelBG(r, idx);
-        applyLinePointFont(r, idx);
-        applyLinePointColor(r, idx);
-        applyLinePointBGColor(r, idx);*/
-
-        return r;
-    }
-
-
-    /** Set line color to renderer. */
-    protected void applyLineColor(XYLineAndShapeRenderer r, int idx) {
-        Color c = ThemeUtil.parseLineColorField(theme);
-        if(c != null) {
-            logger.debug("applyLineColor " + c.toString());
-            r.setSeriesPaint(idx, c);
-        }
-        else {
-            logger.warn("applyLineColor: color is null - malformed linecolor field?");
-        }
-    }
-
-
-    /** Tells the renderer whether or not to add a label to a line. */
-    protected void applyShowLineLabel(XYLineAndShapeRenderer r, int idx) {
-        if (!(r instanceof EnhancedLineAndShapeRenderer)) {
-            return;
-        }
-        boolean showLabelLine = ThemeUtil.parseShowLineLabel(theme);
-        boolean anyLabel = showLabelLine || ThemeUtil.parseShowWidth(theme) ||
-                           ThemeUtil.parseShowLevel(theme) ||
-                           ThemeUtil.parseShowMiddleHeight(theme);
-        ((EnhancedLineAndShapeRenderer)r).setShowLineLabel(anyLabel, idx);
-    }
-
-
-    /** Tells the renderer whether or not to fill the bg of a lines label. */
-    protected void applyShowLineLabelBG(XYLineAndShapeRenderer r, int idx) {
-        if (!(r instanceof EnhancedLineAndShapeRenderer)) {
-            return;
-        }
-        boolean showLabelLine = ThemeUtil.parseLabelShowBackground(theme);
-        ((EnhancedLineAndShapeRenderer)r).setShowLineLabelBG(idx, showLabelLine);
-    }
-
-    /** Tell the renderer which font (and -size and -style) to use for
-     * linelabels. */
-    protected void applyLineLabelFont(XYLineAndShapeRenderer r, int idx) {
-        if (!(r instanceof EnhancedLineAndShapeRenderer)) {
-            return;
-        }
-        ((EnhancedLineAndShapeRenderer)r).setLineLabelFont(
-                ThemeUtil.parseTextFont(theme), idx);
-    }
-
-    /** Tell the renderer which color to use for
-     * linelabels. */
-    protected void applyLineLabelColor(XYLineAndShapeRenderer r, int idx) {
-        if (!(r instanceof EnhancedLineAndShapeRenderer)) {
-            return;
-        }
-        ((EnhancedLineAndShapeRenderer)r).setLineLabelTextColor(
-                idx, ThemeUtil.parseTextColor(theme));
-    }
-
-    /** Tell the renderer which color to use for bg of
-     * linelabels. */
-    protected void applyLineLabelBGColor(XYLineAndShapeRenderer r, int idx) {
-        if (!(r instanceof EnhancedLineAndShapeRenderer)) {
-            return;
-        }
-        ((EnhancedLineAndShapeRenderer)r).setLineLabelBGColor(idx,
-            ThemeUtil.parseTextBackground(theme));
-    }
-
-    /** Set stroke of series. */
-    protected void applyLineSize(XYLineAndShapeRenderer r, int idx) {
-        int size = ThemeUtil.parseLineWidth(theme);
-        r.setSeriesStroke(
-            idx,
-            new BasicStroke(size));
-    }
-
-
-    /** Set stroke strength of series. */
-    protected void applyLineType(XYLineAndShapeRenderer r, int idx) {
-        int size = ThemeUtil.parseLineWidth(theme);
-        float[] dashes = ThemeUtil.parseLineStyle(theme);
-
-        // Do not apply the dashed style.
-        if (dashes.length <= 1) {
-            return;
-        }
-
-        r.setSeriesStroke(
-            idx,
-            new BasicStroke(size,
-                            BasicStroke.CAP_BUTT,
-                            BasicStroke.JOIN_ROUND,
-                            1.0f,
-                            dashes,
-                            0.0f));
-    }
-
-
-    protected void applyPointSize(XYLineAndShapeRenderer r, int idx) {
-        int size = ThemeUtil.parsePointWidth(theme);
-        int dim  = 2 * size;
-
-        r.setSeriesShape(idx, new Ellipse2D.Double(-size, -size, dim, dim));
-    }
-
-
-    protected void applyPointColor(XYLineAndShapeRenderer r, int idx) {
-        Color c = ThemeUtil.parsePointColor(theme);
-
-        if (c != null) {
-            r.setSeriesFillPaint(idx, c);
-            r.setUseFillPaint(true);
-            r.setDrawOutlines(false);
-        }
-    }
-
-
-    /**
-     * Sets form and visibility of points.
-     */
-    protected void applyShowPoints(XYLineAndShapeRenderer r, int idx) {
-        boolean show = ThemeUtil.parseShowPoints(theme);
-
-        r.setSeriesShapesVisible(idx, show);
-        r.setDrawOutlines(true);
-    }
-
-
-    protected void applyShowLine(XYLineAndShapeRenderer r, int idx) {
-        boolean show = ThemeUtil.parseShowLine(theme);
-        r.setSeriesLinesVisible(idx, show);
-    }
-
-
-    protected void applyShowMinimum(XYLineAndShapeRenderer r, int idx) {
-        if (!(r instanceof EnhancedLineAndShapeRenderer)) {
-            return;
-        }
-
-        boolean visible = ThemeUtil.parseShowMinimum(theme);
-
-        EnhancedLineAndShapeRenderer er = (EnhancedLineAndShapeRenderer) r;
-        er.setIsMinimumShapeVisisble(idx, visible);
-    }
-
-
-    protected void applyShowMaximum(XYLineAndShapeRenderer r, int idx) {
-        if (!(r instanceof EnhancedLineAndShapeRenderer)) {
-            return;
-        }
-
-        boolean visible = ThemeUtil.parseShowMaximum(theme);
-
-        EnhancedLineAndShapeRenderer er = (EnhancedLineAndShapeRenderer) r;
-        er.setIsMaximumShapeVisible(idx, visible);
-    }
-
-
-    @Override
-    public XYLineAndShapeRenderer getRenderer() {
-        return this.renderer;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/themes/DefaultTheme.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,184 +0,0 @@
-package de.intevation.flys.themes;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-
-import de.intevation.artifacts.common.utils.XMLUtils;
-import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class DefaultTheme implements Theme {
-
-    /** The name of the theme.*/
-    protected String name;
-
-    /** The description of the theme.*/
-    protected String description;
-
-    protected String facet;
-
-    protected int index;
-
-
-    /** The map storing the fields of this theme.*/
-    protected Map<String, ThemeField> fields;
-
-    /** The map storing the attributes of this theme.*/
-    protected Map<String, String> attr;
-
-
-    /**
-     * Initializes the components of this Theme.
-     */
-    public DefaultTheme(String name, String description) {
-        this.name        = name;
-        this.description = description;
-        this.fields      = new HashMap<String, ThemeField>();
-        this.attr        = new HashMap<String, String>();
-    }
-
-
-    public void init(Node config) {
-    }
-
-
-    public String getName() {
-        return name;
-    }
-
-
-    public String getDescription() {
-        return description;
-    }
-
-
-    public String getFacet() {
-        return facet;
-    }
-
-
-    public void setFacet(String facet) {
-        this.facet = facet;
-    }
-
-
-    public int getIndex() {
-        return index;
-    }
-
-
-    public void setIndex(int index) {
-        this.index = index;
-    }
-
-
-    public void addAttribute(String name, String value) {
-        if (name != null && value != null) {
-            attr.put(name, value);
-        }
-    }
-
-
-    public String getAttribute(String name) {
-        return attr.get(name);
-    }
-
-
-    public void addField(String name, ThemeField field) {
-        if (name != null && field != null) {
-            fields.put(name, field);
-        }
-    }
-
-
-    public void setFieldValue(String name, Object value) {
-        if (name != null && value != null) {
-            ThemeField field = fields.get(name);
-
-            if (field != null) {
-                field.setValue(value);
-            }
-        }
-    }
-
-
-    public ThemeField getField(String name) {
-        return fields.get(name);
-    }
-
-
-    public String getFieldType(String name) {
-        ThemeField field = fields.get(name);
-
-        return field != null ? field.getType() : null;
-    }
-
-
-    public Object getFieldValue(String name) {
-        ThemeField field = fields.get(name);
-
-        return field != null ? field.getValue() : null;
-    }
-
-
-    public Document toXML() {
-        Document doc = XMLUtils.newDocument();
-
-        ElementCreator cr = new ElementCreator(doc, null, null);
-
-        Element theme = cr.create("theme");
-        theme.setAttribute("facet", facet);
-        theme.setAttribute("index", String.valueOf(index));
-
-        appendAttributes(cr, theme);
-        appendFields(cr, theme);
-
-        doc.appendChild(theme);
-
-        return doc;
-    }
-
-
-    /**
-     * Appends the attributes configured for this theme.
-     *
-     * @param cr The ElementCreator.
-     * @param theme The document root element.
-     */
-    protected void appendAttributes(ElementCreator cr, Element theme) {
-
-        for (Map.Entry<String, String> entry: attr.entrySet()) {
-            String key = entry.getKey();
-            String val = entry.getValue();
-
-            if (key != null && val != null) {
-                cr.addAttr(theme, key, val);
-            }
-        }
-    }
-
-
-    /**
-     * Appends the fields configured for this theme.
-     *
-     * @param cr The ElementCreator.
-     * @param theme The document root element.
-     */
-    protected void appendFields(ElementCreator cr, Element theme) {
-
-        for (ThemeField field: fields.values()) {
-            Document doc = field.toXML();
-            Node    root = doc.getFirstChild();
-
-            theme.appendChild(theme.getOwnerDocument().importNode(root, true));
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/themes/DefaultThemeField.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,79 +0,0 @@
-package de.intevation.flys.themes;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-import de.intevation.artifacts.common.utils.XMLUtils;
-import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class DefaultThemeField implements ThemeField {
-
-    protected String name;
-
-    protected Map<String, Object> attr;
-
-
-    public DefaultThemeField(String name) {
-        this.name = name;
-        this.attr = new HashMap<String, Object>();
-    }
-
-
-    public String getName() {
-        return name;
-    }
-
-
-    public String getType() {
-        return (String) getAttribute("type");
-    }
-
-
-    public Object getValue() {
-        return getAttribute("value");
-    }
-
-
-    public void setValue(Object value) {
-        setAttribute("value", value);
-    }
-
-
-    public Object getAttribute(String name) {
-        return attr.get(name);
-    }
-
-
-    public void setAttribute(String name, Object value) {
-        if (name == null || value == null) {
-            return;
-        }
-
-        attr.put(name, value);
-    }
-
-
-    public Document toXML() {
-        Document doc = XMLUtils.newDocument();
-
-        ElementCreator cr = new ElementCreator(doc, null, null);
-
-        Element field = cr.create("field");
-
-        for (Map.Entry<String, Object> entry: attr.entrySet()) {
-            cr.addAttr(field, entry.getKey(), (String)entry.getValue());
-        }
-
-        doc.appendChild(field);
-
-        return doc;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/themes/LineStyle.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,21 +0,0 @@
-package de.intevation.flys.themes;
-
-import java.awt.Color;
-
-public class LineStyle {
-    protected Color lineColor;
-    protected int   lineWidth;
-
-    public LineStyle(Color color, int width) {
-        this.lineColor = color;
-        this.lineWidth = width;
-    }
-
-    public int getWidth() {
-        return lineWidth;
-    }
-
-    public Color getColor() {
-        return lineColor;
-    }
-}
\ No newline at end of file
--- a/flys-artifacts/src/main/java/de/intevation/flys/themes/PointStyle.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,5 +0,0 @@
-package de.intevation.flys.themes;
-
-public class PointStyle {
-    // TODO tbd
-}
\ No newline at end of file
--- a/flys-artifacts/src/main/java/de/intevation/flys/themes/TextStyle.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,52 +0,0 @@
-package de.intevation.flys.themes;
-
-import de.intevation.flys.jfree.StableXYDifferenceRenderer;
-
-import java.awt.Color;
-import java.awt.Font;
-
-import org.apache.log4j.Logger;
-import org.jfree.chart.annotations.XYTextAnnotation;
-
-public class TextStyle {
-    @SuppressWarnings("unused")
-    private static Logger log = Logger.getLogger(TextStyle.class);
-
-    protected Color   textColor;
-    protected Font    font;
-    protected Color   bgColor;
-    protected boolean showBg;
-    protected boolean isVertical;
-
-    public TextStyle(Color fgColor, Font font, Color bgColor,
-        boolean showBg, boolean isVertical
-    ) {
-        this.textColor  = fgColor;
-        this.font       = font;
-        this.bgColor    = bgColor;
-        this.showBg     = showBg;
-        this.isVertical = isVertical;
-    }
-
-    public void apply(XYTextAnnotation ta) {
-        ta.setPaint(textColor);
-        ta.setFont(font);
-        if (this.showBg) {
-            ta.setBackgroundPaint(bgColor);
-        }
-        if (this.isVertical) {
-            ta.setRotationAngle(270f*Math.PI/180f);
-        }
-        else {
-            ta.setRotationAngle(0);
-        }
-    }
-
-    public void apply(StableXYDifferenceRenderer renderer) {
-        renderer.setLabelColor(textColor);
-        renderer.setLabelFont(font);
-        if (this.showBg) {
-            renderer.setLabelBGColor(bgColor);
-        }
-    }
-}
\ No newline at end of file
--- a/flys-artifacts/src/main/java/de/intevation/flys/themes/Theme.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,119 +0,0 @@
-package de.intevation.flys.themes;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Node;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public interface Theme {
-
-    /**
-     * Method to initialize the theme.
-     *
-     * @param config The configuration node.
-     */
-    void init(Node config);
-
-
-    /**
-     * Returns the name of the theme.
-     *
-     * @return the name of the theme.
-     */
-    String getName();
-
-
-    /**
-     * Returns the description of the theme.
-     *
-     * @return the description of the theme.
-     */
-    String getDescription();
-
-
-    String getFacet();
-
-    void setFacet(String facet);
-
-    int getIndex();
-
-    void setIndex(int index);
-
-
-    /**
-     * Adds a new attribute.
-     *
-     * @param name The name of the attribute.
-     * @param value The value of the attribute.
-     */
-    void addAttribute(String name, String value);
-
-
-    /**
-     * Returns the value of a specific attribute.
-     *
-     * @param name the name of the attribute.
-     *
-     * @return the value of the attribute <i>name</i>.
-     */
-    String getAttribute(String name);
-
-
-    /**
-     * Adds a new field to the theme.
-     *
-     * @param name The name of the field.
-     * @param field The field.
-     */
-    void addField(String name, ThemeField field);
-
-
-    /**
-     * Sets the value of an field.
-     *
-     * @param name The name of the field.
-     * @param value The new value of the field.
-     */
-    void setFieldValue(String name, Object value);
-
-
-    /**
-     * Returns the field specified by name.
-     *
-     * @param name The name of the desired field.
-     *
-     * @return an field.
-     */
-    ThemeField getField(String name);
-
-
-    /**
-     * Returns the typename of a field.
-     *
-     * @param name the name of the field.
-     *
-     * @return the typename of a field.
-     */
-    String getFieldType(String name);
-
-
-    /**
-     * Returns the value of a field.
-     *
-     * @param name The name of the field.
-     *
-     * @return the value of a field.
-     */
-    Object getFieldValue(String name);
-
-
-    /**
-     * Dumps the theme to XML.
-     *
-     * @return a document.
-     */
-    Document toXML();
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/themes/ThemeAccess.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,124 +0,0 @@
-package de.intevation.flys.themes;
-
-import de.intevation.flys.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 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/themes/ThemeFactory.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,312 +0,0 @@
-package de.intevation.flys.themes;
-
-import de.intevation.artifacts.common.utils.XMLUtils;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.context.FLYSContext;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.xml.xpath.XPathConstants;
-
-import org.apache.log4j.Logger;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.NamedNodeMap;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- *
- * Mapping-matching rules:
- *
- */
-public class ThemeFactory {
-
-    private static Logger logger = Logger.getLogger(ThemeFactory.class);
-
-    /** Trivial, hidden constructor. */
-    private ThemeFactory() {
-    }
-
-
-    /**
-     * Creates a new theme from <i>config</i>.
-     *
-     * @param themeCfg The theme config document that is used to fetch parent
-     * themes.
-     * @param config The theme config node.
-     *
-     * @return a new theme.
-     */
-    public static Theme createTheme(Document themeCfg, Node config) {
-        String name = getName(config);
-        String desc = getDescription(config);
-
-        logger.debug("Create new theme: " + name);
-
-        Theme theme = new DefaultTheme(name, desc);
-
-        parseInherits(themeCfg, config, theme);
-        parseFields(config, theme);
-        parseAttrs(config, theme);
-
-        return theme;
-    }
-
-
-    /**
-     * Get first matching theme for facet.
-     *
-     * @param name    Name to match "from" of theme mapping.
-     * @param pattern String to 'compare' to pattern in mapping.
-     * @param output  Name of the current output
-     *
-     * @return First matching theme.
-     */
-    public static Theme getTheme(
-        FLYSContext c,
-        String name,
-        String pattern,
-        String output,
-        String groupName)
-    {
-        if (logger.isDebugEnabled()) {
-            logger.debug(
-                "Search theme for: " + name + " - pattern: " + pattern);
-        }
-
-        if (c == null || name == null) {
-            logger.warn("Cannot search for theme.");
-            return null;
-        }
-
-        // Fetch mapping and themes.
-        @SuppressWarnings("unchecked")
-        Map<String, List<ThemeMapping>> map = (Map<String, List<ThemeMapping>>)
-            c.get(FLYSContext.THEME_MAPPING);
-
-        @SuppressWarnings("unchecked")
-        List<ThemeGroup> tgs = (List<ThemeGroup>)
-            c.get(FLYSContext.THEMES);
-
-        ThemeGroup group = null;
-        for (ThemeGroup tg: tgs) {
-            if (tg.getName().equals(groupName)) {
-                group = tg;
-                break;
-            }
-        }
-
-        if (group == null) {
-            logger.warn("No theme group found: '" + groupName + "'");
-            return null;
-        }
-
-        Map<String, Theme> t = group.getThemes();
-
-        FLYSArtifact artifact = (FLYSArtifact) c.get(FLYSContext.ARTIFACT_KEY);
-
-        if (map == null || map.isEmpty() || t == null || t.isEmpty()) {
-            logger.warn("No mappings or themes found. Cannot retrieve theme!");
-            return null;
-        }
-
-        List<ThemeMapping> mapping = map.get(name);
-
-        if (mapping == null) {
-            logger.warn("No theme found for mapping: " + name);
-            return null;
-        }
-
-        // Take first mapping of which all conditions are satisfied.
-        for (ThemeMapping tm: mapping) {
-            if (name.equals(tm.getFrom())
-                && tm.applyPattern(pattern)
-                && tm.masterAttrMatches(artifact)
-                && tm.outputMatches(output))
-            {
-                String target = tm.getTo();
-
-                logger.debug("Found theme '" + target + "'");
-                return t.get(target);
-            }
-        }
-
-        String msg =
-            "No theme found for '" + name +
-            "' with pattern '" + pattern + "' and output " + output + ".";
-
-        logger.warn(msg);
-
-        return null;
-    }
-
-
-    @SuppressWarnings("unchecked")
-    public static List<ThemeGroup> getThemeGroups(FLYSContext c) {
-        List<ThemeGroup> tgs = (List<ThemeGroup>)
-            c.get(FLYSContext.THEMES);
-        return tgs;
-    }
-
-
-    @SuppressWarnings("unchecked")
-    public static List<Theme> getThemes (FLYSContext c, String name) {
-        List<ThemeGroup> tgs = (List<ThemeGroup>)
-            c.get(FLYSContext.THEMES);
-        if (tgs == null) {
-            return null;
-        }
-
-        List<Theme> themes = new ArrayList<Theme>();
-        for (ThemeGroup tg: tgs) {
-            themes.add(tg.getThemeByName(name));
-        }
-        return themes;
-    }
-
-    protected static String getName(Node config) {
-        return ((Element)config).getAttribute("name");
-    }
-
-
-    protected static String getDescription(Node config) {
-        return ((Element)config).getAttribute("desc");
-    }
-
-
-    protected static void parseInherits(Document themeCfg, Node cfg, Theme t) {
-        parseInherits(themeCfg, cfg, t, null);
-    }
-
-    protected static void parseInherits(
-        Document themeCfg,
-        Node     cfg,
-        Theme    t,
-        Map<String, Node> themes
-    ) {
-        logger.debug("ThemeFactory.parseInherits");
-
-        NodeList inherits = ((Element)cfg).getElementsByTagName("inherit");
-
-        int num = inherits.getLength();
-
-        if (num == 0) {
-            logger.debug("Theme does not inherit from other themes.");
-            return;
-        }
-
-        logger.debug("Theme inherits from " + num + " other themes.");
-
-        if (themes == null) {
-            themes = buildThemeMap(themeCfg);
-        }
-
-        for (int i = 0; i < num; i++) {
-            Node inherit = inherits.item(i);
-            String from = ((Element)inherit).getAttribute("from");
-
-            Node tmp = themes.get(from);
-
-            parseInherits(themeCfg, tmp, t, themes);
-            parseFields(tmp, t);
-        }
-    }
-
-    protected static Map<String, Node> buildThemeMap(Document themeCfg) {
-        Map<String, Node> map = new HashMap<String, Node>();
-        String xpath = "/themes/themegroup/theme";
-
-        NodeList nodes = (NodeList)XMLUtils.xpath(
-            themeCfg, xpath, XPathConstants.NODESET);
-
-        if (nodes != null) {
-            for (int i = 0, N = nodes.getLength(); i < N; ++i) {
-                Node node = nodes.item(i);
-                String name = ((Element)node).getAttribute("name");
-                map.put(name, node);
-            }
-        }
-        return map;
-    }
-
-
-    protected static void parseFields(Node config, Theme theme) {
-        if (config == null || theme == null) {
-            logger.warn("Parsing fields without node or theme is senseless!");
-            return;
-        }
-
-        NodeList fields = ((Element)config).getElementsByTagName("field");
-
-        int num = fields.getLength();
-
-        logger.debug("Found " + num + " own fields in this theme.");
-
-        if (num == 0) {
-            logger.debug("Theme has no own fields.");
-            return;
-        }
-
-        for (int i = 0; i < num; i++) {
-            Node field = fields.item(i);
-
-            addField(theme, field);
-        }
-    }
-
-
-    protected static void addField(Theme theme, Node field) {
-        String name = ((Element)field).getAttribute("name");
-
-        logger.debug("Add field " + name + " to theme " + theme.getName());
-
-        NamedNodeMap attrs = field.getAttributes();
-
-        int num = attrs != null ? attrs.getLength() : 0;
-
-        if (num == 0) {
-            logger.warn("This field has no attributes.");
-            return;
-        }
-
-        ThemeField theField = new DefaultThemeField(name);
-
-        for (int i = 0; i < num; i++) {
-            Node attr    = attrs.item(i);
-
-            String key   = attr.getNodeName();
-            String value = attr.getNodeValue();
-
-            theField.setAttribute(key, value);
-        }
-
-        theme.addField(name, theField);
-    }
-
-
-    protected static void parseAttrs(Node config, Theme theme) {
-        NamedNodeMap attrs = config.getAttributes();
-
-        int num = attrs != null ? attrs.getLength() : 0;
-
-        if (num == 0) {
-            logger.debug("Theme has no attributes set.");
-            return;
-        }
-
-        for (int i = 0; i < num; i++) {
-            Node attr = attrs.item(i);
-
-            String name  = attr.getNodeName();
-            String value = attr.getNodeValue();
-
-            theme.addAttribute(name, value);
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/themes/ThemeField.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-package de.intevation.flys.themes;
-
-import org.w3c.dom.Document;
-
-
-/**
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public interface ThemeField {
-
-    /**
-     * Returns the name of this field.
-     *
-     * @return the name of this field.
-     */
-    String getName();
-
-    /**
-     * Returns the type of this field.
-     *
-     * @return the type of this field.
-     */
-    String getType();
-
-
-    /**
-     * Returns the value of this field.
-     *
-     * @return the value of this field.
-     */
-    Object getValue();
-
-
-    /**
-     * Changes the value of this field.
-     *
-     * @param value The new value.
-     */
-    void setValue(Object value);
-
-
-    /**
-     * Sets the value of an attribute.
-     *
-     * @param name The name of an attribute.
-     * @param value The value of an attribute.
-     */
-    void setAttribute(String name, Object value);
-
-
-    /**
-     * Dumps the field to XML.
-     *
-     * @return a document.
-     */
-    Document toXML();
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/themes/ThemeGroup.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,32 +0,0 @@
-package de.intevation.flys.themes;
-
-import java.util.Map;
-
-/**
- * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
- */
-public class ThemeGroup {
-
-    protected String name;
-
-    protected Map<String, Theme> themes;
-
-
-    public ThemeGroup(String name, Map<String, Theme> themes) {
-        this.name = name;
-        this.themes = themes;
-    }
-
-    public String getName() {
-        return this.name;
-    }
-
-    public Map<String, Theme> getThemes() {
-        return this.themes;
-    }
-
-    public Theme getThemeByName(String name) {
-        return themes.get(name);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/themes/ThemeMapping.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,158 +0,0 @@
-package de.intevation.flys.themes;
-
-import org.apache.log4j.Logger;
-
-import java.io.Serializable;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-/**
- * Represents mapping to a theme (including conditions).
- */
-public class ThemeMapping implements Serializable {
-
-    /** The logger that is used in this class */
-    private static Logger logger = Logger.getLogger(ThemeMapping.class);
-
-    /** Name from which to map. */
-    protected String from;
-
-    /** Name to which to map. */
-    protected String to;
-
-    /** Given pattern (held against facet description). */
-    protected String patternStr;
-
-    /** Given masterAttr pattern (held against masterartifacts attributes). */
-    protected String masterAttr;
-
-    /** Given output for which mapping is valid. */
-    protected String output;
-
-    protected Pattern pattern;
-
-
-    public ThemeMapping(String from, String to) {
-        this(from, to, null, null, null);
-    }
-
-
-    public ThemeMapping(
-        String from,
-        String to,
-        String patternStr,
-        String masterAttr,
-        String output)
-   {
-        this.from       = from;
-        this.to         = to;
-        this.patternStr = patternStr;
-        this.masterAttr = masterAttr;
-        this.output     = output;
-
-        this.pattern = Pattern.compile(patternStr);
-    }
-
-
-    public String getFrom() {
-        return from;
-    }
-
-
-    /**
-     * Get name of theme that is mapped to.
-     */
-    public String getTo() {
-        return to;
-    }
-
-
-    /**
-     * Get pattern.
-     */
-    public String getPatternStr() {
-        return patternStr;
-    }
-
-
-    /**
-     * Match regular expression against text.
-     *
-     * @param text string to be matched against.
-     * @return true if pattern matches text or pattern is empty.
-     */
-    public boolean applyPattern(String text) {
-        if (patternStr == null || patternStr.length() == 0) {
-            return true;
-        }
-        Matcher m = pattern.matcher(text);
-
-       if (m.matches()) {
-           logger.debug("Pattern matches: " + text);
-           return true;
-       }
-       else {
-           logger.debug(
-               "Pattern '"+ text + "' does not match: " + this.patternStr);
-           return false;
-       }
-    }
-
-
-    /**
-     * Inspects Artifacts data given the masterAttr-condition.
-     *
-     * The only condition implemented so far is 'key==value', for which
-     * the Artifacts data with name "key" has to be of value "value" in order
-     * for true to be returned.
-     *
-     * @param artifact Artifact of which to inspect data.
-     * @return true if no condition is specified or condition is met.
-     */
-    public boolean masterAttrMatches(FLYSArtifact artifact) {
-        if (masterAttr == null || masterAttr.length() == 0) {
-           return true;
-        }
-
-        // Operator split.
-        String[] parts = masterAttr.split("==");
-        if (parts.length != 2) {
-            logger.error("ThemeMapping could not parse masterAttr.-condition:_"
-                + masterAttr + "_");
-            return false;
-        }
-
-        // Test.
-        if (artifact.getDataAsString(parts[0]).equals(parts[1])) {
-            logger.debug("Matches master Attribute.");
-            return true;
-        }
-        else {
-            logger.debug("Does not match master Attribute.");
-            return false;
-        }
-    }
-
-
-    /**
-     * Returns true if no output condition exists, or the condition is met
-     * in parameter output.
-     */
-    public boolean outputMatches(String output) {
-        if (this.output == null || this.output.length() == 0) {
-            return true;
-        }
-
-        if (this.output.equals(output)) {
-            logger.debug("Output matches this mapping: " + output);
-            return true;
-        }
-        else {
-            logger.debug("Output '"+ output +"' does not match: "+ this.output);
-            return false;
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/utils/ArtifactMapfileGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,340 +0,0 @@
-package de.intevation.flys.utils;
-
-import de.intevation.artifacts.CallContext;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.access.RiverAccess;
-import de.intevation.flys.artifacts.model.LayerInfo;
-import de.intevation.flys.artifacts.model.map.WMSDBLayerFacet;
-import de.intevation.flys.artifacts.model.map.WMSLayerFacet;
-import de.intevation.flys.artifacts.model.map.WSPLGENLayerFacet;
-import de.intevation.flys.artifacts.resources.Resources;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-import org.apache.velocity.Template;
-import org.geotools.data.shapefile.ShpFiles;
-import org.geotools.data.shapefile.shp.ShapefileHeader;
-import org.geotools.data.shapefile.shp.ShapefileReader;
-
-public class ArtifactMapfileGenerator extends MapfileGenerator {
-
-    private static Logger logger = Logger.getLogger(ArtifactMapfileGenerator.class);
-
-    @Override
-    protected String getVelocityLogfile() {
-        return FLYSUtils.getXPathString(FLYSUtils.XPATH_FLOODMAP_VELOCITY_LOGFILE);
-    }
-
-    @Override
-    protected String getMapserverTemplatePath() {
-        return FLYSUtils.getXPathString(FLYSUtils.XPATH_FLOODMAP_MAPSERVER_TEMPLATE_PATH);
-    }
-
-    @Override
-    public String getMapserverUrl() {
-        return FLYSUtils.getXPathString(FLYSUtils.XPATH_FLOODMAP_MAPSERVER_URL);
-    }
-
-    /**
-     * Method which starts searching for meta information file and mapfile
-     * generation.
-     */
-    @Override
-    public void generate() throws IOException
-    {
-        File[] userDirs = getUserDirs();
-        List<String> layers = parseLayers(userDirs);
-        logger.info("Found " + layers.size() + " layers for user mapfile.");
-
-        writeMapfile(layers);
-    }
-
-    /**
-     * Creates a layer file used for Mapserver's mapfile which represents the
-     * floodmap.
-     *
-     * @param flys The FLYSArtifact that owns <i>wms</i>.
-     * @param wms The WMSLayerFacet that contains information for the layer.
-     */
-    public void createUeskLayer(
-        FLYSArtifact  flys,
-        WSPLGENLayerFacet wms,
-        String        style,
-        CallContext context
-    ) throws FileNotFoundException, IOException
-    {
-        logger.debug("createUeskLayer");
-
-        LayerInfo layerinfo = new LayerInfo();
-        layerinfo.setName(MS_WSPLGEN_PREFIX + flys.identifier());
-        layerinfo.setType("POLYGON");
-        layerinfo.setDirectory(flys.identifier());
-        layerinfo.setData(WSPLGEN_RESULT_SHAPE);
-        layerinfo.setTitle(Resources.getMsg(Resources.getLocale(context.getMeta()),
-                                            "floodmap.uesk",
-                                            "Floodmap"));
-        layerinfo.setStyle(style);
-        RiverAccess access = new RiverAccess(flys);
-        String river = access.getRiver();
-        layerinfo.setSrid(FLYSUtils.getRiverDGMSrid(river));
-
-        String name = MS_LAYER_PREFIX + wms.getName();
-
-        Template template = getTemplateByName(WSPLGEN_LAYER_TEMPLATE);
-        if (template == null) {
-            logger.warn("Template '" + WSPLGEN_LAYER_TEMPLATE + "' found.");
-            return;
-        }
-
-        try {
-            File dir = new File(getShapefileBaseDir(), flys.identifier());
-            writeLayer(layerinfo, new File(dir, name), template);
-        }
-        catch (FileNotFoundException fnfe) {
-            logger.error(fnfe, fnfe);
-            logger.warn("Unable to write layer: " + name);
-        }
-    }
-
-
-    /**
-     * Creates a layer file used for Mapserver's mapfile which represents the
-     * user defined barriers.
-     *
-     * @param flys The FLYSArtifact that owns <i>wms</i>.
-     * @param wms The WMSLayerFacet that contains information for the layer.
-     */
-    public void createBarriersLayer(FLYSArtifact flys, WMSLayerFacet wms)
-    throws FileNotFoundException, IOException
-    {
-        logger.debug("createBarriersLayer");
-
-        //String uuid = flys.identifier();
-        //File   dir  = new File(getShapefileBaseDir(), uuid);
-
-        createBarriersLineLayer(flys, wms);
-        createBarriersPolygonLayer(flys, wms);
-    }
-
-
-    protected void createBarriersLineLayer(
-        FLYSArtifact  flys,
-        WMSLayerFacet wms
-    )
-    throws FileNotFoundException, IOException
-    {
-        String uuid       = flys.identifier();
-        String group      = MS_BARRIERS_PREFIX + uuid;
-        String groupTitle = "I18N_BARRIERS_TITLE";
-
-        File dir  = new File(getShapefileBaseDir(), uuid);
-        File test = new File(dir, WSPLGEN_LINES_SHAPE);
-
-        if (!test.exists() || !test.canRead()) {
-            logger.debug("No barrier line layer existing.");
-            return;
-        }
-
-        LayerInfo lineInfo = new LayerInfo();
-        lineInfo.setName(MS_LINE_PREFIX + uuid);
-        lineInfo.setType("LINE");
-        lineInfo.setDirectory(uuid);
-        lineInfo.setData(WSPLGEN_LINES_SHAPE);
-        lineInfo.setTitle("I18N_LINE_SHAPE");
-        lineInfo.setGroup(group);
-        lineInfo.setGroupTitle(groupTitle);
-        lineInfo.setSrid(wms.getSrid());
-
-        String nameLines = MS_LAYER_PREFIX + wms.getName() + "-lines";
-
-        Template tpl = getTemplateByName(SHP_LAYER_TEMPLATE);
-        if (tpl == null) {
-            logger.warn("Template '" + SHP_LAYER_TEMPLATE + "' found.");
-            return;
-        }
-
-        try {
-            writeLayer(lineInfo, new File(dir, nameLines), tpl);
-        }
-        catch (FileNotFoundException fnfe) {
-            logger.error(fnfe, fnfe);
-            logger.warn("Unable to write layer: " + nameLines);
-        }
-    }
-
-    protected void createBarriersPolygonLayer(
-            FLYSArtifact  flys,
-            WMSLayerFacet wms
-        )
-        throws FileNotFoundException, IOException
-        {
-            String uuid       = flys.identifier();
-            String group      = uuid + MS_BARRIERS_PREFIX;
-            String groupTitle = "I18N_BARRIERS_TITLE";
-
-            File dir  = new File(getShapefileBaseDir(), uuid);
-            File test = new File(dir, WSPLGEN_POLYGONS_SHAPE);
-
-            if (!test.exists() || !test.canRead()) {
-                logger.debug("No barrier line layer existing.");
-                return;
-            }
-
-            LayerInfo polygonInfo = new LayerInfo();
-            polygonInfo.setName(MS_POLYGONS_PREFIX + uuid);
-            polygonInfo.setType("POLYGON");
-            polygonInfo.setDirectory(uuid);
-            polygonInfo.setData(WSPLGEN_POLYGONS_SHAPE);
-            polygonInfo.setTitle("I18N_POLYGON_SHAPE");
-            polygonInfo.setGroup(group);
-            polygonInfo.setGroupTitle(groupTitle);
-            polygonInfo.setSrid(wms.getSrid());
-
-            String namePolygons = MS_LAYER_PREFIX + wms.getName() + "-polygons";
-
-            Template tpl = getTemplateByName(SHP_LAYER_TEMPLATE);
-            if (tpl == null) {
-                logger.warn("Template '" + SHP_LAYER_TEMPLATE + "' found.");
-                return;
-            }
-
-            try {
-                writeLayer(polygonInfo, new File(dir, namePolygons), tpl);
-            }
-            catch (FileNotFoundException fnfe) {
-                logger.error(fnfe, fnfe);
-                logger.warn("Unable to write layer: " + namePolygons);
-            }
-        }
-
-
-        /**
-         * Creates a layer file used for Mapserver's mapfile which represents the
-         * shape files uploaded by the user.
-         *
-         * @param flys The FLYSArtifact that owns <i>wms</i>.
-         * @param wms The WMSLayerFacet that contains information for the layer.
-         */
-        public void createUserShapeLayer(FLYSArtifact flys, WMSLayerFacet wms)
-        throws FileNotFoundException, IOException
-        {
-            logger.debug("createUserShapeLayer");
-
-            String uuid = flys.identifier();
-            File   dir  = new File(getShapefileBaseDir(), uuid);
-            File   test = new File(dir, WSPLGEN_USER_SHAPE);
-
-            if (!test.exists() || !test.canRead()) {
-                logger.debug("No user layer existing.");
-                return;
-            }
-
-            File userShape = new File(dir, WSPLGEN_USER_SHAPE);
-            ShpFiles sf = new ShpFiles(userShape);
-            ShapefileReader sfr = new ShapefileReader(sf, true, false, null);
-            ShapefileHeader sfh = sfr.getHeader();
-
-            String group      = uuid + MS_USERSHAPE_PREFIX;
-            String groupTitle = "I18N_USER_SHAPE_TITLE";
-
-            LayerInfo info = new LayerInfo();
-            info.setName(MS_USERSHAPE_PREFIX + uuid);
-            if (sfh.getShapeType().isLineType()) {
-                info.setType("LINE");
-            }
-            else if (sfh.getShapeType().isPolygonType()) {
-                info.setType("POLYGON");
-            }
-            else {
-                return;
-            }
-            info.setDirectory(uuid);
-            info.setData(WSPLGEN_USER_SHAPE);
-            info.setTitle("I18N_USER_SHAPE");
-            info.setGroup(group);
-            info.setGroupTitle(groupTitle);
-            info.setSrid(wms.getSrid());
-
-            String nameUser = MS_LAYER_PREFIX + wms.getName();
-
-            Template tpl = getTemplateByName(SHP_LAYER_TEMPLATE);
-            if (tpl == null) {
-                logger.warn("Template '" + SHP_LAYER_TEMPLATE + "' found.");
-                return;
-            }
-
-            try {
-                writeLayer(info, new File(dir, nameUser), tpl);
-            }
-            catch (FileNotFoundException fnfe) {
-                logger.error(fnfe, fnfe);
-                logger.warn("Unable to write layer: " + nameUser);
-            }
-
-        }
-
-
-        /**
-         * Creates a layer file used for Mapserver's mapfile which represents
-         * geometries from database.
-         *
-         * @param flys The FLYSArtifact that owns <i>wms</i>.
-         * @param wms The WMSLayerFacet that contains information for the layer.
-         */
-        public void createDatabaseLayer(
-            FLYSArtifact    flys,
-            WMSDBLayerFacet wms,
-            String          style
-        )
-        throws FileNotFoundException, IOException
-        {
-            logger.debug("createDatabaseLayer");
-
-            LayerInfo layerinfo = new LayerInfo();
-            layerinfo.setName(wms.getName() + "-" + flys.identifier());
-            layerinfo.setType(wms.getGeometryType());
-            layerinfo.setFilter(wms.getFilter());
-            layerinfo.setData(wms.getData());
-            layerinfo.setTitle(wms.getDescription());
-            layerinfo.setStyle(style);
-            if(wms.getExtent() != null) {
-                layerinfo.setExtent(GeometryUtils.jtsBoundsToOLBounds(wms.getExtent()));
-            }
-            layerinfo.setConnection(wms.getConnection());
-            layerinfo.setConnectionType(wms.getConnectionType());
-            layerinfo.setLabelItem(wms.getLabelItem());
-            layerinfo.setSrid(wms.getSrid());
-
-            String name = MS_LAYER_PREFIX + wms.getName();
-
-            Template template = getTemplateByName(DB_LAYER_TEMPLATE);
-            if (template == null) {
-                logger.warn("Template '" + DB_LAYER_TEMPLATE + "' found.");
-                return;
-            }
-
-            try {
-                File dir = new File(getShapefileBaseDir(), flys.identifier());
-                writeLayer(layerinfo, new File(dir, name), template);
-            }
-            catch (FileNotFoundException fnfe) {
-                logger.error(fnfe, fnfe);
-                logger.warn("Unable to write layer: " + name);
-            }
-        }
-
-        @Override
-        protected String getMapfilePath() {
-            return FLYSUtils.getXPathString(FLYSUtils.XPATH_FLOODMAP_MAPFILE_PATH);
-        }
-
-        @Override
-        protected String getMapfileTemplate() {
-            return FLYSUtils.getXPathString(FLYSUtils.XPATH_FLOODMAP_MAPFILE_TEMPLATE);
-        }
-
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/utils/DataUtil.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,34 +0,0 @@
-package de.intevation.flys.utils;
-
-import java.util.Random;
-
-import gnu.trove.TDoubleArrayList;
-
-public class DataUtil
-{
-    public static boolean guessWaterIncreasing(TDoubleArrayList data) {
-        return guessWaterIncreasing(data, 0.05f);
-    }
-
-    public static boolean guessWaterIncreasing(TDoubleArrayList data, float factor) {
-        int N = data.size();
-        if (N < 2) return false;
-
-        int samples = (int)(factor*N) + 1;
-
-        int up = 0;
-
-        Random rand = new Random();
-
-        for (int i = 0; i < samples; ++i) {
-            int    pos2 = rand.nextInt(N-1) + 1;
-            int    pos1 = rand.nextInt(pos2);
-            double w1   = data.getQuick(pos1);
-            double w2   = data.getQuick(pos2);
-            if (w2 > w1) ++up;
-        }
-
-        return up > samples/2;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/utils/DateAverager.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-package de.intevation.flys.utils;
-
-import gnu.trove.TLongArrayList;
-
-import java.util.Date;
-
-public class DateAverager
-{
-   protected TLongArrayList dates;
-
-    public DateAverager() {
-        dates = new TLongArrayList();
-    }
-
-    public void add(Date date) {
-        dates.add(date.getTime());
-    }
-
-    public Date getAverage() {
-        int N = dates.size();
-        if (N == 0) {
-            return null;
-        }
-        long min = dates.min();
-        long sum = 0L;
-        for (int i = 0; i < N; ++i) {
-            sum += dates.getQuick(i) - min;
-        }
-        return new Date(min + (long)Math.round(sum/(double)N));
-    }
-
-    public void clear() {
-        dates.resetQuick();
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/utils/DoubleUtil.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,246 +0,0 @@
-package de.intevation.flys.utils;
-
-import de.intevation.flys.artifacts.math.Linear;
-
-import gnu.trove.TDoubleArrayList;
-
-import java.util.Arrays;
-
-import org.apache.log4j.Logger;
-
-/** Utils to deal with Double precision values. */
-public class DoubleUtil
-{
-    /** Private logger. */
-    private static Logger log = Logger.getLogger(DoubleUtil.class);
-
-    public static final double DEFAULT_STEP_PRECISION = 1e6;
-
-    /** EPSILON for comparison of double precision values. */
-    public static final double EPSILON = 1e-4;
-
-    private DoubleUtil() {
-    }
-
-    public static final double [] explode(double from, double to, double step) {
-        return explode(from, to, step, DEFAULT_STEP_PRECISION);
-    }
-
-    public static final double round(double x, double precision) {
-        return Math.round(x * precision)/precision;
-    }
-
-    public static final double round(double x) {
-        return Math.round(x * DEFAULT_STEP_PRECISION)/DEFAULT_STEP_PRECISION;
-    }
-
-    /**
-     * Returns array with values from parameter from to to with given step width.
-     * from and to are included.
-     */
-    public static final double [] explode(
-        double from,
-        double to,
-        double step,
-        double precision
-    ) {
-        double lower = from;
-
-        double diff = to - from;
-        double tmp  = diff / step;
-        int    num = (int)Math.abs(Math.ceil(tmp)) + 1;
-
-        if (num < 1) {
-            return new double[0];
-        }
-
-        double [] values = new double[num];
-
-        if (from > to) {
-            step = -step;
-        }
-
-        double max = Math.max(from, to);
-
-        for (int idx = 0; idx < num; idx++) {
-            if (lower - max > EPSILON) {
-                return Arrays.copyOfRange(values, 0, idx);
-            }
-
-            values[idx] = round(lower, precision);
-            lower      += step;
-        }
-
-        return values;
-    }
-
-    public static final double interpolateSorted(
-        double [] xs,
-        double [] ys,
-        double x
-    ) {
-        int lo = 0, hi = xs.length-1;
-
-        int mid = -1;
-
-        while (lo <= hi) {
-            mid = (lo + hi) >> 1;
-            double mx = xs[mid];
-                 if (x < mx) hi = mid - 1;
-            else if (x > mx) lo = mid + 1;
-            else return ys[mid];
-        }
-        if (mid < lo) {
-            return lo >= xs.length
-                ? Double.NaN
-                : Linear.linear(x, xs[mid], xs[mid+1], ys[mid], ys[mid+1]);
-        }
-        return hi < 0
-            ? Double.NaN
-            : Linear.linear(x, xs[mid-1], xs[mid], ys[mid-1], ys[mid]);
-    }
-
-    public static final boolean isIncreasing(double [] array) {
-        int inc = 0;
-        int dec = 0;
-        int sweet = (array.length-1)/2;
-        for (int i = 1; i < array.length; ++i) {
-            if (array[i] > array[i-1]) {
-                if (++inc > sweet) {
-                    return true;
-                }
-            }
-            else if (++dec > sweet) {
-                return false;
-            }
-        }
-        return inc > sweet;
-    }
-
-    public static final double [] swap(double [] array) {
-        int lo = 0;
-        int hi = array.length-1;
-        while (hi > lo) {
-            double t  = array[lo];
-            array[lo] = array[hi];
-            array[hi] = t;
-            ++lo;
-            --hi;
-        }
-
-        return array;
-    }
-
-    public static final double [] swapClone(double [] in) {
-        double [] out = new double[in.length];
-
-        for (int j = out.length-1, i = 0; j >= 0;) {
-            out[j--] = in[i++];
-        }
-
-        return out;
-    }
-
-    public static final double [] sumDiffs(double [] in) {
-        double [] out = new double[in.length];
-
-        for (int i = 1; i < out.length; ++i) {
-            out[i] = out[i-1] + Math.abs(in[i-1] - in[i]);
-        }
-
-        return out;
-    }
-
-    public static final double [] fill(int N, double value) {
-        double [] result = new double[N];
-        Arrays.fill(result, value);
-        return result;
-    }
-
-
-    /** Use with parseSegments. */
-    public interface SegmentCallback {
-        void newSegment(double from, double to, double [] values);
-    }
-
-
-    /** Call callback for every string split by colon.
-     * Expected format FROM:TO:VALUE1,VALUE2,VALUE3*/
-    public static final void parseSegments(
-        String          input,
-        SegmentCallback callback
-    ) {
-        TDoubleArrayList vs = new TDoubleArrayList();
-
-        for (String segmentStr: input.split(":")) {
-            String [] parts = segmentStr.split(";");
-            if (parts.length < 3) {
-                log.warn("invalid segment: '" + segmentStr + "'");
-                continue;
-            }
-            try {
-                double from = Double.parseDouble(parts[0].trim());
-                double to   = Double.parseDouble(parts[1].trim());
-
-                vs.resetQuick();
-
-                for (String valueStr: parts[3].split(",")) {
-                    vs.add(round(Double.parseDouble(valueStr.trim())));
-                }
-
-                callback.newSegment(from, to, vs.toNativeArray());
-            }
-            catch (NumberFormatException nfe) {
-                log.warn("invalid segment: '" + segmentStr + "'");
-            }
-        }
-    }
-
-    public static final boolean isValid(double [][] data) {
-        for (double [] ds: data) {
-            for (double d: ds) {
-                if (Double.isNaN(d)) {
-                    return false;
-                }
-            }
-        }
-        return true;
-    }
-
-
-    /** In an array of doubles, search and return the maximum value. */
-    public static final double maxInArray(double[] values) {
-        double max = - Double.MAX_VALUE;
-        for (double d: values) {
-            if (d > max) max = d;
-        }
-        return max;
-    }
-
-    public static void removeNaNs(TDoubleArrayList [] arrays) {
-
-        int dest = 0;
-
-        int A = arrays.length;
-        int N = arrays[0].size();
-
-        OUTER: for (int i = 0; i < N; ++i) {
-            for (int j = 0; j < A; ++j) {
-                TDoubleArrayList a = arrays[j];
-                double v = a.getQuick(i);
-                if (Double.isNaN(v)) {
-                    continue OUTER;
-                }
-                a.setQuick(dest, v);
-            }
-            ++dest;
-        }
-
-        if (dest < N) {
-            for (int i = 0; i < A; ++i) {
-                arrays[i].remove(dest, N-dest);
-            }
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/utils/FLYSUtils.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,921 +0,0 @@
-package de.intevation.flys.utils;
-
-import de.intevation.artifactdatabase.state.State;
-import de.intevation.artifacts.Artifact;
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.common.utils.Config;
-import de.intevation.artifacts.common.utils.XMLUtils;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.StaticWKmsArtifact;
-import de.intevation.flys.artifacts.WINFOArtifact;
-import de.intevation.flys.artifacts.access.RangeAccess;
-import de.intevation.flys.artifacts.context.FLYSContext;
-import de.intevation.flys.artifacts.model.LocationProvider;
-import de.intevation.flys.artifacts.model.RiverFactory;
-import de.intevation.flys.artifacts.model.WKms;
-import de.intevation.flys.artifacts.model.WQ;
-import de.intevation.flys.artifacts.model.WQKms;
-import de.intevation.flys.artifacts.states.WDifferencesState;
-import de.intevation.flys.artifacts.states.WaterlevelSelectState;
-import de.intevation.flys.backend.SessionFactoryProvider;
-import de.intevation.flys.model.Gauge;
-import de.intevation.flys.model.MainValue;
-import de.intevation.flys.model.River;
-
-import gnu.trove.TDoubleArrayList;
-import gnu.trove.TIntArrayList;
-import gnu.trove.TLongArrayList;
-
-import java.text.NumberFormat;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import javax.xml.xpath.XPathConstants;
-
-import org.apache.log4j.Logger;
-import org.hibernate.SessionFactory;
-import org.hibernate.impl.SessionFactoryImpl;
-import org.w3c.dom.Document;
-
-
-/**
- * Static helper methods to e.g. access FLYSArtifacts data.
- *
- * @deprecated Don't use FLYSUtils to get data from an {@link Artifact} anymore.
- * Instead use and/or create a {@link Access} class hierarchy.
- **/
-@Deprecated
-public class FLYSUtils {
-
-    /** The logger that is used in this utility. */
-    private static Logger logger = Logger.getLogger(FLYSUtils.class);
-
-    /**
-     * An enum that represents the 5 possible WQ modes in FLYS. The 5 values are
-     * <i>QFREE</i> <i>QGAUGE</i> <i>WGAUGE</i> <i>WFREE</i> and <i>NONE</i>.
-     */
-    public static enum WQ_MODE { QFREE, QGAUGE, WFREE, WGAUGE, NONE };
-
-    /**
-     * An enum that represents the 4 possible WQ input modes in FLYS. The 4
-     * values are
-     * <i>ADAPTED</i> <i>SINGLE</i> <i>RANGE</i> and <i>NONE</i>.
-     */
-    public static enum WQ_INPUT { ADAPTED, SINGLE, RANGE, NONE };
-
-    public static final Pattern NUMBERS_PATTERN =
-        Pattern.compile("\\D*(\\d++.\\d*)\\D*");
-
-    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";
-
-    public static final String XPATH_FLOODMAP_VELOCITY_LOGFILE =
-        "/artifact-database/floodmap/velocity/logfile/@path";
-
-    public static final String XPATH_FLOODMAP_MAPSERVER_URL =
-        "/artifact-database/floodmap/mapserver/server/@path";
-
-    public static final String XPATH_RIVERMAP_MAPSERVER_URL =
-            "/artifact-database/rivermap/mapserver/server/@path";
-
-    public static final String XPATH_FLOODMAP_MAPFILE_PATH =
-        "/artifact-database/floodmap/mapserver/mapfile/@path";
-
-    public static final String XPATH_FLOODMAP_MAPFILE_TEMPLATE =
-        "/artifact-database/floodmap/mapserver/map-template/@path";
-
-    public static final String XPATH_FLOODMAP_MAPSERVER_TEMPLATE_PATH =
-        "/artifact-database/floodmap/mapserver/templates/@path";
-
-
-    private FLYSUtils() {
-    }
-
-
-    /**
-     * Pulls Artifact with given UUID fromm database.
-     * @return FLYSArtifact with given UUID or null (in case of errors).
-     */
-    public static FLYSArtifact getArtifact(String uuid, CallContext context) {
-        try {
-            Artifact artifact = context.getDatabase().getRawArtifact(uuid);
-
-            if (artifact == null) {
-                logger.error("Artifact '" + uuid + "' does not exist.");
-                return null;
-            }
-
-            if (!(artifact instanceof FLYSArtifact)) {
-                logger.error("Artifact '" +uuid+ "' is no valid FLYSArtifact.");
-                return null;
-            }
-
-            return (FLYSArtifact) artifact;
-        }
-        // TODO: catch more selective
-        catch (Exception e) {
-            logger.error("Cannot get FLYSArtifact " + uuid
-                + " from database (" + e.getMessage() + ").");
-            return null;
-        }
-    }
-
-
-    /**
-     * Returns the FLYSContext from context object.
-     *
-     * @param context The CallContext or the FLYSContext.
-     *
-     * @return the FLYSContext.
-     */
-    public static FLYSContext getFlysContext(Object context) {
-        return context instanceof FLYSContext
-            ? (FLYSContext) context
-            : (FLYSContext) ((CallContext) context).globalContext();
-    }
-
-
-    /**
-     * Convinience function to retrieve an XPath as string with replaced config
-     * directory.
-     *
-     * @param xpath The XPath expression.
-     *
-     * @return a string with replaced config directory.
-     */
-    public static String getXPathString(String xpath) {
-        String tmp = Config.getStringXPath(xpath);
-        tmp        = Config.replaceConfigDir(tmp);
-
-        return tmp;
-    }
-
-
-    public static boolean isUsingOracle() {
-        SessionFactory sf = SessionFactoryProvider.getSessionFactory();
-
-        String d = SessionFactoryProvider.getDriver((SessionFactoryImpl) sf);
-
-        return d != null ? d.indexOf("Oracle") >= 0 : false;
-    }
-
-
-    /**
-     * This method returns an WQ_MODE enum which is based on the parameters
-     * stored in <i>flys</i> Artifact. If there is no <i>wq_isq</i> parameter
-     * existing, WQ_MODE.NONE is returned.
-     *
-     * @param flys The FLYSArtifact that stores wq mode relevant parameters.
-     *
-     * @return an enum WQ_MODE.
-     */
-    public static WQ_MODE getWQMode(FLYSArtifact flys) {
-        if (flys == null) {
-            return WQ_MODE.NONE;
-        }
-
-        String values = flys.getDataAsString("wq_values");
-        Boolean isQ   = flys.getDataAsBoolean("wq_isq");
-
-        if (values != null) {
-            return isQ ? WQ_MODE.QGAUGE : WQ_MODE.WGAUGE;
-        }
-
-        Boolean isFree = flys.getDataAsBoolean("wq_isfree");
-
-        if (isQ != null && isQ) {
-            return isFree ? WQ_MODE.QFREE : WQ_MODE.QGAUGE;
-        }
-        else if (isQ != null && !isQ) {
-            return isFree ? WQ_MODE.WFREE : WQ_MODE.WGAUGE;
-        }
-        else {
-            return WQ_MODE.NONE;
-        }
-    }
-
-
-    public static WQ_INPUT getWQInputMode(FLYSArtifact flys) {
-        if (flys == null) {
-            return WQ_INPUT.NONE;
-        }
-
-        Boolean selection = flys.getDataAsBoolean("wq_isrange");
-        String adapted = flys.getDataAsString("wq_values");
-
-        if(adapted != null && adapted.length() > 0) {
-            return WQ_INPUT.ADAPTED;
-        }
-
-        if (selection != null && selection) {
-            return WQ_INPUT.RANGE;
-        }
-        else {
-            return WQ_INPUT.SINGLE;
-        }
-    }
-
-
-    /**
-     * Get bounds for river of artifact.
-     * @param flysArtifact artifact which has a "river" data.
-     * @return double array. min is at[0], max at[1]. null if given artifact is null
-     */
-    public static double[] getRiverMinMax(FLYSArtifact flysArtifact) {
-        if (flysArtifact == null) {
-            return null;
-        }
-
-        String riverName = flysArtifact.getDataAsString("river");
-
-        if (riverName == null) {
-            riverName = "";
-        }
-
-        logger.debug("Search for the min/max distances of '" + riverName + "'");
-
-        River river = RiverFactory.getRiver(riverName);
-
-        return river != null
-            ? river.determineMinMaxDistance()
-            : null;
-    }
-
-
-    public static double[] getKmFromTo(FLYSArtifact flys) {
-        String strFrom = flys.getDataAsString("ld_from");
-        String strTo   = flys.getDataAsString("ld_to");
-
-        if (strFrom == null) {
-            strFrom = flys.getDataAsString("from");
-        }
-
-        if (strTo == null) {
-            strTo = flys.getDataAsString("to");
-        }
-
-        if (strFrom == null || strTo == null) {
-            return null;
-        }
-
-        try {
-            return new double[] {
-                Double.parseDouble(strFrom),
-                Double.parseDouble(strTo) };
-        }
-        catch (NumberFormatException nfe) {
-            return null;
-        }
-    }
-
-
-    /**
-     * Return sorted array of locations at which stuff was calculated
-     * (from ld_locations data), null if not parameterized this way.
-     */
-    // TODO moved to RangeAccess. Resolve remaining calls.
-    private static double[] getLocations(FLYSArtifact flys) {
-        String locationStr = flys.getDataAsString("ld_locations");
-
-        if (locationStr == null || locationStr.length() == 0) {
-            if (flys instanceof WINFOArtifact) {
-                WINFOArtifact winfo = (WINFOArtifact) flys;
-                if (winfo.getReferenceStartKm() != null && winfo.getReferenceEndKms() != null) {
-                    return new double[]
-                        {
-                            winfo.getReferenceStartKm().doubleValue(),
-                            winfo.getReferenceEndKms()[0]
-                        };
-                }
-            }
-            return null;
-        }
-
-        String[] tmp               = locationStr.split(" ");
-        TDoubleArrayList locations = new TDoubleArrayList();
-
-        for (String l: tmp) {
-            try {
-                locations.add(Double.parseDouble(l));
-            }
-            catch (NumberFormatException nfe) {
-                logger.debug(nfe.getLocalizedMessage(), nfe);
-            }
-        }
-
-        locations.sort();
-
-        return locations.toNativeArray();
-    }
-
-
-    /**
-     * Returns the Qs for a given FLYSArtifact. This method currently accepts
-     * only instances of WINFOArtifact.
-     *
-     * @param flys A FLYSArtifact.
-     *
-     * @return the Qs.
-     */
-    public static double[] getQs(FLYSArtifact flys) {
-        // XXX this is not nice!
-        if (flys instanceof WINFOArtifact) {
-            return ((WINFOArtifact) flys).getQs();
-        }
-
-        logger.warn("This method currently supports WINFOArtifact only!");
-
-        return null;
-    }
-
-
-    /**
-     * Returns the Ws for a given FLYSArtifact. This method currently accepts
-     * only instances of WINFOArtifact.
-     *
-     * @param flys A FLYSArtifact.
-     *
-     * @return the Ws.
-     */
-    public static double[] getWs(FLYSArtifact flys) {
-        // XXX this is not nice!
-        if (flys instanceof WINFOArtifact) {
-            return ((WINFOArtifact) flys).getWs();
-        }
-
-        logger.warn("This method currently supports WINFOArtifact only!");
-
-        return null;
-    }
-
-
-    /**
-     * Returns the selected River object based on the 'river' data that might
-     * have been inserted by the user.
-     *
-     * @return the selected River or null if no river has been chosen yet.
-     */
-    public static River getRiver(FLYSArtifact flys) {
-        String sRiver = getRivername(flys);
-
-        return (sRiver != null)
-            ? RiverFactory.getRiver(sRiver)
-            : null;
-    }
-
-
-    /**
-     * Returns the name of the river specified in the given <i>flys</i>
-     * Artifact.
-     *
-     * @param flys The FLYSArtifact that stores a river relevant information.
-     *
-     * @return the name of the specified river or null.
-     */
-    public static String getRivername(FLYSArtifact flys) {
-        return flys != null ? flys.getDataAsString("river") : null;
-    }
-
-
-    /**
-     * Extracts the SRID defined in the global configuration for the river
-     * specified in <i>artifact</i>.
-     *
-     * @param artifact The FLYSArtifact that stores the name of the river.
-     *
-     * @return the SRID as string (e.g. "31466").
-     */
-    public static String getRiverSrid(FLYSArtifact artifact) {
-        String river = artifact.getDataAsString("river");
-
-        if (river == null || river.length() == 0) {
-            return null;
-        }
-
-        return getRiverSrid(river);
-    }
-
-
-    public static String getRiverSrid(String rivername) {
-        Map<String, String> variables = new HashMap<String, String>(1);
-        variables.put("name", rivername);
-
-        Document cfg = Config.getConfig();
-
-        return (String) XMLUtils.xpath(
-            cfg,
-            XPATH_FLOODMAP_RIVER_PROJECTION,
-            XPathConstants.STRING,
-            null,
-            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.
-     * @param flys the artifact in question.
-     * @return (First) gauge of locations of river of artifact.
-     */
-    public static Gauge getGauge(FLYSArtifact flys) {
-        River river = getRiver(flys);
-
-        if (river == null) {
-            logger.debug("no river found");
-            return null;
-        }
-
-        RangeAccess rangeAccess = new RangeAccess(flys, null);
-        double[] dist = rangeAccess.getKmRange();
-
-        if (dist == null) {
-            logger.debug("no range found");
-            return null;
-        }
-
-        if (logger.isDebugEnabled()) {
-            logger.debug("Determine gauge for:");
-            logger.debug("... river: " + river.getName());
-            logger.debug("... distance: " + dist[0] + " - " + dist[1]);
-        }
-
-        Gauge gauge = river.determineGauge(dist[0], dist[1]);
-
-        String name = gauge != null ? gauge.getName() : "'n/a";
-        logger.debug("Found gauge: " + name);
-
-        return gauge;
-    }
-
-
-    public static String getGaugename(FLYSArtifact flys) {
-        Gauge gauge = getGauge(flys);
-
-        return gauge != null ? gauge.getName() : null;
-    }
-
-
-    public static Gauge getReferenceGauge(FLYSArtifact flys) {
-        Long officialNumber = flys.getDataAsLong("reference_gauge");
-
-        return officialNumber != null
-            ? Gauge.getGaugeByOfficialNumber(officialNumber)
-            : null;
-    }
-
-
-    public static String getReferenceGaugeName(FLYSArtifact flys) {
-        Gauge refGauge = getReferenceGauge(flys);
-
-        return refGauge != null
-            ? refGauge.getName()
-            : "-- not found --";
-    }
-
-
-    public static Double getValueFromWQ(WQ wq) {
-        if (wq == null) {
-            return null;
-        }
-
-        Matcher m = NUMBERS_PATTERN.matcher(wq.getName());
-
-        if (m.matches()) {
-            logger.debug("Found a number.");
-
-            String raw = m.group(1);
-
-            try {
-                return Double.valueOf(raw);
-            }
-            catch (NumberFormatException nfe) {
-            }
-        }
-
-        return null;
-    }
-
-
-    /** Creates human-readable name for a wsp (waterlevel/longitudinal section).
-     * @param name will be split at '='s.
-     */
-    public static String createWspWTitle(
-        WINFOArtifact winfo,
-        CallContext   cc,
-        String        name
-    ) {
-        String[] parts = name.split("=");
-
-        NumberFormat nf = Formatter.getWaterlevelW(cc);
-
-        String namedMainValue = null;
-
-        boolean isQ    = winfo.isQ();
-        boolean isFree = winfo.isFreeQ();
-
-        double v;
-
-        try {
-            v = Double.valueOf(parts[1]);
-
-            namedMainValue = getNamedMainValue(winfo.getGauge(), v);
-        }
-        catch (NumberFormatException nfe) {
-            logger.warn("Cannot parse Double of: '" + parts[1] + "'");
-            return name;
-        }
-
-        String prefix = null;
-
-        if (isQ && !isFree && namedMainValue != null) {
-            return "W (" + namedMainValue + ")";
-        }
-
-        if (isQ) {
-            prefix = "Q=";
-        }
-
-        return prefix == null
-            ? "W(" + nf.format(v) + ")"
-            : "W(" + prefix + nf.format(v) + ")";
-    }
-
-
-    public static String createWspQTitle(
-        WINFOArtifact winfo,
-        CallContext   cc,
-        String        name
-    ) {
-        String[] parts = name.split("=");
-
-        NumberFormat nf = Formatter.getWaterlevelQ(cc);
-
-        String namedMainValue = null;
-
-        boolean isQ    = winfo.isQ();
-        boolean isFree = winfo.isFreeQ();
-
-        double v;
-
-        try {
-            v = Double.valueOf(parts[1]);
-
-            namedMainValue = getNamedMainValue(winfo.getGauge(), v);
-        }
-        catch (NumberFormatException nfe) {
-            logger.warn("Cannot parse Double of: '" + parts[1] + "'");
-            return name;
-        }
-
-        String prefix = null;
-
-        if (isQ && !isFree && namedMainValue != null) {
-            return namedMainValue;
-        }
-
-        if (!isQ) {
-            prefix = "W=";
-        }
-
-        return prefix == null
-            ? "Q(" + nf.format(v) + ")"
-            : "Q(" + prefix + nf.format(v) + ")";
-    }
-
-
-    /**
-     * Returns the named main value if a Q was selected and if this Q fits to a
-     * named main value. Otherwise, this function returns null.
-     *
-     * @param winfo The WINFO Artifact.
-     * @param value The Q (or W) value.
-     *
-     * @return a named main value or null.
-     */
-    public static String getNamedMainValue(WINFOArtifact winfo, double value) {
-        WQ_MODE wqmode = getWQMode(winfo);
-
-        if (wqmode != WQ_MODE.QGAUGE) {
-            return null;
-        }
-        else {
-            return getNamedMainValue(winfo.getGauge(), value);
-        }
-    }
-
-
-    public static String getNamedMainValue(Gauge gauge, double value) {
-        List<MainValue> mainValues = gauge.getMainValues();
-        logger.debug("Search named main value for: " + value);
-
-        for (MainValue mv: mainValues) {
-            if (mv.getValue().doubleValue() == value) {
-                logger.debug("Found named main value: " + mv.getMainValue().getName());
-                return mv.getMainValue().getName();
-            }
-        }
-
-        logger.debug("Did not find a named main value for: " + value);
-        return null;
-    }
-
-
-    /**
-     *
-     * @param nmv A string that represents a named main value.
-     *
-     * @throws NullPointerException if nmv is null.
-     */
-    public static String stripNamedMainValue(String nmv) {
-        int startIndex = nmv.indexOf("(");
-        int endIndex   = nmv.indexOf(")");
-
-        if (startIndex > 0 && endIndex > 0 && startIndex < endIndex) {
-            return nmv.substring(0, startIndex);
-        }
-
-        return nmv;
-    }
-
-
-    /**
-     * Returns the URL of user mapfile for the owner of Artifact
-     * <i>artifactId</i>.
-     *
-     * @param artifactId The UUID of an artifact.
-     *
-     * @return the URL of the user wms.
-     */
-    public static String getUserWMSUrl(String artifactId) {
-        String url = getXPathString(XPATH_FLOODMAP_MAPSERVER_URL);
-        url = url.endsWith("/") ? url + "user-wms" : url + "/" + "user-wms";
-
-        return url;
-    }
-
-
-    public static String getRiverWMSUrl() {
-        String url = getXPathString(XPATH_RIVERMAP_MAPSERVER_URL);
-        url = url.endsWith("/") ? url + "river-wms" : url + "/" + "river-wms";
-
-        return url;
-    }
-
-
-    /**
-     * This method returns the description for a given <i>km</i> for a specific
-     * river. The river is provided by the FLYSArtifact <i>flys</i>.
-     *
-     * @param flys The FLYSArtifact that provides a river.
-     * @param km The kilometer.
-     *
-     * @return the description for <i>km</i> or an empty string if no
-     * description was found.
-     */
-    public static String getLocationDescription(FLYSArtifact flys, double km) {
-        String river = getRivername(flys);
-
-        if (river == null) {
-            return "";
-        }
-
-        return LocationProvider.getLocation(river, km);
-    }
-
-
-    /**
-     * This method returns the differences for a w-differences calculation.
-     *
-     * @param winfo The WINFOArtifact.
-     * @param context The context.
-     *
-     * @return The differences as string separated by semicolon and linebreak.
-     */
-    public static String getWDifferences(
-        WINFOArtifact winfo,
-        CallContext context)
-    {
-        State state = winfo.getCurrentState(context);
-        if(state instanceof WDifferencesState) {
-            String diffids = winfo.getDataAsString("diffids");
-            String datas[] = diffids.split("#");
-
-            // Validate the Data-Strings.
-            for (String s: datas) {
-                if (!WaterlevelSelectState.isValueValid(s)) {
-                    return "";
-                }
-            }
-
-            if (datas.length < 2) {
-                return "";
-            }
-
-            String diffs = "";
-            for(int i = 0; i < datas.length; i+=2) {
-                // e.g.:
-                // 42537f1e-3522-42ef-8968-635b03d8e9c6;longitudinal_section.w;1
-                WKms minuendWKms = getWKms(StringUtil.unbracket(datas[i+0]),
-                    context);
-                WKms subtrahendWKms = getWKms(StringUtil.unbracket(datas[i+1]),
-                    context);
-                if (minuendWKms != null && subtrahendWKms != null) {
-                    diffs += StringUtil.wWrap(minuendWKms.getName())
-                        + " - " + StringUtil.wWrap(subtrahendWKms.getName());
-                }
-                diffs += ";\n";
-            }
-            return diffs;
-        }
-        else {
-            logger.warn("Not a valid state for differences.");
-            return "";
-        }
-    }
-
-
-    protected static WKms getWKms(String mingle, CallContext context) {
-        String[] def  = mingle.split(";");
-        String   uuid = def[0];
-        String   name = def[1];
-        int      idx  = Integer.parseInt(def[2]);
-
-        if (name.startsWith("staticwkms")) {
-            StaticWKmsArtifact staticWKms =
-                (StaticWKmsArtifact) FLYSUtils.getArtifact(
-                    uuid,
-                    context);
-            WKms wkms = staticWKms.getWKms(idx);
-            if (wkms == null)
-                logger.error("No WKms from artifact.");
-            return wkms;
-        }
-
-        WINFOArtifact flys = (WINFOArtifact) FLYSUtils.getArtifact(
-            uuid,
-            context);
-
-        if (flys == null) {
-            logger.warn("One of the artifacts (1) for diff calculation could not be loaded");
-            return null;
-        }
-        else{
-            WQKms[] wqkms = (WQKms[]) flys.getWaterlevelData().
-                                              getData();
-            if (wqkms == null)
-            logger.warn("not  waterlevels in artifact");
-            else if (wqkms.length < idx)
-            logger.warn("not enough waterlevels in artifact");
-            return wqkms[idx];
-        }
-    }
-
-
-    /**
-     * This method transform a string into an int array. Therefore, the string
-     * <i>raw</i> must consist of int values separated by a <i>';'</i>.
-     *
-     * @param raw The raw integer array as string separated by a ';'.
-     *
-     * @return an array of int values.
-     */
-    public static int[] intArrayFromString(String raw) {
-        String[] splitted = raw != null ? raw.split(";") : null;
-
-        if (splitted == null || splitted.length == 0) {
-            logger.warn("No integer values found in '" + raw + "'");
-            return new int[0];
-        }
-
-        TIntArrayList integers = new TIntArrayList(splitted.length);
-
-        for (String value: splitted) {
-            try {
-                integers.add(Integer.parseInt(value));
-            }
-            catch (NumberFormatException nfe) {
-                logger.warn("Parsing integer failed: " + nfe);
-            }
-        }
-
-        return integers.toNativeArray();
-    }
-
-
-    /**
-     * This method transform a string into a long array. Therefore, the string
-     * <i>raw</i> must consist of int values separated by a <i>';'</i>.
-     *
-     * @param raw The raw long array as string separated by a ';'.
-     *
-     * @return an array of int values.
-     */
-    public static long[] longArrayFromString(String raw) {
-        String[] splitted = raw != null ? raw.split(";") : null;
-
-        if (splitted == null || splitted.length == 0) {
-            logger.warn("No long values found in '" + raw + "'");
-            return new long[0];
-        }
-
-        TLongArrayList longs = new TLongArrayList(splitted.length);
-
-        for (String value: splitted) {
-            try {
-                longs.add(Long.valueOf(value));
-            }
-            catch (NumberFormatException nfe) {
-                logger.warn("Parsing long failed: " + nfe);
-            }
-        }
-
-        return longs.toNativeArray();
-    }
-
-
-    /**
-     * This method transform a string into an double array. Therefore, the
-     * string <i>raw</i> must consist of double values separated by a
-     * <i>';'</i>.
-     *
-     * @param raw The raw double array as string separated by a ';'.
-     *
-     * @return an array of double values.
-     */
-    public static double[] doubleArrayFromString(String raw) {
-        String[] splitted = raw != null ? raw.split(";") : null;
-
-        if (splitted == null || splitted.length == 0) {
-            logger.warn("No double values found in '" + raw + "'");
-            return new double[0];
-        }
-
-        TDoubleArrayList doubles = new TDoubleArrayList(splitted.length);
-
-        for (String value: splitted) {
-            try {
-                doubles.add(Double.valueOf(value));
-            }
-            catch (NumberFormatException nfe) {
-                logger.warn("Parsing double failed: " + nfe);
-            }
-        }
-
-        return doubles.toNativeArray();
-    }
-
-
-    /**
-     * Returns the gauges that match the selected kilometer range.
-     *
-     * @param flys the flys artifact.
-     *
-     * @return the gauges based on the selected kilometer range (null if
-     *         none/no range set).
-     */
-    public static List<Gauge> getGauges(FLYSArtifact flys) {
-
-        River river = getRiver(flys);
-        if (river == null) {
-            logger.debug("getGauges: no river!");
-            return null;
-        }
-
-        RangeAccess rangeAccess = new RangeAccess(flys, null);
-        double[] dist = rangeAccess.getKmRange();
-        if (dist == null) {
-            logger.debug("getGauges: no dist!");
-            return null;
-        }
-        logger.debug("getGauges: " + dist[0] + " - " + dist[1]);
-
-        return river.determineGauges(dist[0], dist[1]);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/utils/Formatter.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,406 +0,0 @@
-package de.intevation.flys.utils;
-
-import java.text.DateFormat;
-import java.text.NumberFormat;
-import java.text.SimpleDateFormat;
-import java.util.Locale;
-
-import de.intevation.artifacts.CallContext;
-import de.intevation.artifacts.CallMeta;
-import de.intevation.flys.artifacts.resources.Resources;
-
-
-/** Helper to access static i18n Formatters. */
-public final class Formatter {
-
-    // KMS IN ERROR REPORTS.
-    public static final int CALCULATION_REPORT_KM_MIN_DIGITS = 1;
-    public static final int CALCULATION_REPORT_KM_MAX_DIGITS = 3;
-
-    // WATERLEVEL FORMATTER CONSTANTS
-    public static final int WATERLEVEL_KM_MIN_DIGITS = 3;
-    public static final int WATERLEVEL_KM_MAX_DIGITS = 3;
-    public static final int WATERLEVEL_W_MIN_DIGITS  = 0;
-    public static final int WATERLEVEL_W_MAX_DIGITS  = 2;
-    public static final int WATERLEVEL_Q_MIN_DIGITS  = 0;
-    public static final int WATERLEVEL_Q_MAX_DIGITS  = 2;
-
-
-    // COMPUTED DISCHARGE CURVE FORMATTER CONSTANTS
-    public static final int COMPUTED_DISCHARGE_W_MIN_DIGITS  = 2;
-    public static final int COMPUTED_DISCHARGE_W_MAX_DIGITS  = 2;
-    public static final int COMPUTED_DISCHARGE_Q_MIN_DIGITS  = 0;
-    public static final int COMPUTED_DISCHARGE_Q_MAX_DIGITS  = 2;
-
-
-    // HISTORICAL DISCHARGE CURVE FORMATTER CONSTANTS
-    public static final int HISTORICAL_DISCHARGE_W_MIN_DIGITS = 0;
-    public static final int HISTORICAL_DISCHARGE_W_MAX_DIGITS = 2;
-    public static final int HISTORICAL_DISCHARGE_Q_MIN_DIGITS = 0;
-    public static final int HISTORICAL_DISCHARGE_Q_MAX_DIGITS = 2;
-
-
-    // DURATION CURVE FORMATTER CONSTANTS
-    public static final int DURATION_W_MIN_DIGITS = 0;
-    public static final int DURATION_W_MAX_DIGITS = 2;
-    public static final int DURATION_Q_MIN_DIGITS = 0;
-    public static final int DURATION_Q_MAX_DIGITS = 1;
-    public static final int DURATION_D_MIN_DIGITS = 0;
-    public static final int DURATION_D_MAX_DIGITS = 0;
-
-
-    // FLOW VELOCITY FORMATTER CONSTANTS
-    public static final int FLOW_VELOCITY_KM_MIN_DIGITS     = 3;
-    public static final int FLOW_VELOCITY_KM_MAX_DIGITS     = 3;
-    public static final int FLOW_VELOCITY_VALUES_MIN_DIGITS = 2;
-    public static final int FLOW_VELOCITY_VALUES_MAX_DIGITS = 2;
-    public static final int FLOW_VELOCITY_Q_MIN_DIGITS      = 0;
-    public static final int FLOW_VELOCITY_Q_MAX_DIGITS      = 2;
-
-
-    // MIDDLE BED HEIGHT FORMATTER CONSTANTS
-    public static final int MIDDLE_BED_HEIGHT_KM_MIN_DIGITS             = 3;
-    public static final int MIDDLE_BED_HEIGHT_KM_MAX_DIGITS             = 3;
-    public static final int MIDDLE_BED_HEIGHT_HEIGHT_MIN_DIGITS         = 3;
-    public static final int MIDDLE_BED_HEIGHT_HEIGHT_MAX_DIGITS         = 3;
-    public static final int MIDDLE_BED_HEIGHT_UNCERT_MIN_DIGITS         = 3;
-    public static final int MIDDLE_BED_HEIGHT_UNCERT_MAX_DIGITS         = 3;
-    public static final int MIDDLE_BED_HEIGHT_DATAGAP_MIN_DIGITS        = 2;
-    public static final int MIDDLE_BED_HEIGHT_DATAGAP_MAX_DIGITS        = 2;
-    public static final int MIDDLE_BED_HEIGHT_SOUNDING_WIDTH_MIN_DIGITS = 0;
-    public static final int MIDDLE_BED_HEIGHT_SOUNDING_WIDTH_MAX_DIGITS = 0;
-    public static final int MIDDLE_BED_HEIGHT_WIDTH_MIN_DIGITS          = 3;
-    public static final int MIDDLE_BED_HEIGHT_WIDTH_MAX_DIGITS          = 3;
-
-    public static final int FIX_DELTA_W_KM_MIN_DIGITS = 3;
-    public static final int FIX_DELTA_W_KM_MAX_DIGITS = 3;
-    public static final int FIX_DELTA_W_DELTA_W_MIN_DIGITS = 3;
-    public static final int FIX_DELTA_W_DELTA_W_MAX_DIGITS = 3;
-    public static final int FIX_DELTA_W_DELTA_Q_MIN_DIGITS  = 0;
-    public static final int FIX_DELTA_W_DELTA_Q_MAX_DIGITS  = 2;
-
-
-    /**
-     * Creates a localized NumberFormatter with given range of decimal digits.
-     * @param m CallMeta to find the locale.
-     * @param min minimum number of decimal ("fraction") digits.
-     * @param max maximum number of decimal ("fraction") digits.
-     * @return A NumberFormat. Use #format(NUMBER) to get String representation
-     *         of NUMBER.
-     */
-    public static NumberFormat getFormatter(CallMeta m, int min, int max){
-        Locale       locale = Resources.getLocale(m);
-        NumberFormat nf     = NumberFormat.getInstance(locale);
-
-        nf.setMaximumFractionDigits(max);
-        nf.setMinimumFractionDigits(min);
-
-        return nf;
-    }
-
-
-    public static NumberFormat getFormatter(CallContext c, int min, int max){
-        return getFormatter(c.getMeta(), min, max);
-    }
-
-
-    /**
-     * Returns a number formatter with no max or min digits set.
-     *
-     * @param c The CallContext.
-     *
-     * @return a number formatter.
-     */
-    public static NumberFormat getRawFormatter(CallContext c) {
-        Locale locale = Resources.getLocale(c.getMeta());
-        return NumberFormat.getInstance(locale);
-    }
-
-
-    /**
-     * Returns a date formatter with SHORT style.
-     */
-    public static DateFormat getShortDateFormat(CallContext cc) {
-        Locale locale = Resources.getLocale(cc.getMeta());
-        return DateFormat.getDateInstance(DateFormat.SHORT, locale);
-    }
-
-
-    /**
-     * Returns a date formatter with MEDIUM style.
-     */
-    public static DateFormat getMediumDateFormat(CallContext cc) {
-        Locale locale = Resources.getLocale(cc.getMeta());
-        return DateFormat.getDateInstance(DateFormat.MEDIUM, locale);
-    }
-
-
-    /**
-     * Returns the number formatter for kilometer values in waterlevel exports.
-     *
-     * @return the number formatter for kilometer values.
-     */
-    public static NumberFormat getWaterlevelKM(CallContext context) {
-        return getFormatter(
-                context,
-                WATERLEVEL_KM_MIN_DIGITS,
-                WATERLEVEL_KM_MAX_DIGITS);
-    }
-
-
-    public static NumberFormat getWaterlevelW(CallMeta meta) {
-        return getFormatter(
-                meta,
-                WATERLEVEL_W_MIN_DIGITS,
-                WATERLEVEL_W_MAX_DIGITS);
-    }
-
-
-    /**
-     * Returns the number formatter for W values in waterlevel exports.
-     *
-     * @return the number formatter for W values.
-     */
-    public static NumberFormat getWaterlevelW(CallContext context) {
-        return getFormatter(
-                context,
-                WATERLEVEL_W_MIN_DIGITS,
-                WATERLEVEL_W_MAX_DIGITS);
-    }
-
-
-    /**
-     * Returns the number formatter for Q values in waterlevel exports.
-     *
-     * @return the number formatter for Q values.
-     */
-    public static NumberFormat getWaterlevelQ(CallContext context) {
-        return getFormatter(
-                context,
-                WATERLEVEL_Q_MIN_DIGITS,
-                WATERLEVEL_Q_MAX_DIGITS);
-    }
-
-
-    public static NumberFormat getWaterlevelQ(CallMeta meta) {
-        return getFormatter(
-                meta,
-                WATERLEVEL_Q_MIN_DIGITS,
-                WATERLEVEL_Q_MAX_DIGITS);
-    }
-
-    /**
-     * Returns the number formatter for W values in exports of computed
-     * discharge curves.
-     *
-     * @return the number formatter for W values.
-     */
-    public static NumberFormat getComputedDischargeW(CallContext context) {
-        return getFormatter(
-                context,
-                COMPUTED_DISCHARGE_W_MIN_DIGITS,
-                COMPUTED_DISCHARGE_W_MAX_DIGITS);
-    }
-
-
-    /**
-     * Returns the number formatter for Q values in exports of computed
-     * discharge curves.
-     *
-     * @return the number formatter for Q values.
-     */
-    public static NumberFormat getComputedDischargeQ(CallContext context) {
-        return getFormatter(
-                context,
-                COMPUTED_DISCHARGE_Q_MIN_DIGITS,
-                COMPUTED_DISCHARGE_Q_MAX_DIGITS);
-    }
-
-
-    /**
-     * Returns the number formatter for W values in exports of historical
-     * discharge curves.
-     *
-     * @return the number formatter for W values.
-     */
-    public static NumberFormat getHistoricalDischargeW(CallContext context) {
-        return getFormatter(
-                context,
-                HISTORICAL_DISCHARGE_W_MIN_DIGITS,
-                HISTORICAL_DISCHARGE_W_MAX_DIGITS);
-    }
-
-
-    /**
-     * Returns the number formatter for Q values in exports of historical
-     * discharge curves.
-     *
-     * @return the number formatter for Q values.
-     */
-    public static NumberFormat getHistoricalDischargeQ(CallContext context) {
-        return getFormatter(
-                context,
-                HISTORICAL_DISCHARGE_Q_MIN_DIGITS,
-                HISTORICAL_DISCHARGE_Q_MAX_DIGITS);
-    }
-
-
-    /**
-     * Returns the number formatter for W values in duration curve exports.
-     *
-     * @return the number formatter for W values.
-     */
-    public static NumberFormat getDurationW(CallContext context) {
-        return getFormatter(
-                context,
-                DURATION_W_MIN_DIGITS,
-                DURATION_W_MAX_DIGITS);
-    }
-
-
-    /**
-     * Returns the number formatter for Q values in duration curve exports.
-     *
-     * @return the number formatter for W values.
-     */
-    public static NumberFormat getDurationQ(CallContext context) {
-        return getFormatter(
-                context,
-                DURATION_Q_MIN_DIGITS,
-                DURATION_Q_MAX_DIGITS);
-    }
-
-
-    /**
-     * Returns the number formatter for D values in duration curve exports.
-     *
-     * @return the number formatter for W values.
-     */
-    public static NumberFormat getDurationD(CallContext context) {
-        return getFormatter(
-                context,
-                DURATION_D_MIN_DIGITS,
-                DURATION_D_MAX_DIGITS);
-    }
-
-    public static NumberFormat getCalculationKm(CallMeta meta) {
-        return getFormatter(
-                meta,
-                CALCULATION_REPORT_KM_MIN_DIGITS,
-                CALCULATION_REPORT_KM_MAX_DIGITS);
-    }
-
-
-    public static NumberFormat getFlowVelocityKM(CallContext context) {
-        return getFormatter(
-                context,
-                FLOW_VELOCITY_KM_MIN_DIGITS,
-                FLOW_VELOCITY_KM_MAX_DIGITS);
-    }
-
-
-    public static NumberFormat getFlowVelocityValues(CallContext context) {
-        return getFormatter(
-                context,
-                FLOW_VELOCITY_VALUES_MIN_DIGITS,
-                FLOW_VELOCITY_VALUES_MAX_DIGITS);
-    }
-
-
-    public static NumberFormat getFlowVelocityQ(CallContext context) {
-        return getFormatter(
-                context,
-                FLOW_VELOCITY_Q_MIN_DIGITS,
-                FLOW_VELOCITY_Q_MAX_DIGITS);
-    }
-
-
-    public static NumberFormat getMiddleBedHeightKM(CallContext context) {
-        return getFormatter(
-                context,
-                MIDDLE_BED_HEIGHT_KM_MIN_DIGITS,
-                MIDDLE_BED_HEIGHT_KM_MAX_DIGITS);
-    }
-
-
-    public static NumberFormat getMiddleBedHeightHeight(CallContext context) {
-        return getFormatter(
-                context,
-                MIDDLE_BED_HEIGHT_HEIGHT_MIN_DIGITS,
-                MIDDLE_BED_HEIGHT_HEIGHT_MAX_DIGITS);
-    }
-
-
-    public static NumberFormat getMiddleBedHeightUncert(CallContext context) {
-        return getFormatter(
-                context,
-                MIDDLE_BED_HEIGHT_UNCERT_MIN_DIGITS,
-                MIDDLE_BED_HEIGHT_UNCERT_MAX_DIGITS);
-    }
-
-
-    public static NumberFormat getMiddleBedHeightDataGap(CallContext context) {
-        return getFormatter(
-                context,
-                MIDDLE_BED_HEIGHT_DATAGAP_MIN_DIGITS,
-                MIDDLE_BED_HEIGHT_DATAGAP_MAX_DIGITS);
-    }
-
-
-    public static NumberFormat getMiddleBedHeightSounding(CallContext context) {
-        return getFormatter(
-                context,
-                MIDDLE_BED_HEIGHT_SOUNDING_WIDTH_MIN_DIGITS,
-                MIDDLE_BED_HEIGHT_SOUNDING_WIDTH_MAX_DIGITS);
-    }
-
-
-    public static NumberFormat getMiddleBedHeightWidth(CallContext context) {
-        return getFormatter(
-                context,
-                MIDDLE_BED_HEIGHT_WIDTH_MIN_DIGITS,
-                MIDDLE_BED_HEIGHT_WIDTH_MAX_DIGITS);
-    }
-
-    public static NumberFormat getFixDeltaWKM(CallContext context) {
-        return getFormatter(
-                context,
-                FIX_DELTA_W_KM_MIN_DIGITS,
-                FIX_DELTA_W_KM_MAX_DIGITS);
-    }
-
-    public static NumberFormat getFixDeltaWDeltaW(CallContext context) {
-        return getFormatter(
-                context,
-                FIX_DELTA_W_DELTA_W_MIN_DIGITS,
-                FIX_DELTA_W_DELTA_W_MAX_DIGITS);
-    }
-
-    public static NumberFormat getFixDeltaWQ(CallContext context) {
-        return getFormatter(
-                context,
-                FIX_DELTA_W_DELTA_Q_MIN_DIGITS,
-                FIX_DELTA_W_DELTA_Q_MAX_DIGITS);
-    }
-
-    public static NumberFormat getFixDeltaWW(CallContext context) {
-        return getFormatter(
-                context,
-                FIX_DELTA_W_DELTA_W_MIN_DIGITS,
-                FIX_DELTA_W_DELTA_W_MAX_DIGITS);
-    }
-
-    public static NumberFormat getMeterFormat(CallContext context) {
-        return getFormatter(
-                context,
-                0,
-                2);
-
-    }
-
-    public static DateFormat getDateFormatter(CallMeta m, String pattern) {
-        Locale locale = Resources.getLocale(m);
-        return new SimpleDateFormat(pattern, locale);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/utils/GeometryUtils.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,383 +0,0 @@
-package de.intevation.flys.utils;
-
-import com.vividsolutions.jts.geom.Coordinate;
-import com.vividsolutions.jts.geom.Envelope;
-import com.vividsolutions.jts.geom.Geometry;
-
-import de.intevation.flys.model.RiverAxis;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.Serializable;
-import java.net.MalformedURLException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.log4j.Logger;
-import org.geotools.data.DataStoreFactorySpi;
-import org.geotools.data.DefaultTransaction;
-import org.geotools.data.FeatureWriter;
-import org.geotools.data.Transaction;
-import org.geotools.data.shapefile.ShapefileDataStore;
-import org.geotools.data.shapefile.ShapefileDataStoreFactory;
-import org.geotools.data.simple.SimpleFeatureIterator;
-import org.geotools.feature.FeatureCollection;
-import org.geotools.feature.FeatureIterator;
-import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
-import org.geotools.geojson.feature.FeatureJSON;
-import org.geotools.geometry.jts.JTS;
-import org.geotools.geometry.jts.ReferencedEnvelope;
-import org.geotools.referencing.CRS;
-import org.hibernate.HibernateException;
-import org.opengis.feature.simple.SimpleFeature;
-import org.opengis.feature.simple.SimpleFeatureType;
-import org.opengis.referencing.FactoryException;
-import org.opengis.referencing.NoSuchAuthorityCodeException;
-import org.opengis.referencing.crs.CoordinateReferenceSystem;
-import org.opengis.referencing.operation.MathTransform;
-import org.opengis.referencing.operation.TransformException;
-
-public class GeometryUtils {
-
-    private static final Logger logger = Logger.getLogger(GeometryUtils.class);
-
-    public static final String PREFIX_EPSG  = "EPSG:";
-
-    public static final String DEFAULT_EPSG = "EPSG:31467";
-
-    private GeometryUtils() {
-    }
-
-    public static Envelope getRiverBoundary(String rivername) {
-        try {
-            List<RiverAxis> axes = RiverAxis.getRiverAxis(rivername);
-            if (axes != null && axes.size() > 0) {
-                Envelope max = null;
-
-                for (RiverAxis axis: axes) {
-                    // TODO Take the correct EPSG into account. Maybe, we need to
-                    // reproject the geometry.
-                    Envelope env = axis.getGeom().getEnvelopeInternal();
-
-                    if (max == null) {
-                        max = env;
-                    }
-                    else {
-                        max.expandToInclude(env);
-                    }
-                }
-
-                return max;
-            }
-        }
-        catch(HibernateException iae) {
-            logger.warn("No vaild river axis found for " + rivername);
-            return null;
-        }
-        logger.warn("No vaild river axis found for " + rivername);
-
-        return null;
-    }
-
-    public static String getRiverBounds(String rivername) {
-        Envelope env = getRiverBoundary(rivername);
-
-        if (env != null) {
-            return jtsBoundsToOLBounds(env);
-        }
-
-        return null;
-    }
-
-    /**
-     * Returns the boundary of Envelope <i>env</i> in OpenLayers representation.
-     *
-     * @param env The envelope of a geometry.
-     *
-     * @return the OpenLayers boundary of <i>env</i>.
-     */
-    public static String jtsBoundsToOLBounds(Envelope env) {
-        StringBuilder buf = new StringBuilder();
-        buf.append(env.getMinX()); buf.append(' ');
-        buf.append(env.getMinY()); buf.append(' ');
-        buf.append(env.getMaxX()); buf.append(' ');
-        buf.append(env.getMaxY());
-        return buf.toString();
-    }
-
-    public static String createOLBounds(Geometry a, Geometry b) {
-        Coordinate[] ca = a.getCoordinates();
-        Coordinate[] cb = b.getCoordinates();
-
-        double lowerX = Double.MAX_VALUE;
-        double lowerY = Double.MAX_VALUE;
-        double upperX = -Double.MAX_VALUE;
-        double upperY = -Double.MAX_VALUE;
-
-        for (Coordinate c: ca) {
-            lowerX = lowerX < c.x ? lowerX : c.x;
-            lowerY = lowerY < c.y ? lowerY : c.y;
-
-            upperX = upperX > c.x ? upperX : c.x;
-            upperY = upperY > c.y ? upperY : c.y;
-        }
-
-        for (Coordinate c: cb) {
-            lowerX = lowerX < c.x ? lowerX : c.x;
-            lowerY = lowerY < c.y ? lowerY : c.y;
-
-            upperX = upperX > c.x ? upperX : c.x;
-            upperY = upperY > c.y ? upperY : c.y;
-        }
-
-        return "" + lowerX + " " + lowerY + " " + upperX + " " + upperY;
-    }
-
-    public static SimpleFeatureType buildFeatureType(
-        String name, String srs, Class<?> geometryType)
-    {
-        return buildFeatureType(name, srs, geometryType, null);
-    }
-
-    /**
-     * Creates a new SimpleFeatureType using a SimpleFeatureTypeBuilder.
-     *
-     * @param name The name of the FeatureType.
-     * @param srs The SRS (e.g. "EPSG:31466").
-     * @param geometryType The geometry type's class (e.g. Polygon.class).
-     * @param attrs Optional. An object with attribute-name/attribute-class pairs
-     * where index 0 specifies the name as string and index 1 the
-     * ype as class.
-     *
-     * @return a new SimpleFeatureType.
-     */
-    public static SimpleFeatureType buildFeatureType(String name, String srs,
-        Class<?> geometryType, Object[][] attrs) {
-        try {
-            SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder();
-            CoordinateReferenceSystem crs    = CRS.decode(srs);
-
-            builder.setName("flys");
-            builder.setNamespaceURI("http://www.intevation.de/");
-            builder.setCRS(crs);
-            builder.setSRS(srs);
-
-            builder.add("geometry", geometryType, crs);
-
-            if (attrs != null) {
-                for (Object[] attr: attrs) {
-                    builder.add((String) attr[0], (Class<?>) attr[1]);
-                }
-            }
-
-            return builder.buildFeatureType();
-        }
-        catch (NoSuchAuthorityCodeException nsae) {
-        }
-        catch (FactoryException fe) {
-        }
-
-        return null;
-    }
-
-    public static List<SimpleFeature> parseGeoJSON(
-        String geojson, SimpleFeatureType ft
-    ) {
-        List<SimpleFeature> collection = new ArrayList<SimpleFeature>();
-
-        try {
-            FeatureJSON fjson = new FeatureJSON();
-            fjson.setFeatureType(ft);
-
-            FeatureIterator<SimpleFeature> iterator =
-                fjson.streamFeatureCollection(geojson);
-
-            while (iterator.hasNext()) {
-                collection.add(iterator.next());
-            }
-        }
-        catch (IOException ioe) {
-            // TODO handle exception
-        }
-
-        return collection;
-    }
-
-
-    /**
-     * This method returns the {@link CoordinateReferenceSystem} by the
-     * {@link String} <i>epsg</i>.
-     *
-     * @param epsg An EPSG code like <b>EPSG:31466</b>
-     *
-     * @return the {@link CoordinateReferenceSystem} specified by <i>epsg</i>.
-     */
-    public static CoordinateReferenceSystem getCoordinateReferenceSystem(
-        String epsg
-    ) {
-        if (epsg == null) {
-            logger.warn("cannot create CoordinateReferenceSystem with null");
-            return null;
-        }
-
-        if (!epsg.startsWith(PREFIX_EPSG)) {
-            epsg = PREFIX_EPSG + epsg;
-        }
-
-        try {
-            return CRS.decode(epsg);
-        }
-        catch (FactoryException fe) {
-            logger.error(
-                "unable to get CoordinateReferenceSystem for: " + epsg,
-                fe);
-        }
-
-        return null;
-    }
-
-
-    public static Envelope transform(Envelope orig, String targetSrs) {
-        return transform(orig, targetSrs, DEFAULT_EPSG);
-    }
-
-
-    public static Envelope transform(
-        Envelope orig,
-        String   targetSrs,
-        String   origSrs
-    ) {
-        if (targetSrs == null || orig == null || origSrs == null) {
-            logger.warn("unable to transform envelope: empty parameters");
-            return orig;
-        }
-
-        logger.debug("Transform envlope to '" + targetSrs + "'");
-        try {
-            CoordinateReferenceSystem sourceCRS =
-                getCoordinateReferenceSystem(origSrs);
-
-            CoordinateReferenceSystem targetCRS =
-                getCoordinateReferenceSystem(targetSrs);
-
-            if (sourceCRS != null && targetCRS != null) {
-                ReferencedEnvelope tmpEnv =
-                    new ReferencedEnvelope(orig, CRS.decode(origSrs));
-
-                Envelope target = tmpEnv.transform(targetCRS, false);
-
-                if (logger.isDebugEnabled()) {
-                    logger.debug("   orig envelope       : " + orig);
-                    logger.debug("   transformed envelope: " + target);
-                }
-
-                return target;
-            }
-        }
-        catch (NoSuchAuthorityCodeException nsae) {
-            logger.error("Cannot get CoordinateReferenceSystem!", nsae);
-        }
-        catch (FactoryException fe) {
-            logger.error("Cannot get CoordinateReferenceSystem!", fe);
-        }
-        catch (TransformException te) {
-            logger.error("Cannot transform envelope from source "
-                + origSrs + " to target srs " + targetSrs);
-        }
-
-        return null;
-    }
-
-
-    public static boolean writeShapefile(File shape,
-        SimpleFeatureType featureType, FeatureCollection<?, ?> collection) {
-        return writeShapefile(shape, featureType, collection,
-            featureType.getCoordinateReferenceSystem());
-    }
-
-
-    public static boolean writeShapefile(File shape,
-        SimpleFeatureType featureType, FeatureCollection<?, ?> collection,
-        CoordinateReferenceSystem crs) {
-        if (collection.isEmpty()) {
-            logger.warn("Shapefile is not written - no features given!");
-            return false;
-        }
-
-        Transaction   transaction = null;
-
-        try {
-            MathTransform transform = CRS.findMathTransform(
-                CRS.decode(DEFAULT_EPSG), crs);
-
-            Map<String, Serializable> params =
-                new HashMap<String, Serializable>();
-
-            params.put("url", shape.toURI().toURL());
-            params.put("create spatial index", Boolean.TRUE);
-
-            DataStoreFactorySpi dataStoreFactory =
-                new ShapefileDataStoreFactory();
-
-            ShapefileDataStore newDataStore =
-                (ShapefileDataStore)dataStoreFactory.createNewDataStore(params);
-            newDataStore.createSchema(featureType);
-
-            transaction = new DefaultTransaction("create");
-
-            String typeName = newDataStore.getTypeNames()[0];
-
-            FeatureWriter<SimpleFeatureType, SimpleFeature> writer =
-                newDataStore.getFeatureWriter(typeName, transaction);
-
-            SimpleFeatureIterator iterator = (SimpleFeatureIterator) collection.features();
-
-            while (iterator.hasNext()){
-                SimpleFeature feature = iterator.next();
-                SimpleFeature copy    = writer.next();
-
-                copy.setAttributes(feature.getAttributes());
-
-                Geometry orig        = (Geometry) feature.getDefaultGeometry();
-                Geometry reprojected = JTS.transform(orig, transform);
-
-                copy.setDefaultGeometry(reprojected);
-                writer.write();
-            }
-
-            transaction.commit();
-
-            return true;
-        }
-        catch (MalformedURLException mue) {
-            logger.error("Unable to prepare shapefile: " + mue.getMessage());
-        }
-        catch (IOException ioe) {
-            logger.error("Unable to write shapefile: " + ioe.getMessage());
-        }
-        catch (NoSuchAuthorityCodeException nsae) {
-            logger.error("Cannot get CoordinateReferenceSystem for '"
-                + DEFAULT_EPSG + "'");
-        }
-        catch (FactoryException fe) {
-            logger.error("Cannot get CoordinateReferenceSystem for '"
-                + DEFAULT_EPSG + "'");
-        }
-        catch (TransformException te) {
-            logger.error("Was not able to transform geometry!", te);
-        }
-        finally {
-            if (transaction != null) {
-                try {
-                    transaction.close();
-                }
-                catch (IOException ioe) { /* do nothing */ }
-            }
-        }
-
-        return false;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/utils/IdGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,18 +0,0 @@
-package de.intevation.flys.utils;
-
-public class IdGenerator {
-
-    protected int id;
-
-    public IdGenerator() {
-    }
-
-    public IdGenerator(int id) {
-        this.id = id;
-    }
-
-    public int next() {
-        return id++;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/utils/KMIndex.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,93 +0,0 @@
-package de.intevation.flys.utils;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Iterator;
-
-import java.io.Serializable;
-
-/** km to value, searchable. Tolerance is at 10cm. */
-public class KMIndex<A>
-implements   Serializable, Iterable<KMIndex.Entry<A>>
-{
-    public static final double EPSILON = 1e-4;
-
-    public static class Entry<A>
-    implements          Serializable, Comparable<Entry<A>>
-    {
-        protected double km;
-        protected A      value;
-
-        public Entry(double km) {
-            this.km = km;
-        }
-
-        public Entry(double km, A value) {
-            this.km    = km;
-            this.value = value;
-        }
-
-        public double getKm() {
-            return km;
-        }
-
-        public A getValue() {
-            return value;
-        }
-
-        public void setValue(A value) {
-            this.value = value;
-        }
-
-        @Override
-        public int compareTo(Entry<A> other) {
-            double diff = km - other.km;
-            if (diff < -EPSILON) return -1;
-            if (diff > +EPSILON) return +1;
-            return 0;
-        }
-
-        public boolean epsilonEquals(double km) {
-            return Math.abs(this.km - km) < EPSILON;
-        }
-    } // class Entry
-
-
-    protected List<Entry<A>> entries;
-
-    public KMIndex() {
-        this(10);
-    }
-
-    public KMIndex(int capacity) {
-        entries = new ArrayList<Entry<A>>(capacity);
-    }
-
-    public void add(double km, A value) {
-        entries.add(new Entry<A>(km, value));
-    }
-
-    public void sort() {
-        Collections.sort(entries);
-    }
-
-    public Entry<A> search(double km) {
-        for (Entry<A> entry: entries) {
-            if (entry.epsilonEquals(km)) {
-                return entry;
-            }
-        }
-        return null;
-    }
-
-    public Entry<A> binarySearch(double km) {
-        int index = Collections.binarySearch(entries, new Entry<A>(km));
-        return index >= 0 ? entries.get(index) : null;
-    }
-
-    public Iterator<Entry<A>> iterator() {
-        return entries.iterator();
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/utils/MapUtils.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,147 +0,0 @@
-package de.intevation.flys.utils;
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.apache.log4j.Logger;
-import org.hibernate.impl.SessionFactoryImpl;
-
-import de.intevation.flys.backend.SessionFactoryProvider;
-
-
-public class MapUtils
-{
-    private static final Logger logger = Logger.getLogger(MapUtils.class);
-
-    public static final Pattern DB_URL_PATTERN =
-        Pattern.compile("(.*)\\/\\/(.*):([0-9]+)\\/([\\.a-zA-Z0-9_-]+)");
-
-    public static final Pattern DB_PSQL_URL_PATTERN =
-        Pattern.compile("(.*)\\/\\/(.*):([0-9]+)\\/([a-zA-Z0-9_-]+)");
-
-    /**
-     * This method returns a connection string for databases used by
-     * Mapserver's Mapfile.
-     *
-     * @return A connection string for Mapserver.
-     */
-    public static String getConnection() {
-        SessionFactoryImpl sf = (SessionFactoryImpl)
-        SessionFactoryProvider.getSessionFactory();
-
-        String user = SessionFactoryProvider.getUser(sf);
-        String pass = SessionFactoryProvider.getPass(sf);
-        String url  = SessionFactoryProvider.getURL(sf);
-
-        logger.debug("Parse connection url: " + url);
-
-        Matcher m = DB_URL_PATTERN.matcher(url);
-        if (!m.matches()) {
-            logger.warn("Could not parse Connection string." +
-                "Try to parse PostgreSQL string.");
-            // maybe this is a PostgreSQL connection...
-            return getPostgreSQLConnection();
-        }
-
-        logger.debug("Groups for connection string: " + m.groupCount());
-        int groups = m.groupCount();
-
-
-        if (logger.isDebugEnabled()) {
-            for (int i = 0; i <= groups; i++) {
-                logger.debug("Group " + i + ": " + m.group(i));
-            }
-        }
-
-        String connection = null;
-
-        if (FLYSUtils.isUsingOracle()) {
-            if (groups < 4) {
-                logger.warn("Could only partially parse connection string.");
-                return null;
-            }
-
-            String host = m.group(2);
-            String port = m.group(3);
-            String backend = m.group(4);
-            connection = user + "/" + pass + "@" + host + ":" + port + "/" + backend;
-        }
-        else {
-            if (groups < 4) {
-                logger.warn("Could only partially parse connection string.");
-                return null;
-            }
-
-            String host = m.group(2);
-            String port = m.group(3);
-            String db   = m.group(4);
-
-            connection = createConnectionString(user, pass, host, db, port);
-        }
-
-        return connection;
-    }
-
-    public static String createConnectionString(
-        String user,
-        String pass,
-        String host,
-        String db,
-        String port
-    ) {
-        StringBuilder sb = new StringBuilder();
-        sb.append("dbname=").append(db);
-        sb.append(" host='").append(host).append("'");
-        sb.append(" user=").append(user);
-        sb.append(" port=").append(port);
-        // XXX: We need to escape this somehow.
-        sb.append(" password='").append(pass).append("'");
-        sb.append(" sslmode=disable");
-        return sb.toString();
-    }
-
-    protected static String getPostgreSQLConnection() {
-        SessionFactoryImpl sf = (SessionFactoryImpl)
-        SessionFactoryProvider.getSessionFactory();
-
-        String user = SessionFactoryProvider.getUser(sf);
-        String pass = SessionFactoryProvider.getPass(sf);
-        String url  = SessionFactoryProvider.getURL(sf);
-
-        Matcher m = DB_PSQL_URL_PATTERN.matcher(url);
-        if (!m.matches()) {
-            logger.warn("Could not parse PostgreSQL Connection string.");
-            return null;
-        }
-
-        int groups = m.groupCount();
-        logger.debug("Groups for PostgreSQL connection string: " + groups);
-
-        if (logger.isDebugEnabled()) {
-            for (int i = 0; i <= groups; i++) {
-                logger.debug("Group " + i + ": " + m.group(i));
-            }
-        }
-
-        String connection = null;
-
-        if (groups < 4) {
-            logger.warn("Could only partially parse connection string.");
-            return null;
-        }
-
-        String host = m.group(2);
-        String port = m.group(3);
-        String db   = m.group(4);
-
-        connection = createConnectionString(user, pass, host, db, port);
-
-        logger.debug("Created connection: '" + connection + "'");
-
-        return connection;
-    }
-
-    public static String getConnectionType() {
-        return FLYSUtils.isUsingOracle() ? "oraclespatial" : "postgis";
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/utils/MapfileGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,384 +0,0 @@
-package de.intevation.flys.utils;
-
-import de.intevation.artifacts.common.utils.Config;
-import de.intevation.flys.artifacts.model.LayerInfo;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileWriter;
-import java.io.FilenameFilter;
-import java.io.IOException;
-import java.io.Writer;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-import org.apache.velocity.Template;
-import org.apache.velocity.VelocityContext;
-import org.apache.velocity.app.VelocityEngine;
-import org.apache.velocity.runtime.RuntimeConstants;
-
-/**
- * This class iterates over a bunch of directories, searches for meta
- * information coresponding to shapefiles and creates a mapfile which is used by
- * a <i>MapServer</i>.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public abstract class MapfileGenerator
-{
-    public static final String WSPLGEN_RESULT_SHAPE   = "wsplgen.shp";
-    public static final String WSPLGEN_LINES_SHAPE    = "barrier_lines.shp";
-    public static final String WSPLGEN_POLYGONS_SHAPE = "barrier_polygons.shp";
-    public static final String WSPLGEN_USER_SHAPE     = "user-rgd.shp";
-
-    public static final String WSPLGEN_LAYER_TEMPLATE = "wsplgen_layer.vm";
-    public static final String SHP_LAYER_TEMPLATE = "shapefile_layer.vm";
-    public static final String DB_LAYER_TEMPLATE  = "db_layer.vm";
-    public static final String RIVERAXIS_LAYER_TEMPLATE = "riveraxis-layer.vm";
-
-    public static final String MS_WSPLGEN_PREFIX   = "wsplgen-";
-    public static final String MS_BARRIERS_PREFIX  = "barriers-";
-    public static final String MS_LINE_PREFIX      = "lines-";
-    public static final String MS_POLYGONS_PREFIX  = "polygons-";
-    public static final String MS_LAYER_PREFIX     = "ms_layer-";
-    public static final String MS_USERSHAPE_PREFIX = "user-";
-
-    private static Logger logger = Logger.getLogger(MapfileGenerator.class);
-
-    private File shapefileDirectory;
-
-    private VelocityEngine velocityEngine;
-
-
-    protected MapfileGenerator() {
-    }
-
-
-    /**
-     * Method to check the existance of a template file.
-     *
-     * @param templateID The name of a template.
-     * @return true, of the template exists - otherwise false.
-     */
-    public boolean templateExists(String templateID){
-        Template template = getTemplateByName(templateID);
-        return template != null;
-    }
-
-
-    public abstract void generate() throws Exception;
-
-
-    /**
-     * Returns the VelocityEngine used for the template mechanism.
-     *
-     * @return the velocity engine.
-     */
-    protected VelocityEngine getVelocityEngine() {
-        if (velocityEngine == null) {
-            velocityEngine = new VelocityEngine();
-            try {
-                setupVelocity(velocityEngine);
-            }
-            catch (Exception e) {
-                logger.error(e, e);
-                return null;
-            }
-        }
-        return velocityEngine;
-    }
-
-
-    /**
-     * Initialize velocity.
-     *
-     * @param engine Velocity engine.
-     * @throws Exception if an error occured while initializing velocity.
-     */
-    protected void setupVelocity(VelocityEngine engine)
-    throws Exception
-    {
-        engine.setProperty(
-            "input.encoding",
-            "UTF-8");
-
-        engine.setProperty(
-            RuntimeConstants.RUNTIME_LOG,
-            getVelocityLogfile());
-
-        engine.setProperty(
-            "resource.loader",
-            "file");
-
-        engine.setProperty(
-            "file.resource.loader.path",
-            getMapserverTemplatePath());
-
-        engine.init();
-    }
-
-    protected abstract String getVelocityLogfile();
-
-    protected abstract String getMapserverTemplatePath();
-
-    public abstract String getMapserverUrl();
-
-    protected VelocityContext getVelocityContext() {
-        VelocityContext context = new VelocityContext();
-
-        try {
-            context.put("MAPSERVERURL",
-                getMapserverUrl());
-            context.put("SHAPEFILEPATH",
-                getShapefileBaseDir().getCanonicalPath());
-            context.put("CONFIGDIR",
-                Config.getConfigDirectory().getCanonicalPath());
-        }
-        catch (FileNotFoundException fnfe) {
-            // this is bad
-            logger.warn(fnfe, fnfe);
-        }
-        catch (IOException ioe) {
-            // this is also bad
-            logger.warn(ioe, ioe);
-        }
-
-        return context;
-    }
-
-
-    /**
-     * Returns a template specified by <i>model</i>.
-     *
-     * @param model The name of the template.
-     * @return a template.
-     */
-    public Template getTemplateByName(String model) {
-        if (model.indexOf(".vm") < 0) {
-            model = model.concat(".vm");
-        }
-
-        try {
-            VelocityEngine engine = getVelocityEngine();
-            if (engine == null) {
-                logger.error("Error while fetching VelocityEngine.");
-                return null;
-            }
-
-            return engine.getTemplate(model);
-        }
-        catch (Exception e) {
-            logger.warn(e, e);
-        }
-
-        return null;
-    }
-
-
-    /**
-     * Returns the mapfile  template.
-     *
-     * @return the mapfile template.
-     * @throws Exception if an error occured while reading the configuration.
-     */
-    protected Template getMapfileTemplateObj()
-    throws Exception
-    {
-        String mapfileName = getMapfileTemplate();
-        return getTemplateByName(mapfileName);
-    }
-
-    protected abstract String getMapfilePath();
-
-    protected abstract String getMapfileTemplate();
-
-
-    /**
-     * Returns the base directory storing the shapefiles.
-     *
-     * @return the shapefile base directory.
-     *
-     * @throws FileNotFoundException if no shapefile path is found or
-     * configured.
-     */
-    public File getShapefileBaseDir()
-            throws FileNotFoundException, IOException
-    {
-        if (shapefileDirectory == null) {
-            String path = FLYSUtils.getXPathString(
-                FLYSUtils.XPATH_FLOODMAP_SHAPEFILE_DIR);
-
-            if (path != null) {
-                shapefileDirectory = new File(path);
-            }
-
-            if (shapefileDirectory == null) {
-                throw new FileNotFoundException("No shapefile directory given");
-            }
-
-            if (!shapefileDirectory.exists()) {
-                shapefileDirectory.mkdirs();
-            }
-        }
-
-        return shapefileDirectory;
-    }
-
-
-    protected File[] getUserDirs()
-            throws FileNotFoundException, IOException
-    {
-        File   baseDir      = getShapefileBaseDir();
-        File[] artifactDirs = baseDir.listFiles();
-
-        // TODO ONLY RETURN DIRECTORIES OF THE SPECIFIED USER
-
-        return artifactDirs;
-    }
-
-
-
-    protected List<String> parseLayers(File[] dirs) {
-        List<String> layers = new ArrayList<String>();
-
-        for (File dir: dirs) {
-            File[] layerFiles = dir.listFiles(new FilenameFilter() {
-                @Override
-                public boolean accept(File directory, String name) {
-                    return name.startsWith(MS_LAYER_PREFIX);
-                }
-            });
-
-            for (File layer: layerFiles) {
-                try {
-                    layers.add(layer.getCanonicalPath());
-                }
-                catch (IOException ioe) {
-                    logger.warn(ioe, ioe);
-                }
-            }
-        }
-
-        return layers;
-    }
-
-
-
-
-    /**
-     * Creates a layer snippet which might be included in the mapfile.
-     *
-     * @param layerinfo A LayerInfo object that contains all necessary
-     * information to build a Mapserver LAYER section.
-     * @param dir The base dir for the LAYER snippet.
-     * @param filename The name of the file that is written.
-     * @param tpl The Velocity template which is used to create the LAYER
-     * section.
-     */
-    public void writeLayer(
-        LayerInfo layerInfo,
-        File      layerFile,
-        Template  tpl
-    )
-    throws    FileNotFoundException
-    {
-        if (logger.isDebugEnabled()) {
-            logger.debug("Write layer for:");
-            logger.debug("   directory/file: " + layerFile.getName());
-        }
-
-        Writer writer = null;
-
-        try {
-            writer = new FileWriter(layerFile);
-
-            VelocityContext context = getVelocityContext();
-            context.put("LAYER", layerInfo);
-
-            tpl.merge(context, writer);
-        }
-        catch (FileNotFoundException fnfe) {
-            logger.error(fnfe, fnfe);
-        }
-        catch (IOException ioe) {
-            logger.error(ioe, ioe);
-        }
-        catch (Exception e) {
-            logger.error(e, e);
-        }
-        finally {
-            try {
-                if (writer != null) {
-                    writer.close();
-                }
-            }
-            catch (IOException ioe) {
-                logger.debug(ioe, ioe);
-            }
-        }
-    }
-
-
-    /**
-     * Creates a mapfile with the layer information stored in <i>layers</i>.
-     *
-     * @param layers Layer information.
-     */
-    public void writeMapfile(List<String> layers) {
-        String tmpMapName = "mapfile" + new Date().getTime();
-
-        File mapfile = new File(getMapfilePath());
-
-        File   tmp     = null;
-        Writer writer  = null;
-
-        try {
-            tmp = new File(mapfile.getParent(), tmpMapName);
-            tmp.createNewFile();
-
-            writer = new FileWriter(tmp);
-
-            VelocityContext context = getVelocityContext();
-            context.put("LAYERS", layers);
-
-            Template mapTemplate = getMapfileTemplateObj();
-            if (mapTemplate == null) {
-                logger.warn("No mapfile template found.");
-                return;
-            }
-
-            mapTemplate.merge(context, writer);
-
-            // we need to create a temporary mapfile first und rename it into
-            // real mapfile because we don't run into race conditions on this
-            // way. (iw)
-            tmp.renameTo(mapfile);
-        }
-        catch (FileNotFoundException fnfe) {
-            logger.error(fnfe, fnfe);
-        }
-        catch (IOException ioe) {
-            logger.error(ioe, ioe);
-        }
-        catch (Exception e) {
-            logger.error(e, e);
-        }
-        finally {
-            try {
-                if (writer != null) {
-                    writer.close();
-                }
-
-                if (tmp.exists()) {
-                    tmp.delete();
-                }
-            }
-            catch (IOException ioe) {
-                logger.debug(ioe, ioe);
-            }
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/utils/Pair.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-/*
- * Copyright (c) 2010 by Intevation GmbH
- *
- * This program is free software under the LGPL (>=v2.1)
- * Read the file LGPL.txt coming with the software for details
- * or visit http://www.gnu.org/licenses/ if it does not exist.
- */
-
-package de.intevation.flys.utils;
-
-import java.io.Serializable;
-
-/**
- * @param <A>
- * @param <B>
- * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a>
- */
-public final class Pair<A, B>
-implements         Serializable
-{
-    private A a;
-    private B b;
-
-    @SuppressWarnings("unused")
-    private Pair() {
-    }
-
-    public Pair(A a, B b) {
-        this.a = a;
-        this.b = b;
-    }
-
-    public A getA() {
-        return a;
-    }
-
-    public B getB() {
-        return b;
-    }
-
-    public void setA(A a) {
-        this.a = a;
-    }
-
-    public void setB(B b) {
-        this.b = b;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/utils/RiverMapfileGenerator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,161 +0,0 @@
-package de.intevation.flys.utils;
-
-import com.vividsolutions.jts.geom.Envelope;
-import com.vividsolutions.jts.geom.MultiLineString;
-
-import de.intevation.flys.artifacts.model.LayerInfo;
-import de.intevation.flys.artifacts.model.RiverFactory;
-
-import de.intevation.flys.model.River;
-import de.intevation.flys.model.RiverAxis;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import java.util.regex.Pattern;
-
-import org.apache.log4j.Logger;
-
-import org.apache.velocity.Template;
-import org.hibernate.HibernateException;
-
-public class RiverMapfileGenerator extends MapfileGenerator {
-
-    public static final String XPATH_RIVERMAP_RIVER_PROJECTION =
-            "/artifact-database/rivermap/river[@name=$name]/srid/@value";
-
-    public static final String XPATH_RIVERMAP_SHAPEFILE_DIR =
-            "/artifact-database/rivermap/shapefile-path/@value";
-
-    public static final String XPATH_RIVERMAP_VELOCITY_LOGFILE =
-            "/artifact-database/rivermap/velocity/logfile/@path";
-
-    public static final String XPATH_RIVERMAP_MAPSERVER_URL =
-            "/artifact-database/rivermap/mapserver/server/@path";
-
-    public static final String XPATH_RIVERMAP_MAPFILE_PATH =
-            "/artifact-database/rivermap/mapserver/mapfile/@path";
-
-    public static final String XPATH_RIVERMAP_MAPFILE_TEMPLATE =
-            "/artifact-database/rivermap/mapserver/map-template/@path";
-
-    public static final String XPATH_RIVERMAP_MAPSERVER_TEMPLATE_PATH =
-            "/artifact-database/rivermap/mapserver/templates/@path";
-
-    public static final Pattern DB_URL_PATTERN =
-        Pattern.compile("(.*)\\/\\/(.*):([0-9]+)\\/([a-zA-Z]+)");
-
-    public static final Pattern DB_PSQL_URL_PATTERN =
-        Pattern.compile("(.*)\\/\\/(.*):([0-9]+)\\/([a-zA-Z0-9]+)");
-
-    private static Logger logger = Logger.getLogger(RiverMapfileGenerator.class);
-
-    /**
-     * Generate river axis mapfile.
-     */
-    @Override
-    public void generate() {
-        logger.debug("generate()");
-
-        List<River>  rivers     = RiverFactory.getRivers();
-        List<String> riverFiles = new ArrayList<String>();
-
-        for (River river : rivers) {
-            // We expect that every river has only one RiverAxis.
-            // This is not correct but currently the case here, see
-            // RiverAxis.java.
-            List<RiverAxis> riverAxis = null;
-            try {
-                riverAxis = RiverAxis.getRiverAxis(river.getName());
-            }
-            catch (HibernateException iae) {
-                logger.error("No valid riveraxis found for " + river.getName());
-                continue;
-            }
-
-            if (riverAxis == null) {
-                logger.warn("River " + river.getName() + " has no river axis!");
-                continue;
-            }
-            if (riverAxis.get(0).getGeom() == null) {
-                logger.warn("River " + river.getName() +
-                    " has no riveraxis geometry!");
-                continue;
-            }
-            MultiLineString geom = riverAxis.get(0).getGeom();
-            Envelope extent = geom.getEnvelopeInternal();
-
-            createRiverAxisLayer(
-                    river.getName(),
-                    river.getId(),
-                    Integer.toString(geom.getSRID()),
-                    extent.getMinX() + " " +
-                    extent.getMinY() + " " +
-                    extent.getMaxX() + " " +
-                    extent.getMaxY());
-
-            riverFiles.add("river-" + river.getName() + ".map");
-        }
-        writeMapfile(riverFiles);
-    }
-
-    protected void createRiverAxisLayer(String riverName, int riverID, String srid, String extend) {
-        LayerInfo layerInfo = new LayerInfo();
-        layerInfo.setName(riverName);
-        layerInfo.setConnection(MapUtils.getConnection());
-        layerInfo.setConnectionType(MapUtils.getConnectionType());
-        layerInfo.setSrid(srid);
-        layerInfo.setExtent(extend);
-        layerInfo.setType("line");
-        // FIXME: Use templates for that
-        if (FLYSUtils.isUsingOracle()) {
-            layerInfo.setData("geom FROM river_axes USING SRID " + srid);
-        } else {
-            layerInfo.setData("geom FROM river_axes");
-        }
-        layerInfo.setFilter("river_id = " + riverID + " and kind_id = 1");
-        layerInfo.setTitle(riverName + " RiverAxis");
-
-        File layerFile = new File("river-" + riverName + ".map");
-        Template template = getTemplateByName("riveraxis-layer.vm");
-        if (template == null) {
-            logger.warn("Template riveraxis-layer.vm not found.");
-            return;
-        }
-
-        try {
-            writeLayer(layerInfo, layerFile, template);
-        }
-        catch (FileNotFoundException e) {
-            logger.warn(e.getLocalizedMessage(), e);
-        }
-    }
-
-    @Override
-    protected String getVelocityLogfile() {
-        return FLYSUtils.getXPathString(XPATH_RIVERMAP_VELOCITY_LOGFILE);
-    }
-
-    @Override
-    protected String getMapserverTemplatePath() {
-        return FLYSUtils.getXPathString(XPATH_RIVERMAP_MAPSERVER_TEMPLATE_PATH);
-    }
-
-    @Override
-    public String getMapserverUrl() {
-        return FLYSUtils.getXPathString(XPATH_RIVERMAP_MAPSERVER_URL);
-    }
-
-    @Override
-    protected String getMapfilePath() {
-        return FLYSUtils.getXPathString(XPATH_RIVERMAP_MAPFILE_PATH);
-    }
-
-    @Override
-    protected String getMapfileTemplate() {
-        return FLYSUtils.getXPathString(XPATH_RIVERMAP_MAPFILE_TEMPLATE);
-    }
-}
--- a/flys-artifacts/src/main/java/de/intevation/flys/utils/ThemeUtil.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,798 +0,0 @@
-package de.intevation.flys.utils;
-
-import de.intevation.artifacts.common.utils.XMLUtils;
-import de.intevation.flys.artifacts.model.MapserverStyle;
-import de.intevation.flys.artifacts.model.MapserverStyle.Clazz;
-import de.intevation.flys.artifacts.model.MapserverStyle.Expression;
-import de.intevation.flys.artifacts.model.MapserverStyle.Label;
-import de.intevation.flys.artifacts.model.MapserverStyle.Style;
-
-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);
-
-    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)
-    {
-        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);
-/*
-        String strNumClasses = XMLUtils.xpathString(theme, XPATH_WSPLGEN_NUMCLASSES, null);
-        int numClasses;
-        if (strNumClasses != null) {
-            numClasses = Integer.parseInt(strNumClasses);
-        }
-        else {
-            numClasses = 5;
-            logger.warn("createDynamicMapserverStyle(): strNumClasses is null");
-        }
-
-        if (numClasses < 5) {
-            numClasses = 5;
-        }
-        else if (numClasses > 20) {
-            numClasses = 20;
-        }*/
-
-
-        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;
-
-        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 = createWSPLGENExpression(from + n * step, step, n + 1, numClasses);
-
-            Clazz c = new Clazz(expr);
-            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 createWSPLGENExpression(float val, float step, int idx, int maxIdx) {
-        if (idx < maxIdx) {
-            return "[DIFF] >= " + val + " AND  [DIFF] < " + (val + step);
-        }
-        else {
-            return "[DIFF] >= " + 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 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/wsplgen/FacetCreator.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,204 +0,0 @@
-package de.intevation.flys.wsplgen;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import com.vividsolutions.jts.geom.Envelope;
-
-import de.intevation.artifactdatabase.state.Facet;
-import de.intevation.artifacts.CallContext;
-import de.intevation.flys.artifacts.access.RangeAccess;
-import de.intevation.flys.artifacts.FLYSArtifact;
-import de.intevation.flys.artifacts.model.FacetTypes;
-import de.intevation.flys.artifacts.model.map.WMSLayerFacet;
-import de.intevation.flys.artifacts.model.map.WSPLGENLayerFacet;
-import de.intevation.flys.artifacts.resources.Resources;
-import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
-import de.intevation.flys.model.CrossSectionTrack;
-import de.intevation.flys.utils.FLYSUtils;
-import de.intevation.flys.utils.GeometryUtils;
-import de.intevation.flys.utils.MapfileGenerator;
-
-
-public class FacetCreator implements FacetTypes {
-
-    public static final String I18N_WSPLGEN_RESULT    = "floodmap.uesk";
-    public static final String I18N_WSPLGEN_DEFAULT   = "floodmap.uesk";
-    public static final String I18N_BARRIERS          = "floodmap.barriers";
-    public static final String I18N_BARRIERS_DEFAULT  = "floodmap.barriers";
-    public static final String I18N_USERSHAPE         = "floodmap.usershape";
-    public static final String I18N_USERSHAPE_DEFAULT = "floodmap.usershape";
-
-    protected FLYSArtifact artifact;
-
-    protected CallContext  cc;
-
-    protected List<Facet> facets;
-    protected List<Facet> tmpFacets;
-
-    protected String url;
-    protected String hash;
-    protected String stateId;
-
-
-    private static Logger logger = Logger.getLogger(FacetCreator.class);
-
-
-    public FacetCreator(
-        FLYSArtifact artifact,
-        CallContext  cc,
-        String       hash,
-        String       sId,
-        List<Facet>  facets
-    ) {
-        this.tmpFacets  = new ArrayList<Facet>(2);
-        this.facets     = facets;
-        this.artifact   = artifact;
-        this.cc         = cc;
-        this.hash       = hash;
-        this.stateId    = sId;
-    }
-
-    // TODO We have FLYSUtils and will have RiverAccess to do this
-    protected String getRiver() {
-        return artifact.getDataAsString("river");
-    }
-
-    protected String getUrl() {
-        return FLYSUtils.getUserWMSUrl(artifact.identifier());
-    }
-
-    protected String getSrid() {
-        return FLYSUtils.getRiverSrid(artifact);
-    }
-
-    protected Envelope getWSPLGENBounds() {
-        String river = getRiver();
-        RangeAccess rangeAccess = new RangeAccess(artifact, null);
-        double kms[] = rangeAccess.getKmRange();
-
-        logger.debug("### getWSPLGENBounds");
-        logger.debug("###    from km: " + kms[0]);
-        logger.debug("###    to   km: " + kms[1]);
-
-        CrossSectionTrack a =
-            CrossSectionTrack.getCrossSectionTrack(river, kms[0]);
-
-        CrossSectionTrack b =
-            CrossSectionTrack.getCrossSectionTrack(river, kms[1]);
-
-        if (a == null || b == null) {
-            return null;
-        }
-
-        Envelope envA = a.getGeom().getEnvelopeInternal();
-        Envelope envB = b.getGeom().getEnvelopeInternal();
-
-        envA.expandToInclude(envB);
-        envA = GeometryUtils.transform(envA, getSrid());
-
-        logger.debug("###    => " + envA);
-
-        return envA;
-    }
-
-    protected Envelope getBounds() {
-        return GeometryUtils.getRiverBoundary(getRiver());
-    }
-
-    public List<Facet> getFacets() {
-        return tmpFacets;
-    }
-
-    public void createWSPLGENFacet() {
-        String river = getRiver();
-        RangeAccess rangeAccess = new RangeAccess(artifact, null);
-        double kms[] = rangeAccess.getKmRange();
-
-        WSPLGENLayerFacet wsplgen = new WSPLGENLayerFacet(
-            0,
-            FLOODMAP_WSPLGEN,
-            Resources.format(
-                cc.getMeta(),
-                I18N_WSPLGEN_RESULT,
-                I18N_WSPLGEN_DEFAULT,
-                river,
-                kms[0], kms[1]),
-            ComputeType.ADVANCE,
-            stateId,
-            hash,
-            getUrl());
-
-        Envelope bounds = getWSPLGENBounds();
-
-        if (bounds == null) {
-            bounds = getBounds();
-        }
-
-        wsplgen.addLayer(
-            MapfileGenerator.MS_WSPLGEN_PREFIX + artifact.identifier());
-        wsplgen.setSrid(getSrid());
-        wsplgen.setOriginalExtent(bounds);
-        wsplgen.setExtent(bounds);
-
-        tmpFacets.add(wsplgen);
-    }
-
-    public void createBarrierFacet() {
-        WMSLayerFacet barriers = new WMSLayerFacet(
-            1,
-            FLOODMAP_BARRIERS,
-            Resources.getMsg(
-                cc.getMeta(),
-                I18N_BARRIERS,
-                I18N_BARRIERS_DEFAULT),
-            ComputeType.ADVANCE,
-            stateId,
-            hash,
-            getUrl());
-
-        barriers.addLayer(MapfileGenerator.MS_LAYER_PREFIX +
-            MapfileGenerator.MS_BARRIERS_PREFIX + "lines" + artifact.identifier());
-        barriers.addLayer( MapfileGenerator.MS_LAYER_PREFIX +
-            MapfileGenerator.MS_BARRIERS_PREFIX + "poly" + artifact.identifier());
-
-        barriers.setSrid(getSrid());
-        barriers.setExtent(getBounds());
-
-        tmpFacets.add(barriers);
-    }
-
-
-    public void createShapeFacet(
-        String desc,
-        String layer,
-        String type,
-        int ndx) {
-        WMSLayerFacet shape = new WMSLayerFacet(
-            1,
-            type,
-            Resources.getMsg(
-                cc.getMeta(),
-                desc,
-                I18N_USERSHAPE_DEFAULT),
-            ComputeType.ADVANCE,
-            stateId,
-            hash,
-            getUrl());
-
-        shape.addLayer(
-            layer + artifact.identifier());
-        shape.setSrid(getSrid());
-        shape.setExtent(getBounds());
-
-        tmpFacets.add(shape);
-    }
-
-
-    public void finish() {
-        facets.addAll(getFacets());
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/wsplgen/JobObserver.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,106 +0,0 @@
-package de.intevation.flys.wsplgen;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.flys.artifacts.model.CalculationMessage;
-import de.intevation.flys.artifacts.model.map.WSPLGENJob;
-
-
-public class JobObserver extends Thread {
-
-    private static Logger logger = Logger.getLogger(JobObserver.class);
-
-
-    public static final String WSPLGEN_ENCODING =
-        "ISO-8859-1";
-
-    public static final String WSPLGEN_LOG_OUTPUT =
-        System.getProperty("wsplgen.log.output", "false");
-
-    public static final String[] STEPS = {
-        ".*<-Auswertung der Kommandozeilen-Parameter beendet.*",
-        ".*->Laden des DGM in Datei '.*' gestartet.*",
-        ".*->Triangulierung der Knoten gestartet.*",
-        ".*->Anpassung der Elemente an Dämme und Gräben gestartet.*",
-        ".*<-WSPLGEN Version .* beendet.*"
-    };
-
-
-    protected WSPLGENJob job;
-
-    protected InputStream in;
-
-    protected Pattern[] patterns;
-
-    protected int len;
-
-    protected boolean copy;
-
-
-    public JobObserver(WSPLGENJob job) {
-        this.job  = job;
-        this.len  = 0;
-        this.copy = Boolean.parseBoolean(WSPLGEN_LOG_OUTPUT);
-
-        patterns = new Pattern[STEPS.length];
-    }
-
-
-    protected void prepareRegexes() {
-        for (int num = STEPS.length, i = 0; i < num; i++) {
-            patterns[i] = Pattern.compile(STEPS[i], Pattern.DOTALL);
-        }
-    }
-
-
-    public void setInputStream(InputStream in) {
-        this.in = in;
-    }
-
-
-    public void run() {
-        logger.debug("Start observation...");
-        prepareRegexes();
-
-        try {
-            BufferedReader reader =
-                new BufferedReader(
-                    new InputStreamReader(in, WSPLGEN_ENCODING));
-
-            String line = null;
-
-            while ((line = reader.readLine()) != null) {
-                if (copy) {
-                    logger.debug(line);
-                }
-
-                update(line);
-            }
-        }
-        catch (IOException ioe) {
-            logger.warn("Observation canceled: " + ioe.getMessage());
-        }
-    }
-
-
-    protected void update(String log) {
-        for (int num = patterns.length, i = 0; i < num; i++) {
-            Matcher m = patterns[i].matcher(log);
-
-            if (m.matches()) {
-                job.getCallContext().addBackgroundMessage(
-                    new CalculationMessage(num, i+1, log));
-
-                logger.info("Finished step " + (i+1) + " / " + num);
-            }
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=5 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/wsplgen/ProblemObserver.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,115 +0,0 @@
-package de.intevation.flys.wsplgen;
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.flys.artifacts.model.map.WSPLGENCalculation;
-import de.intevation.flys.artifacts.model.map.WSPLGENJob;
-
-
-public class ProblemObserver extends JobObserver {
-
-    private static Logger logger = Logger.getLogger(ProblemObserver.class);
-
-
-    public static final Pattern WSPLGEN_ERROR_START = Pattern.compile(
-        ".*->(.*Fehler)(\\s*\\((\\d+)\\).*)*",
-        Pattern.DOTALL);
-
-    public static final Pattern WSPLGEN_ERROR_END = Pattern.compile(
-        ".*<-(.*Fehler).*",
-        Pattern.DOTALL);
-
-    public static final Pattern WSPLGEN_WARNING_START = Pattern.compile(
-        ".*->Warnung\\s*\\((\\d+)\\).*",
-        Pattern.DOTALL);
-
-    public static final Pattern WSPLGEN_WARNING_END = Pattern.compile(
-        ".*<-Warnung .*",
-        Pattern.DOTALL);
-
-
-    protected int error;
-    protected int warning;
-
-    protected WSPLGENCalculation calculation;
-
-
-    public ProblemObserver(WSPLGENJob job) {
-        super(job);
-        error       = -1;
-        warning     = -1;
-        calculation = job.getCalculation();
-    }
-
-
-    public void run() {
-        logger.debug("Start observation...");
-
-        super.run();
-    }
-
-
-    @Override
-    protected void prepareRegexes() {
-        // do nothing
-    }
-
-
-    @Override
-    protected void update(String log) {
-        Matcher startError = WSPLGEN_ERROR_START.matcher(log);
-        if (startError.matches()) {
-            if (startError.groupCount() >= 2) {
-                String tmp = startError.group(3);
-
-                if (tmp != null && tmp.length() > 0) {
-                    error = Integer.parseInt(tmp);
-                }
-                else error = 1;
-            }
-            else {
-                error = 1;
-            }
-
-            return;
-        }
-
-        Matcher endError = WSPLGEN_ERROR_END.matcher(log);
-        if (endError.matches()) {
-            error = -1;
-        }
-
-        if (error > 0) {
-            calculation.addError(new Integer(error), log);
-        }
-
-        Matcher startWarning = WSPLGEN_WARNING_START.matcher(log);
-        if (startWarning.matches()) {
-            warning = Integer.parseInt(startWarning.group(1));
-            return;
-        }
-
-        Matcher endWarning = WSPLGEN_WARNING_END.matcher(log);
-        if (endWarning.matches()) {
-            warning = -1;
-        }
-
-        if (warning > 0) {
-            calculation.addWarning(new Integer(warning), log);
-        }
-    }
-
-
-    public int numErrors() {
-        return calculation.numErrors();
-    }
-
-
-    public int numWarnings() {
-        return calculation.numWarnings();
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=5 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/wsplgen/Scheduler.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,111 +0,0 @@
-package de.intevation.flys.wsplgen;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.Future;
-import java.util.concurrent.ScheduledThreadPoolExecutor;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.CallContext;
-import de.intevation.flys.artifacts.model.map.WSPLGENJob;
-
-
-/**
- * The Scheduler is used to retrieve new WSPLGENJob. The incoming jobs are added
- * to a ScheduledThreadPoolExecutor. This thread pool has a number of worker
- * threads that processes the WSPLGENJobs. The number of worker threads can be
- * set using a System property <i>wsplgen.max.threads</i> ; its default value is
- * 1.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class Scheduler {
-
-    private class FutureJob {
-        public Future     future;
-        public WSPLGENJob job;
-
-        public FutureJob(Future future, WSPLGENJob job) {
-            this.future = future;
-            this.job    = job;
-        }
-    }
-
-    public static final int MAX_WSPLGEN_PROCESSES =
-        Integer.getInteger("wsplgen.max.threads", 1);
-
-
-    protected ScheduledThreadPoolExecutor pool;
-    protected Map<String, FutureJob> jobs;
-
-
-    private static Scheduler INSTANCE;
-
-    private static final Logger logger = Logger.getLogger(Scheduler.class);
-
-
-
-    private Scheduler() {
-        jobs = new HashMap<String, FutureJob>();
-        pool = new ScheduledThreadPoolExecutor(MAX_WSPLGEN_PROCESSES);
-    }
-
-
-    public static Scheduler getInstance() {
-        if (INSTANCE == null) {
-            logger.info("Create new WSPLGEN Scheduler...");
-
-            INSTANCE = new Scheduler();
-        }
-
-        return INSTANCE;
-    }
-
-
-    public void addJob(final WSPLGENJob job) {
-        synchronized (jobs) {
-            WSPLGENFuture f = new WSPLGENFuture(new WSPLGENCallable(this, job));
-            pool.execute(f);
-
-            jobs.put(job.getArtifact().identifier(), new FutureJob(f, job));
-
-            logger.info("New WSPLGEN job successfully added.");
-        }
-    }
-
-
-    /**
-     * Cancels a running (or queued) job.
-     *
-     * @param jobId The id of the job (which is the identifier of an Artifact).
-     */
-    public void cancelJob(String jobId) {
-        logger.debug("Search job in queue: " + jobId);
-
-        synchronized (jobs) {
-            FutureJob fj = jobs.get(jobId);
-
-            if (fj != null) {
-                logger.info("Try to cancel job: " + jobId);
-
-                fj.future.cancel(true);
-
-                removeJob(jobId);
-
-                fj.job.getCallContext().afterBackground(
-                    CallContext.STORE);
-
-                logger.info("Canceled job: " + jobId);
-            }
-        }
-    }
-
-
-    protected void removeJob(String id) {
-        synchronized (jobs) {
-            jobs.remove(id);
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/wsplgen/SchedulerSetup.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +0,0 @@
-package de.intevation.flys.wsplgen;
-
-import org.w3c.dom.Document;
-
-import de.intevation.artifacts.GlobalContext;
-
-import de.intevation.artifactdatabase.LifetimeListener;
-
-import de.intevation.flys.artifacts.context.FLYSContext;
-
-
-/**
- * A LifetimeListener that is used to create an instance of Scheduler. This
- * instance is put into the GlobalContext using FLYSContext.SCHEDULER.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class SchedulerSetup implements LifetimeListener {
-
-    @Override
-    public void setup(Document document) {
-    }
-
-
-    @Override
-    public void systemUp(GlobalContext globalContext) {
-        Scheduler scheduler = Scheduler.getInstance();
-        globalContext.put(FLYSContext.SCHEDULER, scheduler);
-    }
-
-
-    @Override
-    public void systemDown(GlobalContext globalContext) {
-        // TODO IMPLEMENT ME!
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/wsplgen/WSPLGENCallable.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,137 +0,0 @@
-package de.intevation.flys.wsplgen;
-
-import de.intevation.artifacts.CallContext;
-import de.intevation.flys.artifacts.model.map.WSPLGENJob;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.concurrent.Callable;
-
-import org.apache.log4j.Logger;
-
-
-/**
- * A Callable that is used to start and observe an external Process for WSPLGEN.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class WSPLGENCallable implements Callable {
-
-    public static final String WSPLGEN_PARAMETER_FILE =
-        "wsplgen.par";
-
-    public static final String WSPLGEN_BIN_PATH =
-        System.getProperty("wsplgen.bin.path");
-
-
-    private Logger logger = Logger.getLogger(WSPLGENCallable.class);
-
-    private Process process;
-
-    protected Scheduler scheduler;
-
-    protected WSPLGENJob job;
-
-    protected JobObserver     logObserver;
-    protected ProblemObserver errorObserver;
-
-
-    public WSPLGENCallable(Scheduler scheduler, WSPLGENJob job) {
-        this.scheduler     = scheduler;
-        this.job           = job;
-        this.logObserver   = new JobObserver(job);
-        this.errorObserver = new ProblemObserver(job);
-    }
-
-
-    @Override
-    public WSPLGENJob call() {
-        File dir       = job.getWorkingDir();
-        File parameter = new File(dir, WSPLGEN_PARAMETER_FILE);
-
-        String[] args = new String[] {
-            WSPLGEN_BIN_PATH,
-            "-PAR=\"" + parameter.getAbsolutePath() + "\""
-        };
-
-        execute(args, dir);
-
-        return job;
-    }
-
-
-    protected void execute(String[] args, File dir) {
-        logger.info("Start JobExecutor for artifact: " + dir.getName());
-
-        try {
-            synchronized (this) {
-                process = Runtime.getRuntime().exec(args, null, dir);
-
-                logObserver.setInputStream(process.getInputStream());
-                errorObserver.setInputStream(process.getErrorStream());
-
-                logObserver.start();
-                errorObserver.start();
-
-                try {
-                    process.waitFor();
-                }
-                catch (InterruptedException ie) {
-                    logger.warn("WSPLGEN job interrupted: " + ie.getMessage());
-                }
-
-                try {
-                    logObserver.join();
-                    errorObserver.join();
-                }
-                catch (InterruptedException iee) { /* do nothing */ }
-
-                logger.info("WSPLGEN exit value: " + process.exitValue());
-                logger.info(
-                    "WSPLGEN throw " +
-                    errorObserver.numErrors() + " errors.");
-                logger.info(
-                    "WSPLGEN throw " +
-                    errorObserver.numWarnings() + " warnings.");
-
-                if (process.exitValue() < 2 && errorObserver.numErrors() == 0) {
-                    FacetCreator fc = job.getFacetCreator();
-                    fc.createWSPLGENFacet();
-                    fc.finish();
-                }
-
-                job.getCallContext().afterBackground(CallContext.STORE);
-
-                scheduler.removeJob(getJob().getArtifact().identifier());
-
-                return;
-            }
-        }
-        catch (SecurityException se) {
-            logger.error(se);
-        }
-        catch (IOException ioe) {
-            logger.error(ioe);
-        }
-        catch (NullPointerException npe) {
-            logger.error(npe, npe);
-        }
-        catch (IndexOutOfBoundsException ioobe) {
-            logger.error(ioobe, ioobe);
-        }
-    }
-
-
-    public void cancelWSPLGEN() {
-        if (process != null) {
-            logger.debug("Cancel running WSPLGEN process.");
-            process.destroy();
-        }
-    }
-
-
-    public WSPLGENJob getJob() {
-        return job;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/wsplgen/WSPLGENFuture.java	Thu Apr 25 12:01:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +0,0 @@
-package de.intevation.flys.wsplgen;
-
-import java.util.concurrent.FutureTask;
-
-import org.apache.log4j.Logger;
-
-
-/**
- * This FutureTask overrides the <i>cancel()</i> method. Before super.cancel()
- * is called, WSPLGENCallable.cancelWSPLGEN() is executed to kill a running
- * WSPLGEN process.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class WSPLGENFuture extends FutureTask {
-
-    private static final Logger logger = Logger.getLogger(WSPLGENFuture.class);
-
-    protected WSPLGENCallable wsplgenCallable;
-
-
-    public WSPLGENFuture(WSPLGENCallable callable) {
-        super(callable);
-        this.wsplgenCallable = callable;
-    }
-
-
-    public WSPLGENCallable getWSPLGENCallable() {
-        return wsplgenCallable;
-    }
-
-
-    @Override
-    public boolean cancel(boolean mayInterruptIfRunning) {
-        logger.debug("WSPLGENFuture.cancel");
-
-        wsplgenCallable.cancelWSPLGEN();
-        return super.cancel(mayInterruptIfRunning);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/AbstractStaticStateArtifact.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,140 @@
+package org.dive4elements.river.artifacts;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.ArtifactNamespaceContext;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator;
+
+import org.dive4elements.artifactdatabase.ProtocolUtils;
+import org.dive4elements.artifactdatabase.data.StateData;
+import org.dive4elements.artifactdatabase.state.State;
+
+import org.dive4elements.river.artifacts.states.StaticState;
+
+/**
+ * A abstract baseclass for Artifacts which are using only one static state.
+ *
+ * This class is intended to be used without the config/stateengine to generate
+ * the static state.
+ *
+ * @author <a href="mailto:bjoern.ricks@intevation.de">Björn Ricks</a>
+ */
+public abstract class AbstractStaticStateArtifact extends StaticFLYSArtifact {
+
+    private transient StaticState staticstate;
+
+    private static final Logger logger =
+        Logger.getLogger(AbstractStaticStateArtifact.class);
+
+    /**
+     * 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(getStaticState());
+        return states;
+    }
+
+
+    /**
+     * Get the "current" state.
+     * @param cc ignored.
+     * @return always the set static state.
+     */
+    @Override
+    public State getCurrentState(Object cc) {
+        return getStaticState();
+    }
+
+    /**
+     * A child class must override this method to set its static state
+     */
+    protected abstract void initStaticState();
+
+    protected void setStaticState(StaticState state) {
+        this.staticstate = state;
+    }
+
+    protected StaticState getStaticState() {
+        if (staticstate == null) {
+            initStaticState();
+        }
+        return staticstate;
+    }
+
+    /**
+     * Get the state.
+     * @param context ignored.
+     * @param stateID ignored.
+     * @return the state.
+     */
+    @Override
+    protected State getState(Object context, String stateID) {
+        return getStaticState();
+    }
+
+    @Override
+    public Document describe(Document data, CallContext cc) {
+        logger.debug("Describe artifact: " + identifier());
+
+        Document desc = XMLUtils.newDocument();
+
+        ElementCreator creator = new ElementCreator(
+            desc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element root = ProtocolUtils.createRootNode(creator);
+        desc.appendChild(root);
+
+        Element name = ProtocolUtils.createArtNode(
+            creator, "name",
+            new String[] { "value" },
+            new String[] { getName() });
+
+        root.appendChild(name);
+        root.appendChild(createOutputModes(cc, desc, creator));
+
+        ProtocolUtils.appendDescribeHeader(creator, root, identifier(), hash());
+
+        // Add the data to an anonymous state.
+        Collection<StateData> datas = getAllData();
+        if (datas.size() > 0) {
+            Element ui = creator.create("ui");
+            Element staticE = creator.create("static");
+
+            StaticState current = getStaticState();
+            Element state = current.describeStatic(this, desc, root, cc, null);
+            staticE.appendChild(state);
+
+            for (StateData dataItem : datas) {
+                Element itemelent = creator.create("data");
+                creator.addAttr(itemelent, "name", dataItem.getName(), true);
+                creator.addAttr(itemelent, "type", dataItem.getType(), true);
+                state.appendChild(itemelent);
+                Element valuement = creator.create("item");
+                creator.addAttr(valuement, "label", dataItem.getDescription(), true);
+                creator.addAttr(valuement, "value", dataItem.getValue().toString(), true);
+                itemelent.appendChild(valuement);
+            }
+
+            ui.appendChild(staticE);
+            root.appendChild(ui);
+        }
+
+        return desc;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/AnnotationArtifact.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,261 @@
+package org.dive4elements.river.artifacts;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.ArtifactFactory;
+import org.dive4elements.artifacts.ArtifactNamespaceContext;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.artifactdatabase.ProtocolUtils;
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifactdatabase.state.FacetActivity;
+import org.dive4elements.artifactdatabase.state.Output;
+import org.dive4elements.artifactdatabase.state.State;
+import org.dive4elements.artifactdatabase.state.StateEngine;
+
+import org.dive4elements.river.artifacts.model.FacetTypes;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+
+import org.dive4elements.river.artifacts.states.DefaultState;
+import org.dive4elements.river.artifacts.context.FLYSContext;
+
+import org.dive4elements.river.utils.FLYSUtils;
+
+/**
+ * Artifact to access names of Points Of Interest along a segment of a river.
+ */
+public class AnnotationArtifact
+extends      StaticFLYSArtifact
+implements   FacetTypes {
+
+    /** The logger for this class. */
+    private static Logger logger = Logger.getLogger(AnnotationArtifact.class);
+
+    /** The name of the artifact. */
+    public static final String ARTIFACT_NAME = "annotation";
+
+    // Let Annotations enter life inactively if in Fix Analysis LS setting.
+    static {
+        FacetActivity.Registry.getInstance().register(
+            "annotation",
+            new FacetActivity() {
+                @Override
+                public Boolean isInitialActive(
+                    Artifact artifact,
+                    Facet    facet,
+                    String   output
+                ) {
+                    if (output.contains(FacetTypes.ChartType.FLSC.toString())) {
+                        // Longitudinal section chart
+                        String name = facet.getName();
+
+                        if (name.contains(FacetTypes.LONGITUDINAL_ANNOTATION)) {
+                            return Boolean.FALSE;
+                        }
+                    }
+
+                    return Boolean.TRUE;
+                }
+            });
+    }
+
+
+    @Override
+    public void setup(
+        String          identifier,
+        ArtifactFactory factory,
+        Object          context,
+        CallMeta        callMeta,
+        Document        data)
+    {
+        logger.debug("AnnotationArtifact.setup");
+        String filter = StaticFLYSArtifact.getDatacageIDValue(data);
+        String[] splits = filter.split(":");
+        if (splits.length > 1) {
+            addStringData("nameFilter", splits[1]);
+        }
+        super.setup(identifier, factory, context, callMeta, data);
+    }
+
+
+    /** Get river, setup Facets. */
+    @Override
+    protected void initialize(Artifact artifact, Object context,
+            CallMeta meta) {
+        logger.debug("AnnotationArtifact.initialize, id: "
+            + artifact.identifier());
+
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+        // TODO issue880: make annotations available _per type_
+        importData(flys, "river");
+
+        List<Facet> fs = new ArrayList<Facet>();
+
+        // TODO Add CallMeta (duplicate TODO in RiverAxisArtifact.java).
+        DefaultState state = (DefaultState) getCurrentState(context);
+        state.computeInit(this, hash(), context, meta, fs);
+
+        if (!fs.isEmpty()) {
+            logger.debug("Facets to add in AnnotationsArtifact.initialize .");
+            addFacets(getCurrentStateId(), fs);
+        }
+        else {
+            logger.debug("No facets to add in AnnotationsArtifact.initialize .");
+        }
+    }
+
+
+    /** Shortcut to nameFilter-data (TODO: move to respective Access). */
+    public String getFilterName() {
+        return getDataAsString("nameFilter");
+    }
+
+
+    public double[] getDistance() {
+        /** TODO In initialize(), access maximal range of river (via
+         * AnnotationFactory) instead of overriding getDistance,
+         * important for diagram generation. */
+        return new double[] {0f, 1000f};
+    }
+
+
+    /**
+     * Create the description of this AnnotationArtifact-instance.
+     *
+     * @param data Some data.
+     * @param context The CallContext.
+     *
+     * @return the description of this artifact.
+     */
+    @Override
+    public Document describe(Document data, CallContext context) {
+        logger.debug("Describe: the current state is: " + getCurrentStateId());
+
+        if (logger.isDebugEnabled()) {
+            dumpArtifact();
+        }
+
+        Document description            = XMLUtils.newDocument();
+        XMLUtils.ElementCreator creator = new XMLUtils.ElementCreator(
+            description,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element root = ProtocolUtils.createRootNode(creator);
+        description.appendChild(root);
+
+        State current = getCurrentState(context);
+
+        ProtocolUtils.appendDescribeHeader(creator, root, identifier(), hash());
+        ProtocolUtils.appendState(creator, root, current);
+
+        Element name = ProtocolUtils.createArtNode(
+            creator, "name",
+            new String[] { "value" },
+            new String[] { getName() });
+
+        Element outs = ProtocolUtils.createArtNode(
+            creator, "outputmodes", null, null);
+        appendOutputModes(description, outs, context);
+
+        root.appendChild(name);
+        root.appendChild(outs);
+
+        return description;
+    }
+
+
+    /**
+     * Returns the name of the concrete artifact.
+     *
+     * @return the name of the concrete artifact.
+     */
+    public String getName() {
+        return ARTIFACT_NAME;
+    }
+
+
+    /**
+     * Append outputmode elements to given document.
+     *
+     * @param doc Document to add outputmodes to.
+     * @param outs Element to add outputmode elements to.
+     * @param context The given CallContext (mostly for internationalization).
+     */
+    //@Override
+    protected void appendOutputModes(
+        Document    doc,
+        Element     outs,
+        CallContext context)
+    {
+        List<String> stateIds = getPreviousStateIds();
+
+        FLYSContext flysContext = FLYSUtils.getFlysContext(context);
+        StateEngine engine      = (StateEngine) flysContext.get(
+            FLYSContext.STATE_ENGINE_KEY);
+
+        for (String stateId: stateIds) {
+            logger.debug("Append output modes for state: " + stateId);
+            DefaultState state = (DefaultState) engine.getState(stateId);
+
+            List<Output> list = state.getOutputs();
+            if (list == null || list.isEmpty()) {
+                logger.debug("-> No output modes for this state.");
+                continue;
+            }
+
+            List<Facet> fs = getFacets(stateId);
+            if (fs == null || fs.isEmpty()) {
+                logger.debug("No facets found.");
+                continue;
+            }
+
+            logger.debug("Found " + fs.size() + " facets in previous states.");
+
+            List<Output> generated = generateOutputs(list, fs);
+
+            ProtocolUtils.appendOutputModes(doc, outs, generated);
+        }
+
+        try {
+            DefaultState cur = (DefaultState) getCurrentState(context);
+            if (cur.validate(this)) {
+                List<Output> list = cur.getOutputs();
+                if (list != null && list.size() > 0) {
+                    logger.debug(
+                        "Append output modes for state: " + cur.getID());
+
+                    List<Facet> fs = getFacets(cur.getID());
+                    if (fs != null && fs.size() > 0) {
+                        List<Output> generated = generateOutputs(list, fs);
+
+                        logger.debug("Found " + fs.size() + " current facets.");
+                        if (!generated.isEmpty()) {
+                            ProtocolUtils.appendOutputModes(
+                                doc, outs, generated);
+                        }
+                        else{
+                            logger.debug("Cannot append output to generated document.");
+                        }
+                    }
+                    else {
+                        logger.debug("No facets found for the current state.");
+                    }
+                }
+            }
+        }
+        catch (IllegalArgumentException iae) {
+            // state is not valid, so we do not append its outputs.
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/AreaArtifact.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,180 @@
+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.artifacts.Artifact;
+import org.dive4elements.artifacts.ArtifactFactory;
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.river.artifacts.model.AreaFacet;
+
+
+import org.dive4elements.river.artifacts.states.AreaCreationState;
+import org.dive4elements.river.artifacts.states.StaticState;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifactdatabase.state.State;
+
+
+/**
+ * Artifact describing the area between two WKms.
+ */
+public class AreaArtifact extends StaticFLYSArtifact {
+
+    /** Name of Artifact. */
+    public static final String AREA_ARTIFACT_NAME = "area_artifact";
+
+    /** Dataitem: Facet name. Facets with this name will be created (important
+     * to not have the area calculated in e.g. a CrossSection to be shown in
+     * LongitudinalSection.  */
+    protected static final String FACET_NAME = "area.facet";
+
+    /** Name of state. */
+    public static final String STATIC_STATE_NAME = "state.area_artifact";
+
+    /** data item name to access upper curve. */
+    protected static final String AREA_CURVE_OVER = "area.curve_over";
+
+    /** data item name to access lower curve. */
+    protected static final String AREA_CURVE_UNDER = "area.curve_under";
+
+    /** data item name to access whether or not paint over and under. */
+    protected static final String AREA_BETWEEN = "area.between";
+
+    /** Name of state. */
+    protected static final String AREA_NAME = "area.name";
+
+    /** Own logger. */
+    private static final Logger logger =
+        Logger.getLogger(AreaArtifact.class);
+
+
+    /** Return given name. */
+    @Override
+    public String getName() {
+        return AREA_ARTIFACT_NAME;
+    }
+
+
+    /** Store ids, create an AreaFacet. */
+    @Override
+    public void setup(
+        String          identifier,
+        ArtifactFactory factory,
+        Object          context,
+        CallMeta        callMeta,
+        Document        data)
+    {
+        logger.info("AreaArtifact.setup");
+
+        super.setup(identifier, factory, context, callMeta, data);
+
+        // TODO yet unused.
+        String ids = getDatacageIDValue(data);
+
+        // TODO this facet will be remodeled during next feed.
+        List<Facet> fs = new ArrayList<Facet>();
+        fs.add(new AreaFacet(0, "", "TODO: I am an AreaFacet"));
+
+        AreaCreationState state = (AreaCreationState) getCurrentState(context);
+
+        if (!fs.isEmpty()) {
+            addFacets(getCurrentStateId(), fs);
+        }
+    }
+
+    // TODO Data is not cached in this way.
+
+    /** Do not copy data from daddyfact. */
+    @Override
+    protected void initialize(
+        Artifact artifact,
+        Object   context,
+        CallMeta callMeta)
+    {
+        // do nothing
+    }
+
+
+    /**
+     * Get name of facets to create.
+     */
+    public String getFacetName() {
+        return getDataAsString(FACET_NAME);
+    }
+
+
+    /**
+     * Get dataprovider key for the 'lower' curve (we got that information fed
+     * from the client and store it as data).
+     */
+    public String getLowerDPKey() {
+        return getDataAsString(AREA_CURVE_UNDER);
+    }
+
+
+    /**
+     * True if the whole area between the two curves shall be filled.
+     */
+    public boolean getPaintBetween() {
+        String val = getDataAsString(AREA_BETWEEN);
+
+        return val != null && val.equals("true");
+    }
+
+
+    /**
+     * Get dataprovider key for the 'upper' curve (we got that information fed
+     * from the client and store it as data).
+     */
+    public String getUpperDPKey() {
+        return getDataAsString(AREA_CURVE_OVER);
+    }
+
+
+    /** Return data item that is used to configure name of area. */
+    public String getAreaName() {
+        return getDataAsString(AREA_NAME);
+    }
+
+
+    /**
+     * Create and return a new AreaCreationState with charting output.
+     */
+    @Override
+    public State getCurrentState(Object cc) {
+        final List<Facet> fs = getFacets(getCurrentStateId());
+
+        AreaCreationState state = new AreaCreationState();
+
+        StaticState.addDefaultChartOutput(state, "cross_section", fs);
+
+        return state;
+    }
+
+
+    /**
+     * 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(getCurrentState(context));
+
+        return states;
+    }
+
+
+    /** Trivia. */
+    protected State getState(Object context, String stateID) {
+        return getCurrentState(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/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/BedHeightsArtifact.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,137 @@
+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.artifacts.ArtifactFactory;
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+import org.dive4elements.river.artifacts.model.minfo.BedHeightFacet;
+import org.dive4elements.river.artifacts.model.minfo.BedHeightFactory;
+import org.dive4elements.river.artifacts.states.StaticState;
+
+public class BedHeightsArtifact
+extends      AbstractStaticStateArtifact
+{
+    /** The logger for this class. */
+    private static Logger logger =
+        Logger.getLogger(BedHeightsArtifact.class);
+
+    private static final String NAME = "bedheights";
+    private static final String STATIC_FACET_NAME = "bedheight";
+
+    static {
+        // TODO: Move to configuration.
+        FacetActivity.Registry.getInstance()
+            .register(NAME, FacetActivity.INACTIVE);
+    }
+
+    public static final String STATIC_STATE_NAME =
+        "state.additional_bedheights.static";
+
+    /** Data Item name to know whether we are Heighmarks and reveive
+     * some data slightly different. */
+    public static final String DATA_HEIGHT_TYPE =
+        "height_marks";
+
+    /**
+     * Trivial Constructor.
+     */
+    public BedHeightsArtifact() {
+        logger.debug("BedHeightsArtifact.BedHeightsArtifact");
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    /**
+     * Gets called from factory, to set things up.
+     */
+    @Override
+    public void setup(
+        String          identifier,
+        ArtifactFactory factory,
+        Object          context,
+        CallMeta        callMeta,
+        Document        data)
+    {
+        logger.debug("BedHeightsArtifact.setup");
+
+        if (logger.isDebugEnabled()) {
+            logger.debug(XMLUtils.toString(data));
+        }
+
+        String code = getDatacageIDValue(data);
+
+        if (code != null) {
+            String [] parts = code.split("-");
+
+            if (parts.length >= 4) {
+                String name = parts[0];
+                String type = parts[1];
+                String btype = type;
+                int hId = Integer.parseInt(parts[2]);
+                if (name.equals("bedheight")) {
+                    addStringData("type", type);
+                    addStringData("height_id", parts[2]);
+                    addStringData("time", parts[3]);
+                }
+                if (type.equals("singlevalues")) {
+                    btype = "single";
+                }
+
+                String bedHName = BedHeightFactory.getHeightName(btype, hId);
+
+                Facet facet = new BedHeightFacet(
+                        STATIC_FACET_NAME, bedHName, type);
+
+                ArrayList<Facet> facets = new ArrayList<Facet>(1);
+                facets.add(facet);
+
+                addFacets(STATIC_STATE_NAME, facets);
+            }
+        }
+        super.setup(identifier, factory, context, callMeta, data);
+    }
+
+    @Override
+    protected void initStaticState() {
+
+        logger.debug("initStaticState " + getName() + " " + identifier());
+
+        StaticState state = new StaticState(STATIC_STATE_NAME);
+        DefaultOutput output = new DefaultOutput(
+                "general",
+                "general",
+                "image/png",
+                "chart");
+
+        List<Facet> facets = getFacets(STATIC_STATE_NAME);
+        output.addFacets(facets);
+        state.addOutput(output);
+
+        setStaticState(state);
+    }
+
+    @Override
+    protected void initialize(Artifact artifact, Object context, CallMeta meta) {
+        // do not clone facets, etc. from master artifact
+
+        logger.debug("initialize");
+        importData((FLYSArtifact)artifact, "ld_from");
+        importData((FLYSArtifact)artifact, "ld_to");
+
+        logger.debug("ld_from " + getDataAsString("ld_from"));
+        logger.debug("ld_to " + getDataAsString("ld_to"));
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/ChartArtifact.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,207 @@
+package org.dive4elements.river.artifacts;
+
+import org.apache.log4j.Logger;
+
+import java.util.List;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import org.dive4elements.artifacts.Artifact;
+
+import org.dive4elements.artifacts.ArtifactFactory;
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator;
+
+import org.dive4elements.artifactdatabase.ProtocolUtils;
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifactdatabase.state.State;
+import org.dive4elements.artifactdatabase.state.StateEngine;
+import org.dive4elements.artifactdatabase.state.Output;
+
+import org.dive4elements.river.utils.FLYSUtils;
+
+import org.dive4elements.river.artifacts.states.DefaultState;
+import org.dive4elements.river.artifacts.context.FLYSContext;
+import org.dive4elements.river.artifacts.resources.Resources;
+
+
+/** Artifact, open to generate any (?) out. */
+public class ChartArtifact extends FLYSArtifact {
+
+    private static final Logger logger =
+        Logger.getLogger(ChartArtifact.class);
+
+    @Override
+    public void setup(
+        String          identifier,
+        ArtifactFactory factory,
+        Object          context,
+        CallMeta        callmeta,
+        Document        data)
+    {
+        logger.debug("ChartArtifact.setup");
+        this.identifier = identifier;
+        name = "new_chart";
+
+        List<State> states = getStates(context);
+
+        setCurrentState(states.get(0));
+    }
+
+    @Override
+    protected void appendBackgroundActivity(
+        ElementCreator cr,
+        Element        root,
+        CallContext    context
+    ) {
+        Element inBackground = cr.create("background-processing");
+        root.appendChild(inBackground);
+
+        cr.addAttr(
+            inBackground,
+            "value",
+            String.valueOf(context.isInBackground()),
+            true);
+    }
+
+
+    /**
+     * Append output mode nodes to a document.
+     */
+    @Override
+    protected void appendOutputModes(
+        Document    doc,
+        Element     outs,
+        CallContext context,
+        String      uuid)
+    {
+        List<String> stateIds = getPreviousStateIds();
+
+        FLYSContext flysContext = FLYSUtils.getFlysContext(context);
+        StateEngine engine      = (StateEngine) flysContext.get(
+            FLYSContext.STATE_ENGINE_KEY);
+
+        for (String stateId: stateIds) {
+            logger.debug("Append output modes for state: " + stateId);
+            DefaultState state = (DefaultState) engine.getState(stateId);
+
+            List<Output> list = state.getOutputs();
+            if (list == null || list.isEmpty()) {
+                logger.debug("-> No output modes for this state.");
+                continue;
+            }
+
+            List<Facet> fs = getFacets(stateId);
+
+            if (fs == null || fs.isEmpty()) {
+                logger.debug("No facets for previous state found.");
+                continue;
+            }
+
+            logger.debug("Found " + fs.size() + " facets in previous states.");
+
+            List<Output> generated = generateOutputs(list, fs);
+
+            ProtocolUtils.appendOutputModes(doc, outs, generated);
+        }
+
+        try {
+            DefaultState cur = (DefaultState) getCurrentState(context);
+            List<Output> list = cur.getOutputs();
+            if (list != null && list.size() > 0) {
+                logger.debug(
+                    "Append output modes for current state: " + cur.getID());
+
+                List<Facet> fs = getFacets(cur.getID());
+
+                if (fs != null && fs.size() > 0) {
+                    List<Output> generated = generateOutputs(list, fs);
+
+                    logger.debug("Found " + fs.size() + " current facets.");
+                    if (!generated.isEmpty()) {
+                        ProtocolUtils.appendOutputModes(
+                            doc, outs, generated);
+                    }
+                }
+                else {
+                    logger.debug("No facets found for the current state.");
+                }
+            }
+        }
+        catch (IllegalArgumentException iae) {
+            // state is not valid, so we do not append its outputs.
+        }
+    }
+
+    public static class ChartState extends DefaultState {
+
+        public static final String FIELD_MODE = "chart_type";
+
+        public static final String DURATION_CURVE =
+            "chart.new.durationcurve";
+
+        public static final String COMPUTED_DISCHARGE_CURVE =
+            "chart.new.computeddischargecurve";
+
+        public static final String DISCHARGE_LONGITUDINAL_CURVE =
+            "chart.new.longitudinal_section";
+
+        public static final String W_DIFFERENCES =
+            "chart.new.w_differences";
+
+        public static final String WATERLEVEL =
+            "chart.new.crosssection";
+
+        public static final String[] CHARTS = {
+            COMPUTED_DISCHARGE_CURVE,
+            DURATION_CURVE,
+            DISCHARGE_LONGITUDINAL_CURVE,
+            W_DIFFERENCES,
+            WATERLEVEL };
+
+
+
+        @Override
+        public Object computeAdvance(
+            FLYSArtifact artifact,
+            String       hash,
+            CallContext  context,
+            List<Facet>  facets,
+            Object       old)
+        {
+            logger.debug("ChartState.computeAdvance");
+
+
+            return null;
+        }
+
+
+        @Override
+        protected Element[] createItems(
+            XMLUtils.ElementCreator cr,
+            Artifact    artifact,
+            String      name,
+            CallContext context)
+        {
+            CallMeta meta   = context.getMeta();
+            Element[] charts = new Element[CHARTS.length];
+
+            int i = 0;
+
+            for (String chart: CHARTS) {
+                charts[i++] = createItem(
+                    cr, new String[] {
+                        Resources.getMsg(meta, chart, chart),
+                        chart
+                    });
+            }
+
+            return charts;
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/CollectionMonitor.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,104 @@
+package org.dive4elements.river.artifacts;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.xpath.XPathConstants;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.ArtifactNamespaceContext;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.Hook;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator;
+
+import org.dive4elements.artifactdatabase.state.Output;
+
+import org.dive4elements.river.artifacts.datacage.Recommendations;
+
+/** Monitors collection changes. */
+public class CollectionMonitor implements Hook {
+
+    public static final String XPATH_RESULT = "/art:result";
+
+
+    @Override
+    public void setup(Node cfg) {
+    }
+
+
+    @Override
+    public void execute(Artifact artifact, CallContext context, Document doc) {
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        Element result = (Element) XMLUtils.xpath(
+            doc,
+            XPATH_RESULT,
+            XPathConstants.NODE,
+            ArtifactNamespaceContext.INSTANCE);
+
+        ElementCreator creator = new ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element recommended = creator.create("recommended-artifacts");
+        result.appendChild(recommended);
+
+        String[] outs              = extractOutputNames(flys, context);
+        Map<String, Object> params = getNoneUserSpecificParameters(flys, context);
+
+        Recommendations rec = Recommendations.getInstance();
+
+        // TODO For newer official-lines recommendations we actually
+        // need user-id (null here).
+        rec.recommend(flys, null, outs, params, recommended);
+    }
+
+
+    /**
+     * Get outputnames from current state (only the ones for which
+     * facets exist).
+     */
+    public static String[] extractOutputNames(
+        FLYSArtifact flys,
+        CallContext  context)
+    {
+        if (flys instanceof ChartArtifact) {
+            return new String[0];
+        }
+
+        List<Output> outs = flys.getCurrentOutputs(context);
+
+        int num = outs == null ? 0 : outs.size();
+
+        String[] names = new String[num];
+
+        for (int i = 0; i < num; i++) {
+            names[i] = outs.get(i).getName();
+        }
+
+        return names;
+    }
+
+
+    /**
+     * Creates Map from Strings "recommended" to "true".
+     */
+    protected Map<String, Object> getNoneUserSpecificParameters(
+        FLYSArtifact flys,
+        CallContext  context)
+    {
+        Map<String, Object> params = new HashMap<String, Object>(1);
+        params.put("recommended", "true");
+
+        return params;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/CrossSectionArtifact.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,346 @@
+package org.dive4elements.river.artifacts;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.NavigableMap;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.ArtifactFactory;
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.river.artifacts.access.RangeAccess;
+import org.dive4elements.river.artifacts.model.CrossSectionFacet;
+import org.dive4elements.river.artifacts.model.FastCrossSectionLineFactory;
+
+import org.dive4elements.river.model.FastCrossSectionLine;
+
+import org.dive4elements.river.model.CrossSection;
+import org.dive4elements.river.model.CrossSectionLine;
+import org.dive4elements.river.artifacts.model.CrossSectionFactory;
+
+import org.dive4elements.river.artifacts.states.StaticState;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifactdatabase.state.FacetActivity;
+import org.dive4elements.artifactdatabase.state.State;
+
+import org.dive4elements.river.artifacts.services.CrossSectionKMService;
+
+
+/**
+ * Artifact describing a cross-section.
+ */
+public class CrossSectionArtifact extends StaticFLYSArtifact {
+
+    /** Name of Artifact. */
+    public static final String CS_ARTIFACT_NAME = "cross_section";
+
+    /** Name of state. */
+    public static final String STATIC_STATE_NAME = "state.cross_section";
+
+    /** Name of data item keeping the position. */
+    public static final String DATA_KM = "cross_section.km";
+
+    /** Name of data item keeping the database id of this c.s.. */
+    public static final String DATA_DBID = "cross_section.dbid";
+
+    /** Name of data item flagging whether we think that we are master. */
+    public static final String DATA_IS_MASTER = "cross_section.master?";
+
+    /** Name of data item flagging whether we are the newest. */
+    public static final String DATA_IS_NEWEST = "cross_section.newest?";
+
+    /** Name of data item storing the previous possible km. */
+    public static final String DATA_PREV_KM = "cross_section.km.previous";
+
+    /** Name of data item storing the next possible km. */
+    public static final String DATA_NEXT_KM = "cross_section.km.next";
+
+    /** Own logger. */
+    private static final Logger logger =
+        Logger.getLogger(CrossSectionArtifact.class);
+
+    static {
+        // TODO: Move to configuration.
+        FacetActivity.Registry.getInstance().register(
+            CS_ARTIFACT_NAME,
+            new FacetActivity() {
+                @Override
+                public Boolean isInitialActive(
+                    Artifact artifact,
+                    Facet    facet,
+                    String   outputName
+                ) {
+                    if (artifact instanceof FLYSArtifact) {
+                        FLYSArtifact flys = (FLYSArtifact)artifact;
+                        String data = flys.getDataAsString(DATA_IS_NEWEST);
+                        return data != null && data.equals("1");
+                    }
+                    return null;
+                }
+            });
+    }
+
+    /** Return given name. */
+    @Override
+    public String getName() {
+        return CS_ARTIFACT_NAME;
+    }
+
+
+    /** Store ids, create a CrossSectionFacet. */
+    @Override
+    public void setup(
+        String          identifier,
+        ArtifactFactory factory,
+        Object          context,
+        CallMeta        callMeta,
+        Document        data)
+    {
+        logger.info("CrossSectionArtifact.setup");
+
+        super.setup(identifier, factory, context, callMeta, data);
+
+        String ids = getDatacageIDValue(data);
+
+        if (ids != null && ids.length() > 0) {
+            addStringData(DATA_DBID, ids);
+            logger.debug("CrossSectionArtifacts db-id: " + ids);
+        }
+        else {
+            throw new IllegalArgumentException("No attribute 'ids' found!");
+        }
+
+        List<Facet> fs = new ArrayList<Facet>();
+        CrossSection cs = CrossSectionFactory.getCrossSection(
+            Integer.parseInt(ids));
+
+        List<CrossSectionLine> csls = cs.getLines();
+        if (!csls.isEmpty()) {
+            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));
+            if (dataKm < csl.getKm().doubleValue()) {
+                addStringData(DATA_KM, csl.getKm().toString());
+            }
+        }
+        fs.add(new CrossSectionFacet(0, cs.getDescription()));
+
+        // Find out if we are newest and become master if so.
+        boolean isNewest = CrossSectionFactory.isNewest(cs);
+        String newString = (isNewest) ? "1" : "0";
+        addStringData(DATA_IS_NEWEST, newString);
+        addStringData(DATA_IS_MASTER, newString);
+
+        StaticState state = (StaticState) getCurrentState(context);
+
+        if (!fs.isEmpty()) {
+            addFacets(getCurrentStateId(), fs);
+        }
+    }
+
+
+    /** Copy km where master-artifact "starts". */
+    @Override
+    protected void initialize(
+        Artifact artifact,
+        Object   context,
+        CallMeta callMeta)
+    {
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        RangeAccess rangeAccess = new RangeAccess(flys, null);
+        double[] range = rangeAccess.getKmRange();
+        double min = 0.0f;
+        if (range != null && range.length > 0) {
+            min = range[0];
+        }
+        this.addStringData(DATA_KM, Double.toString(min));
+    }
+
+
+    /** Returns next possible km for a cross-section. */
+    public Double getNextKm() {
+        return getDataAsDouble(DATA_NEXT_KM);
+    }
+
+
+    /** Returns previous possible km for a cross-section. */
+    public Double getPrevKm() {
+        return getDataAsDouble(DATA_PREV_KM);
+    }
+
+
+    /**
+     * Create and return a new StaticState with charting output.
+     */
+    @Override
+    public State getCurrentState(Object cc) {
+        final List<Facet> fs = getFacets(getCurrentStateId());
+
+        StaticState state = new StaticState(STATIC_STATE_NAME) {
+            @Override
+            public Object staticCompute(List<Facet> facets) {
+                if (facets != null) {
+                    facets.addAll(fs);
+                }
+                return null;
+            }
+        };
+
+        state.addDefaultChartOutput("cross_section", fs);
+
+        return state;
+    }
+
+
+    /**
+     * 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(getCurrentState(context));
+
+        return states;
+    }
+
+    // TODO all data access needs proper caching.
+
+    /**
+     * Get a DataItem casted to int (0 if fails).
+     */
+    public int getDataAsIntNull(String dataName) {
+        String val = getDataAsString(dataName);
+        try {
+            return Integer.parseInt(val);
+        }
+        catch (NumberFormatException e) {
+            logger.warn("Could not get data " + dataName + " as int", e);
+            return 0;
+        }
+    }
+
+
+    /** Returns database-id of cross-section (from data). */
+    protected int getDBID() {
+        return getDataAsIntNull(DATA_DBID);
+    }
+
+
+    /**
+     * Return position (km) from data, 0 if not found.
+     */
+    protected double getKm() {
+        String val = getDataAsString(DATA_KM);
+        try {
+            return Double.valueOf(val);
+        }
+        catch (NumberFormatException e) {
+            logger.warn("Could not get data " + DATA_KM + " as double", e);
+            return 0;
+        }
+    }
+
+
+    /** Returns true if artifact is set to be a "master" (other facets will
+     * refer to this). */
+    public boolean isMaster() {
+        return !getDataAsString(DATA_IS_MASTER).equals("0");
+    }
+
+
+    /**
+     * Get points of Profile of cross section at given kilometer.
+     *
+     * @return an array holding coordinates of points of profile (
+     *         in the form {{x1, x2} {y1, y2}} ).
+     */
+    public double [][] getCrossSectionData() {
+        logger.info("getCrossSectionData() for cross_section.km "
+            + getDataAsString(DATA_KM));
+        FastCrossSectionLine line = searchCrossSectionLine();
+
+        return line != null
+               ? line.fetchCrossSectionProfile()
+               : null;
+    }
+
+
+    /**
+     * Get CrossSectionLine spatially closest to what is specified in the data
+     * "cross_section.km", null if considered too far.
+     *
+     * It also adds DataItems to store the next and previous (numerically)
+     * values at which cross-section data was recorded.
+     *
+     * @return CrossSectionLine closest to "cross_section.km", might be null
+     *         if considered too far.
+     */
+    public FastCrossSectionLine searchCrossSectionLine() {
+        double TOO_FAR = 1d;
+        CrossSection crossSection = CrossSectionFactory
+            .getCrossSection(getDBID());
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("dbid " + getDBID() + " : " + crossSection);
+        }
+
+        NavigableMap<Double, Integer> kms = CrossSectionKMService
+            .getKms(crossSection.getId());
+
+        Double wishKM = getKm();
+
+        Double floor = kms.floorKey(wishKM);
+        Double ceil  = kms.ceilingKey(wishKM);
+
+        Double nextKm;
+        Double prevKm;
+
+        double floorD = floor != null
+            ? Math.abs(floor - wishKM)
+            : Double.MAX_VALUE;
+
+        double ceilD = ceil != null
+            ? Math.abs(ceil - wishKM)
+            : Double.MAX_VALUE;
+
+        double km;
+        if (floorD < ceilD) {
+            km = floor;
+        }
+        else {
+            km = ceil;
+        }
+
+        // If we are too far from the wished km, return null.
+        if (Math.abs(km - wishKM) > TOO_FAR) {
+            return null;
+        }
+
+        // Store next and previous km.
+        nextKm = kms.higherKey(km);
+        prevKm = kms.lowerKey(km);
+
+        if (prevKm == null) {
+            prevKm = -1d;
+        }
+        if (nextKm == null) {
+            nextKm = -1d;
+        }
+
+        addStringData(DATA_PREV_KM, prevKm.toString());
+        addStringData(DATA_NEXT_KM, nextKm.toString());
+
+        return FastCrossSectionLineFactory
+            .getCrossSectionLine(crossSection, km);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/ExternalWMSArtifact.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,165 @@
+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.artifacts.Artifact;
+import org.dive4elements.artifacts.ArtifactFactory;
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.artifactdatabase.state.DefaultOutput;
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifactdatabase.state.State;
+
+import org.dive4elements.river.artifacts.states.WMSBackgroundState;
+
+
+public class ExternalWMSArtifact extends StaticFLYSArtifact {
+
+    public static final String NAME = "external_wms";
+
+    private static final Logger logger =
+        Logger.getLogger(ExternalWMSArtifact.class);
+
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+
+    @Override
+    public void setup(
+        String          identifier,
+        ArtifactFactory factory,
+        Object          context,
+        CallMeta        callMeta,
+        Document        data)
+    {
+        logger.info("ExternalWMSArtifact.setup");
+
+        super.setup(identifier, factory, context, callMeta, data);
+
+        String ids = getDatacageIDValue(data);
+
+        if (ids != null && ids.length() > 0) {
+            addStringData("ids", ids);
+        }
+        else {
+            throw new IllegalArgumentException("No attribute 'ids' found!");
+        }
+
+        List<Facet> fs = new ArrayList<Facet>();
+
+        WMSBackgroundState s = (WMSBackgroundState) getCurrentState(context);
+        s.computeInit(this, hash(), context, callMeta, fs);
+
+        if (!fs.isEmpty()) {
+            addFacets(getCurrentStateId(), fs);
+        }
+    }
+
+
+    @Override
+    protected void initialize(
+        Artifact artifact,
+        Object   context,
+        CallMeta callMeta)
+    {
+        // do nothing
+    }
+
+
+    @Override
+    public State getCurrentState(Object cc) {
+        State s = new ExternalWMSState(this);
+
+        List<Facet> fs = getFacets(getCurrentStateId());
+
+        DefaultOutput o = new DefaultOutput(
+            "floodmap",
+            "floodmap",
+            "image/png",
+            fs,
+            "map");
+
+        s.getOutputs().add(o);
+
+        return s;
+    }
+
+
+    /**
+     * 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(getCurrentState(context));
+
+        return states;
+    }
+
+
+    public static class ExternalWMSState extends WMSBackgroundState {
+
+        protected ExternalWMSArtifact artifact;
+
+        protected String ids;
+
+
+        public ExternalWMSState(ExternalWMSArtifact artifact) {
+            super();
+            this.artifact = artifact;
+        }
+
+        protected String getIds() {
+            if (ids == null || ids.length() == 0) {
+                ids = artifact.getDataAsString("ids");
+            }
+
+            return ids;
+        }
+
+        @Override
+        protected String getFacetType() {
+            return FLOODMAP_EXTERNAL_WMS;
+        }
+
+        @Override
+        protected String getSrid() {
+            return "";
+        }
+
+        @Override
+        protected String getUrl() {
+            String   ids   = getIds();
+            String[] parts = ids.split(";");
+
+            return parts[0];
+        }
+
+        @Override
+        protected String getLayer() {
+            String   ids   = getIds();
+            String[] parts = ids.split(";");
+
+            return parts[1];
+        }
+
+        @Override
+        protected String getTitle(CallMeta meta) {
+            String   ids   = getIds();
+            String[] parts = ids.split(";");
+
+            return parts[2];
+        }
+    } // end of class ExternalWMSState
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/FLYSArtifact.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,1564 @@
+package org.dive4elements.river.artifacts;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+import javax.xml.xpath.XPathConstants;
+
+import net.sf.ehcache.Cache;
+
+import org.apache.log4j.Logger;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import org.dive4elements.artifactdatabase.ArtifactDatabaseImpl;
+import org.dive4elements.artifactdatabase.DefaultArtifact;
+import org.dive4elements.artifactdatabase.ProtocolUtils;
+import org.dive4elements.artifactdatabase.data.DefaultStateData;
+import org.dive4elements.artifactdatabase.data.StateData;
+import org.dive4elements.artifactdatabase.state.DefaultFacet;
+import org.dive4elements.artifactdatabase.state.DefaultOutput;
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifactdatabase.state.Output;
+import org.dive4elements.artifactdatabase.state.State;
+import org.dive4elements.artifactdatabase.state.StateEngine;
+import org.dive4elements.artifactdatabase.transition.TransitionEngine;
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.ArtifactDatabase;
+import org.dive4elements.artifacts.ArtifactDatabaseException;
+import org.dive4elements.artifacts.ArtifactFactory;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.artifacts.Message;
+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.artifacts.cache.CacheFactory;
+import org.dive4elements.river.artifacts.context.FLYSContext;
+import org.dive4elements.river.artifacts.model.CalculationMessage;
+import org.dive4elements.river.artifacts.states.DefaultState;
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+import org.dive4elements.river.utils.FLYSUtils;
+
+/**
+ * The default FLYS artifact with convenience added.
+ * (Subclass to get fully functional artifacts).
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public abstract class FLYSArtifact extends DefaultArtifact {
+
+    /** The logger that is used in this artifact. */
+    private static Logger log = Logger.getLogger(FLYSArtifact.class);
+
+    public static final String COMPUTING_CACHE = "computed.values";
+
+    /** The XPath that points to the input data elements of the FEED document. */
+    public static final String XPATH_FEED_INPUT =
+        "/art:action/art:data/art:input";
+
+    /** The XPath that points to the name of the target state of ADVANCE. */
+    public static final String XPATH_ADVANCE_TARGET =
+        "/art:action/art:target/@art:name";
+
+    public static final String XPATH_MODEL_ARTIFACT =
+        "/art:action/art:template/@uuid";
+
+    public static final String XPATH_FILTER =
+        "/art:action/art:filter/art:out";
+
+    /** The constant string that shows that an operation was successful. */
+    public static final String OPERATION_SUCCESSFUL = "SUCCESS";
+
+    /** The constant string that shows that an operation failed. */
+    public static final String OPERATION_FAILED = "FAILURE";
+
+    /** The identifier of the current state. */
+    protected String currentStateId;
+
+    /** The identifiers of previous states on a stack. */
+    protected List<String> previousStateIds;
+
+    /** The name of the artifact. */
+    protected String name;
+
+    /** The data that have been inserted into this artifact. */
+    private Map<String, StateData> data;
+
+    /** Mapping of state names to created facets. */
+    private Map<String, List<Facet>> facets;
+
+    /**
+     * Used to generates "view" on the facets (hides facets not matching the
+     * filter in output of collection);  out -&gt; facets.
+     */
+    protected Map<String, List<Facet>> filterFacets;
+
+
+    /**
+     * The default constructor that creates an empty FLYSArtifact.
+     */
+    public FLYSArtifact() {
+        data             = new TreeMap<String, StateData>();
+        previousStateIds = new ArrayList<String>();
+        facets           = new HashMap<String, List<Facet>>();
+    }
+
+
+    /**
+     * This method appends the static data - that has already been inserted by
+     * the user - to the static node of the DESCRIBE document.
+     *
+     * @param doc The document.
+     * @param ui The root node.
+     * @param context The CallContext.
+     * @param uuid The identifier of the artifact.
+     */
+    protected void appendStaticUI(
+        Document    doc,
+        Node        ui,
+        CallContext context,
+        String uuid)
+    {
+        List<String> stateIds = getPreviousStateIds();
+
+        FLYSContext flysContext = FLYSUtils.getFlysContext(context);
+        StateEngine engine      = (StateEngine) flysContext.get(
+            FLYSContext.STATE_ENGINE_KEY);
+
+        boolean debug = log.isDebugEnabled();
+
+        for (String stateId: stateIds) {
+            if (debug) {
+                log.debug("Append static data for state: " + stateId);
+            }
+            DefaultState state = (DefaultState) engine.getState(stateId);
+
+            ui.appendChild(state.describeStatic(this, doc, ui, context, uuid));
+        }
+    }
+
+
+    /**
+     * Returns the name of the concrete artifact.
+     *
+     * @return the name of the concrete artifact.
+     */
+    public String getName() {
+        return name;
+    }
+
+
+    /**
+     * Initialize the artifact and insert new data if <code>data</code> contains
+     * information necessary for this artifact.
+     *
+     * @param identifier The UUID.
+     * @param factory The factory that is used to create this artifact.
+     * @param context The CallContext.
+     * @param data Some optional data.
+     */
+    @Override
+    public void setup(
+        String          identifier,
+        ArtifactFactory factory,
+        Object          context,
+        CallMeta        callMeta,
+        Document        data)
+    {
+        boolean debug = log.isDebugEnabled();
+
+        if (debug) {
+            log.debug("Setup this artifact with the uuid: " + identifier);
+        }
+
+        super.setup(identifier, factory, context, callMeta, data);
+
+        FLYSContext flysContext = FLYSUtils.getFlysContext(context);
+
+        List<State> states = getStates(context);
+
+        String name = getName();
+
+        if (debug) {
+            log.debug("setup(): Set initial state for artifact '" + name + "'");
+        }
+
+        if (states == null) {
+            log.error("No states found from which an initial "
+                + "state could be picked.");
+        }
+        setCurrentState(states.get(0));
+
+        String model = XMLUtils.xpathString(
+            data,
+            XPATH_MODEL_ARTIFACT,
+            ArtifactNamespaceContext.INSTANCE);
+
+        if (model != null && model.length() > 0) {
+            ArtifactDatabase db = (ArtifactDatabase) flysContext.get(
+                ArtifactDatabaseImpl.GLOBAL_CONTEXT_KEY);
+
+            try {
+                initialize(db.getRawArtifact(model), context, callMeta);
+            }
+            catch (ArtifactDatabaseException adbe) {
+                log.error(adbe, adbe);
+            }
+        }
+
+        filterFacets = buildFilterFacets(data);
+    }
+
+
+    /** Get copy of previous state ids as Strings in list. */
+    protected List<String> clonePreviousStateIds() {
+        return new ArrayList<String>(previousStateIds);
+    }
+
+
+    /**
+     * Copies data item from other artifact to this artifact.
+     *
+     * @param other Artifact from which to get data.
+     * @param name  Name of data.
+     */
+    protected void importData(FLYSArtifact other, final String name) {
+        if (other == null) {
+            log.error("No other art. to import data " + name + " from.");
+            return;
+        }
+
+        StateData sd = other.getData(name);
+
+        if (sd == null) {
+            log.warn("Other artifact has no data " + name + ".");
+            return;
+        }
+
+        this.addData(name, sd);
+    }
+
+
+    /** Clone the internal map of map of state-name to state-data. */
+    protected Map<String, StateData> cloneData() {
+        Map<String, StateData> copy = new TreeMap<String, StateData>();
+
+        for (Map.Entry<String, StateData> entry: data.entrySet()) {
+            copy.put(entry.getKey(), entry.getValue().deepCopy());
+        }
+
+        return copy;
+    }
+
+
+    /**
+     * Return a copy of the facet mapping.
+     * @return Mapping of state-ids to facets.
+     */
+    protected Map<String, List<Facet>> cloneFacets() {
+        Map<String, List<Facet>> copy = new HashMap<String, List<Facet>>();
+
+        for (Map.Entry<String, List<Facet>> entry: facets.entrySet()) {
+            List<Facet> facets      = entry.getValue();
+            List<Facet> facetCopies = new ArrayList<Facet>(facets.size());
+            for (Facet facet: facets) {
+                facetCopies.add(facet.deepCopy());
+            }
+            copy.put(entry.getKey(), facetCopies);
+        }
+
+        return copy;
+    }
+
+
+    /**
+     * (called from setup).
+     * @param artifact master-artifact (if any, otherwise initialize is not called).
+     */
+    protected void initialize(
+        Artifact artifact,
+        Object   context,
+        CallMeta callMeta)
+    {
+        if (!(artifact instanceof FLYSArtifact)) {
+            return;
+        }
+
+        FLYSArtifact flys = (FLYSArtifact)artifact;
+
+        currentStateId   = flys.currentStateId;
+        previousStateIds = flys.clonePreviousStateIds();
+        name             = flys.name;
+        data             = flys.cloneData();
+        facets           = flys.cloneFacets();
+        // Do not clone filter facets!
+
+        ArrayList<String> stateIds     = (ArrayList<String>) getPreviousStateIds();
+        ArrayList<String> toInitialize = (ArrayList<String>) stateIds.clone();
+
+        toInitialize.add(getCurrentStateId());
+
+        for (String stateId: toInitialize) {
+            State state = getState(context, stateId);
+
+            if (state != null) {
+                state.initialize(artifact, this, context, callMeta);
+            }
+        }
+    }
+
+
+    /**
+     * Builds filter facets from document.
+     * @see filterFacets
+     */
+    protected Map<String, List<Facet>> buildFilterFacets(Document document) {
+        if (log.isDebugEnabled()) {
+            log.debug("Building filter factes for artifact " + this.getName());
+        }
+
+        NodeList nodes = (NodeList)XMLUtils.xpath(
+            document,
+            XPATH_FILTER,
+            XPathConstants.NODESET,
+            ArtifactNamespaceContext.INSTANCE);
+
+        if (nodes == null || nodes.getLength() == 0) {
+            return null;
+        }
+
+        Map<String, List<Facet>> result = new HashMap<String, List<Facet>>();
+
+        for (int i = 0, N = nodes.getLength(); i < N; ++i) {
+            Element element = (Element)nodes.item(i);
+            String oName = element.getAttribute("name");
+            if (oName == null || oName.isEmpty()) {
+                continue;
+            }
+
+            List<Facet> facets = new ArrayList<Facet>();
+
+            NodeList facetNodes = element.getElementsByTagNameNS(
+                ArtifactNamespaceContext.NAMESPACE_URI,
+                "facet");
+
+            for (int j = 0, M = facetNodes.getLength(); j < M; ++j) {
+                Element facetElement = (Element)facetNodes.item(j);
+
+                String fName = facetElement.getAttribute("name");
+
+                int index;
+                try {
+                    index = Integer.parseInt(facetElement.getAttribute("index"));
+                }
+                catch (NumberFormatException nfe) {
+                    log.warn(nfe);
+                    index = 0;
+                }
+                if (log.isDebugEnabled()) {
+                    log.debug("Creating filter facet " + fName + " with  index " + index +
+                            " for out " + oName);
+                }
+                facets.add(new DefaultFacet(index, fName, ""));
+            }
+
+            if (!facets.isEmpty()) {
+                result.put(oName, facets);
+            }
+        }
+
+        return result;
+    }
+
+
+    /**
+     * Insert new data included in <code>input</code> into the current state.
+     *
+     * @param target XML document that contains new data.
+     * @param context The CallContext.
+     *
+     * @return a document that contains a SUCCESS or FAILURE message.
+     */
+    @Override
+    public Document feed(Document target, CallContext context) {
+        log.debug("FLYSArtifact.feed()");
+
+        Document doc = XMLUtils.newDocument();
+
+        XMLUtils.ElementCreator creator = new XMLUtils.ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element result = creator.create("result");
+        doc.appendChild(result);
+
+        try {
+            saveData(target, context);
+
+            compute(context, ComputeType.FEED, true);
+
+            return describe(target, context);
+        }
+        catch (IllegalArgumentException iae) {
+            // do not store state if validation fails.
+            context.afterCall(CallContext.NOTHING);
+            creator.addAttr(result, "type", OPERATION_FAILED, true);
+
+            result.setTextContent(iae.getMessage());
+        }
+
+        return doc;
+    }
+
+
+    /**
+     * This method returns a description of this artifact.
+     *
+     * @param data Some data.
+     * @param context The CallContext.
+     *
+     * @return the description of this artifact.
+     */
+    @Override
+    public Document describe(Document data, CallContext context) {
+
+        if (log.isDebugEnabled()) {
+            log.debug(
+                "Describe: the current state is: " + getCurrentStateId());
+            dumpArtifact();
+        }
+
+        FLYSContext flysContext = FLYSUtils.getFlysContext(context);
+
+        StateEngine stateEngine = (StateEngine) flysContext.get(
+            FLYSContext.STATE_ENGINE_KEY);
+
+        TransitionEngine transitionEngine = (TransitionEngine) flysContext.get(
+            FLYSContext.TRANSITION_ENGINE_KEY);
+
+        List<State> reachable = transitionEngine.getReachableStates(
+            this, getCurrentState(context), stateEngine);
+
+        Document description            = XMLUtils.newDocument();
+        XMLUtils.ElementCreator creator = new XMLUtils.ElementCreator(
+            description,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element root = ProtocolUtils.createRootNode(creator);
+        description.appendChild(root);
+
+        State current = getCurrentState(context);
+
+        ProtocolUtils.appendDescribeHeader(creator, root, identifier(), hash());
+        ProtocolUtils.appendState(creator, root, current);
+        ProtocolUtils.appendReachableStates(creator, root, reachable);
+
+        appendBackgroundActivity(creator, root, context);
+
+        Element ui = ProtocolUtils.createArtNode(
+            creator, "ui", null, null);
+
+        Element staticUI  = ProtocolUtils.createArtNode(
+            creator, "static", null, null);
+
+        Element outs = ProtocolUtils.createArtNode(
+            creator, "outputmodes", null, null);
+        appendOutputModes(description, outs, context, identifier());
+
+        appendStaticUI(description, staticUI, context, identifier());
+
+        Element name = ProtocolUtils.createArtNode(
+            creator, "name",
+            new String[] { "value" },
+            new String[] { getName() });
+
+        Element dynamic = current.describe(
+            this,
+            description,
+            root,
+            context,
+            identifier());
+
+        if (dynamic != null) {
+            ui.appendChild(dynamic);
+        }
+
+        ui.appendChild(staticUI);
+
+        root.appendChild(name);
+        root.appendChild(ui);
+        root.appendChild(outs);
+
+        return description;
+    }
+
+    /** Override me! */
+
+    protected void appendBackgroundActivity(
+        ElementCreator cr,
+        Element        root,
+        CallContext    context
+    ) {
+        LinkedList<Message> messages = context.getBackgroundMessages();
+
+        if (messages == null) {
+            return;
+        }
+
+        Element inBackground = cr.create("background-processing");
+        root.appendChild(inBackground);
+
+        cr.addAttr(
+            inBackground,
+            "value",
+            String.valueOf(context.isInBackground()),
+            true);
+
+        CalculationMessage message = (CalculationMessage) messages.getLast();
+        cr.addAttr(
+            inBackground,
+            "steps",
+            String.valueOf(message.getSteps()),
+            true);
+
+        cr.addAttr(
+            inBackground,
+            "currentStep",
+            String.valueOf(message.getCurrentStep()),
+            true);
+
+        inBackground.setTextContent(message.getMessage());
+    }
+
+    /**
+     * Append output mode nodes to a document.
+     */
+    protected void appendOutputModes(
+        Document    doc,
+        Element     outs,
+        CallContext context,
+        String      uuid)
+    {
+        List<Output> generated = getOutputs(context);
+
+        if (log.isDebugEnabled()) {
+            log.debug("This Artifact has " + generated.size() + " Outputs.");
+        }
+
+        ProtocolUtils.appendOutputModes(doc, outs, generated);
+    }
+
+
+    /**
+     * This method handles request for changing the current state of an
+     * artifact. It is possible to step forward or backward.
+     *
+     * @param target The incoming ADVANCE document.
+     * @param context The CallContext.
+     *
+     * @return a document that contains a SUCCESS or FAILURE message.
+     */
+    @Override
+    public Document advance(Document target, CallContext context) {
+
+        boolean debug = log.isDebugEnabled();
+
+        Document doc = XMLUtils.newDocument();
+
+        XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element result = ec.create("result");
+
+        String currentStateId = getCurrentStateId();
+        String targetState    = XMLUtils.xpathString(
+            target, XPATH_ADVANCE_TARGET, ArtifactNamespaceContext.INSTANCE);
+
+        if (debug) {
+            log.debug("FLYSArtifact.advance() to '" + targetState + "'");
+        }
+
+        if (!currentStateId.equals(targetState)
+            && isStateReachable(targetState, context))
+        {
+            if (debug) {
+                log.debug("Advance: Step forward");
+            }
+
+            List<String> prev = getPreviousStateIds();
+            prev.add(currentStateId);
+
+            setCurrentStateId(targetState);
+
+            if (debug) {
+                log.debug("Compute data for state: " + targetState);
+            }
+            compute(context, ComputeType.ADVANCE, true);
+
+            return describe(target, context);
+        }
+        else if (isPreviousState(targetState, context)) {
+            if (debug) {
+                log.debug("Advance: Step back to");
+            }
+
+            List<String> prevs   = getPreviousStateIds();
+            int targetIdx        = prevs.indexOf(targetState);
+            int start            = prevs.size() - 1;
+
+            destroyStates(prevs, context);
+
+            for (int i = start; i >= targetIdx; i--) {
+                String prev = prevs.get(i);
+                if (debug) {
+                    log.debug("Remove state id '" + prev + "'");
+                }
+
+                prevs.remove(prev);
+                facets.remove(prev);
+            }
+
+            destroyState(getCurrentStateId(), context);
+            setCurrentStateId(targetState);
+
+            return describe(target, context);
+        }
+
+        log.warn("Advance: Cannot advance to '" + targetState + "'");
+        ec.addAttr(result, "type", OPERATION_FAILED, true);
+
+        doc.appendChild(result);
+
+        return doc;
+    }
+
+
+    /**
+     * Returns the identifier of the current state.
+     *
+     * @return the identifier of the current state.
+     */
+    public String getCurrentStateId() {
+        return currentStateId;
+    }
+
+
+    /**
+     * Sets the identifier of the current state.
+     *
+     * @param id the identifier of a state.
+     */
+    protected void setCurrentStateId(String id) {
+        currentStateId = id;
+    }
+
+
+    /**
+     * Set the current state of this artifact. <b>NOTE</b>We don't store the
+     * State object itself - which is not necessary - but its identifier. So
+     * this method will just call the setCurrentStateId() method with the
+     * identifier of <i>state</i>.
+     *
+     * @param state The new current state.
+     */
+    protected void setCurrentState(State state) {
+        setCurrentStateId(state.getID());
+    }
+
+
+    /**
+     * Returns the current state of the artifact.
+     *
+     * @return the current State of the artifact.
+     */
+    public State getCurrentState(Object context) {
+        return getState(context, getCurrentStateId());
+    }
+
+
+    /**
+     * Get list of existant states for this Artifact.
+     * @param context Contex to get StateEngine from.
+     * @return list of states.
+     */
+    protected List<State> getStates(Object context) {
+        FLYSContext flysContext = FLYSUtils.getFlysContext(context);
+        StateEngine engine      = (StateEngine) flysContext.get(
+            FLYSContext.STATE_ENGINE_KEY);
+        return engine.getStates(getName());
+    }
+
+
+    /**
+     * Get state with given ID.
+     * @param context Context to get StateEngine from.
+     * @param stateID ID of state to get.
+     * @return state with given ID.
+     */
+    protected State getState(Object context, String stateID) {
+        FLYSContext flysContext = FLYSUtils.getFlysContext(context);
+        StateEngine engine      = (StateEngine) flysContext.get(
+            FLYSContext.STATE_ENGINE_KEY);
+        return engine.getState(stateID);
+    }
+
+
+    /**
+     * Returns the vector of previous state identifiers.
+     *
+     * @return the vector of previous state identifiers.
+     */
+    protected List<String> getPreviousStateIds() {
+        return previousStateIds;
+    }
+
+
+    /**
+     * Get all previous and the current state id.
+     * @return #getPreviousStateIds() + #getCurrentStateId()
+     */
+    public List<String> getStateHistoryIds() {
+        ArrayList<String> prevIds = (ArrayList) getPreviousStateIds();
+        ArrayList<String> allIds  = (ArrayList) prevIds.clone();
+
+        allIds.add(getCurrentStateId());
+        return allIds;
+    }
+
+
+    /**
+     * Adds a new StateData item to the data pool of this artifact.
+     *
+     * @param name the name of the data object.
+     * @param data the data object itself.
+     */
+    protected void addData(String name, StateData data) {
+        this.data.put(name, data);
+    }
+
+
+    /** Remove and return statedata associated to name. */
+    protected StateData removeData(String name) {
+        return this.data.remove(name);
+    }
+
+
+    /**
+     * This method returns a specific StateData object that is stored in the
+     * data pool of this artifact.
+     *
+     * @param name The name of the data object.
+     *
+     * @return the StateData object if existing, otherwise null.
+     */
+    public StateData getData(String name) {
+        return data.get(name);
+    }
+
+
+    /**
+     * A derived Artifact class can use this method to set the data
+     */
+    protected void setData(Map<String, StateData> data) {
+        this.data = data;
+    }
+
+
+    /** Return named data item, null if not found. */
+    public String getDataAsString(String name) {
+        StateData data = getData(name);
+        return data != null ? (String) data.getValue() : null;
+    }
+
+
+    /**
+     * This method returns the value of a StateData object stored in the data
+     * pool of this Artifact as Integer.
+     *
+     * @param name The name of the StateData object.
+     *
+     * @return an Integer representing the value of the data object or null if
+     * no object was found for <i>name</i>.
+     *
+     * @throws NumberFormatException if the value of the data object could not
+     * be transformed into an Integer.
+     */
+    public Integer getDataAsInteger(String name)
+    throws NumberFormatException
+    {
+        String value = getDataAsString(name);
+
+        if (value != null && value.length() > 0) {
+            return Integer.parseInt(value);
+        }
+
+        return null;
+    }
+
+
+    /**
+     * This method returns the value of a StateData object stored in the data
+     * pool of this Artifact as Double.
+     *
+     * @param name The name of the StateData object.
+     *
+     * @return an Double representing the value of the data object or null if
+     * no object was found for <i>name</i>.
+     *
+     * @throws NumberFormatException if the value of the data object could not
+     * be transformed into a Double.
+     */
+    public Double getDataAsDouble(String name)
+    throws NumberFormatException
+    {
+        String value = getDataAsString(name);
+
+        if (value != null && value.length() > 0) {
+            return Double.parseDouble(value);
+        }
+
+        return null;
+    }
+
+
+    /**
+     * This method returns the value of a StateData object stored in the data
+     * pool of this Artifact as Long.
+     *
+     * @param name The name of the StateData object.
+     *
+     * @return a Long representing the value of the data object or null if
+     * no object was found for <i>name</i>.
+     *
+     * @throws NumberFormatException if the value of the data object could not
+     * be transformed into a Long.
+     */
+    public Long getDataAsLong(String name)
+    throws NumberFormatException
+    {
+        String value = getDataAsString(name);
+
+        if (value != null && value.length() > 0) {
+            return Long.parseLong(value);
+        }
+
+        return null;
+    }
+
+
+    /**
+     * This method returns the value of a StateData object stored in the data
+     * pool of this Artifact is Boolean using Boolean.valueOf().
+     *
+     * @param name The name of the StateData object.
+     *
+     * @return a Boolean representing the value of the data object or null if no
+     * such object is existing.
+     */
+    public Boolean getDataAsBoolean(String name) {
+        String value = getDataAsString(name);
+
+        if (value == null || value.length() == 0) {
+            return null;
+        }
+
+        return Boolean.valueOf(value);
+    }
+
+
+    /**
+     * Add StateData containing a given string.
+     * @param name Name of the data object.
+     * @param value String to store.
+     */
+    public void addStringData(String name, String value) {
+        addData(name, new DefaultStateData(name, null, null, value));
+    }
+
+    /**
+     * This method returns all stored StateData in this artifact as a Collection
+     * @return a Collection of all StateData objects in this artifact
+     */
+    public Collection<StateData> getAllData() {
+        return data.values();
+    }
+
+
+    /** Return all produced facets. */
+    public List<Facet> getFacets() {
+        List<Facet> all = new ArrayList<Facet>();
+
+        // Iterate over facets of each state.
+        for (List<Facet> fs: facets.values()) {
+            all.addAll(fs);
+        }
+
+        return all;
+    }
+
+
+    /**
+     * Get facet as stored internally, with equalling name and index than given
+     * facet.
+     * @param facet that defines index and name of facet searched.
+     * @return facet instance or null if not found.
+     */
+    public Facet getNativeFacet(Facet facet) {
+        String name  = facet.getName();
+        int    index = facet.getIndex();
+
+        for (List<Facet> fs: facets.values()) {
+            for (Facet f: fs) {
+                if (f.getIndex() == index && f.getName().equals(name)) {
+                    return f;
+                }
+            }
+        }
+
+        log.warn("Could not find facet: " + name + " at " + index);
+        log.warn("Available facets for : " + getName() + " " + identifier() +
+                ": " + facets.values());
+        return null;
+    }
+
+
+    /**
+     * This method stores the data that is contained in the FEED document.
+     *
+     * @param feed The FEED document.
+     * @param xpath The XPath that points to the data nodes.
+     */
+    public void saveData(Document feed, CallContext context)
+    throws IllegalArgumentException
+    {
+        if (feed == null) {
+            throw new IllegalArgumentException("error_feed_no_data");
+        }
+
+        NodeList nodes = (NodeList) XMLUtils.xpath(
+            feed,
+            XPATH_FEED_INPUT,
+            XPathConstants.NODESET,
+            ArtifactNamespaceContext.INSTANCE);
+
+        if (nodes == null || nodes.getLength() == 0) {
+            throw new IllegalArgumentException("error_feed_no_data");
+        }
+
+        boolean debug = log.isDebugEnabled();
+
+        int count = nodes.getLength();
+
+        if (debug) {
+            log.debug("Try to save " + count + " data items.");
+        }
+
+        String uri = ArtifactNamespaceContext.NAMESPACE_URI;
+
+        DefaultState current = (DefaultState) getCurrentState(context);
+
+        FLYSContext flysContext = FLYSUtils.getFlysContext(context);
+        StateEngine engine      = (StateEngine) flysContext.get(
+            FLYSContext.STATE_ENGINE_KEY);
+
+        for (int i = 0; i < count; i++) {
+            Element node = (Element)nodes.item(i);
+
+            String name  = node.getAttributeNS(uri, "name");
+            String value = node.getAttributeNS(uri, "value");
+
+            if (name.length() > 0 && value.length() > 0) {
+                if (debug) {
+                    log.debug("Save data item for '" + name + "' : " + value);
+                }
+
+                StateData model = engine.getStateData(getName(), name);
+
+                StateData sd = model != null
+                    ? model.deepCopy()
+                    : new DefaultStateData(name, null, null, value);
+
+                addData(
+                    name, current.transform(this, context, sd, name, value));
+            }
+            else if (name.length() > 0 && value.length() == 0) {
+                if (removeData(name) != null && debug) {
+                    log.debug("Removed data '" + name + "' successfully.");
+                }
+            }
+        }
+
+        current.validate(this);
+    }
+
+
+    /**
+     * Determines if the state with the identifier <i>stateId</i> is reachable
+     * from the current state. The determination itself takes place in the
+     * TransitionEngine.
+     *
+     * @param stateId The identifier of a state.
+     * @param context The context object.
+     *
+     * @return true, if the state specified by <i>stateId</i> is reacahble,
+     * otherwise false.
+     */
+    protected boolean isStateReachable(String stateId, Object context) {
+
+        if (log.isDebugEnabled()) {
+            log.debug("Determine if the state '" + stateId + "' is reachable.");
+        }
+
+        FLYSContext flysContext = FLYSUtils.getFlysContext(context);
+
+        State currentState  = getCurrentState(context);
+        StateEngine sEngine = (StateEngine) flysContext.get(
+            FLYSContext.STATE_ENGINE_KEY);
+
+        TransitionEngine tEngine = (TransitionEngine) flysContext.get(
+            FLYSContext.TRANSITION_ENGINE_KEY);
+
+        return tEngine.isStateReachable(this, stateId, currentState, sEngine);
+    }
+
+
+    /**
+     * Determines if the state with the identifier <i>stateId</i> is a previous
+     * state of the current state.
+     *
+     * @param stateId The target state identifier.
+     * @param context The context object.
+     */
+    protected boolean isPreviousState(String stateId, Object context) {
+        if (log.isDebugEnabled()) {
+            log.debug("Determine if the state '" + stateId + "' is old.");
+        }
+
+        return getPreviousStateIds().contains(stateId);
+    }
+
+
+    /**
+     * Computes the hash code of the entered values.
+     *
+     * @return a hash code.
+     */
+    @Override
+    public String hash() {
+
+        long hash  = 0L;
+        int  shift = 3;
+
+        for (Map.Entry<String, StateData> entry: data.entrySet()) {
+            String key   = entry.getKey();
+            Object value = entry.getValue().getValue();
+
+            hash ^= ((long)key.hashCode() << shift)
+                 |  ((long)value.hashCode() << (shift + 3));
+            shift += 2;
+        }
+
+        return getCurrentStateId() + hash;
+    }
+
+
+    /**
+     * Return List of outputs, where combinations of outputname and filtername
+     * that match content in filterFacets is left out.
+     * @return filtered Outputlist.
+     */
+    protected List<Output> filterOutputs(List<Output> outs) {
+        if (filterFacets == null || filterFacets.isEmpty()) {
+            log.debug("No filter for Outputs.");
+            return outs;
+        }
+
+        boolean debug = log.isDebugEnabled();
+
+        if (debug) {
+            log.debug(
+                "Filter Facets with " + filterFacets.size() + " filters.");
+        }
+
+        List<Output> filtered = new ArrayList<Output>();
+
+        for (Output out: outs) {
+            String outName = out.getName();
+
+            if (debug) {
+                log.debug("  filter Facets for Output: " + outName);
+            }
+
+            List<Facet> fFacets = filterFacets.get(outName);
+            if (fFacets != null) {
+                if (debug) {
+                    log.debug("" + fFacets.size() + " filters for: " + outName);
+                    for (Facet tmp: fFacets) {
+                        log.debug("   filter = '" + tmp.getName() + "'");
+                    }
+                }
+
+                List<Facet> resultFacets = new ArrayList<Facet>();
+
+                for (Facet facet: out.getFacets()) {
+                    for (Facet fFacet: fFacets) {
+                        if (facet.getIndex() == fFacet.getIndex()
+                        &&  facet.getName().equals(fFacet.getName())) {
+                            resultFacets.add(facet);
+                            break;
+                        }
+                    }
+                }
+
+                if (debug) {
+                    log.debug(
+                        "Facets after filtering = " + resultFacets.size());
+                }
+
+                if (!resultFacets.isEmpty()) {
+                    DefaultOutput nout = new DefaultOutput(
+                        out.getName(),
+                        out.getDescription(),
+                        out.getMimeType(),
+                        resultFacets);
+                    filtered.add(nout);
+                }
+            }
+            else if (debug) {
+                log.debug("No filter Factes for Output: " + outName);
+            }
+        }
+
+        if (debug) {
+            log.debug("Number of outs after filtering = " + filtered.size());
+        }
+
+        return filtered;
+    }
+
+
+    /**
+     * Get all outputs that the Artifact can do in this state (which includes
+     * all previous states).
+     *
+     * @return list of outputs
+     */
+    public List<Output> getOutputs(Object context) {
+        if (log.isDebugEnabled()) {
+            log.debug("##### Get Outputs for: " + identifier() + " #####");
+            dumpArtifact();
+        }
+
+        List<String> stateIds  = getPreviousStateIds();
+        List<Output> generated = new ArrayList<Output>();
+
+        for (String stateId: stateIds) {
+            DefaultState state = (DefaultState) getState(context, stateId);
+            generated.addAll(getOutputForState(state));
+        }
+
+        generated.addAll(getCurrentOutputs(context));
+
+        return filterOutputs(generated);
+    }
+
+
+    /**
+     * Get output(s) for current state.
+     * @return list of outputs for current state.
+     */
+    public List<Output> getCurrentOutputs(Object context) {
+        DefaultState cur = (DefaultState) getCurrentState(context);
+
+        try {
+            if (cur.validate(this)) {
+                return getOutputForState(cur);
+            }
+        }
+        catch (IllegalArgumentException iae) { }
+
+        return new ArrayList<Output>();
+    }
+
+
+    /**
+     * Get output(s) for a specific state.
+     * @param state State of interest
+     * @return list of output(s) for given state.
+     */
+    protected List<Output> getOutputForState(DefaultState state) {
+
+        if (state == null) {
+            log.error("state == null: This should not happen!");
+            return new ArrayList<Output>();
+        }
+
+        boolean debug = log.isDebugEnabled();
+
+        if (debug) {
+            log.debug("Find Outputs for State: " + state.getID());
+        }
+
+        List<Output> list = state.getOutputs();
+        if (list == null || list.isEmpty()) {
+            if (debug) {
+                log.debug("-> No output modes for this state.");
+            }
+            return new ArrayList<Output>();
+        }
+
+        String stateId = state.getID();
+
+        List<Facet> fs = getFacets(stateId);
+
+        if (fs == null || fs.isEmpty()) {
+            if (debug) {
+                log.debug("No facets found.");
+            }
+            return new ArrayList<Output>();
+        }
+        if (debug) {
+            log.debug("State '" + stateId + "' has facets " + fs);
+        }
+
+        List<Output> gen = generateOutputs(list, fs);
+
+        if (debug) {
+            log.debug("State '" + stateId + "' has " + gen.size() + " outs");
+        }
+
+        return gen;
+    }
+
+
+    /**
+     * Generate a list of outputs with facets from fs if type is found in list
+     * of output.
+     *
+     * @param list List of outputs
+     * @param fs List of facets
+     */
+    protected List<Output> generateOutputs(List<Output> list, List<Facet> fs) {
+        List<Output> generated = new ArrayList<Output>();
+        log.debug("generateOutputs for Artifact " + getName() + " "
+                + identifier());
+
+        boolean debug = log.isDebugEnabled();
+
+        for (Output out: list) {
+            log.debug("check facets for output: " + out.getName());
+            Output o = new DefaultOutput(
+                out.getName(),
+                out.getDescription(),
+                out.getMimeType(),
+                out.getType());
+
+            Set<String> outTypes = new HashSet<String>();
+
+            for (Facet f: out.getFacets()) {
+                if (outTypes.add(f.getName()) && debug) {
+                    log.debug("configured facet " + f);
+                }
+            }
+
+            boolean facetAdded = false;
+            for (Facet f: fs) {
+                String type = f.getName();
+
+                if (outTypes.contains(type)) {
+                    if (debug) {
+                        log.debug("Add facet " + f);
+                    }
+                    facetAdded = true;
+                    o.addFacet(f);
+                }
+            }
+
+            if (facetAdded) {
+                generated.add(o);
+            }
+        }
+
+        return generated;
+    }
+
+
+    /**
+     * Dispatches the computation request to compute(CallContext context, String
+     * hash) with the current hash value of the artifact which is provided by
+     * hash().
+     *
+     * @param context The CallContext.
+     */
+    public Object compute(
+        CallContext context,
+        ComputeType type,
+        boolean     generateFacets
+    ) {
+        return compute(context, hash(), type, generateFacets);
+    }
+
+
+    /**
+     * Dispatches computation requests to the current state which needs to
+     * implement a createComputeCallback(String hash, FLYSArtifact artifact)
+     * method.
+     *
+     * @param context The CallContext.
+     * @param hash The hash value which is used to fetch computed data from
+     * cache.
+     *
+     * @return the computed data.
+     */
+    public Object compute(
+        CallContext context,
+        String      hash,
+        ComputeType type,
+        boolean     generateFacets
+    ) {
+        DefaultState current = (DefaultState) getCurrentState(context);
+        return compute(context, hash, current, type, generateFacets);
+    }
+
+
+    /**
+     * Like compute, but identify State by it id (string).
+     */
+    public Object compute(
+        CallContext context,
+        String      hash,
+        String      stateID,
+        ComputeType type,
+        boolean     generateFacets
+    ) {
+        DefaultState current =
+            (stateID == null)
+            ? (DefaultState)getCurrentState(context)
+            : (DefaultState)getState(context, stateID);
+
+        if (hash == null) {
+            hash = hash();
+        }
+
+        return compute(context, hash, current, type, generateFacets);
+    }
+
+
+    /**
+     * Let current state compute and register facets.
+     *
+     * @param key key of state
+     * @param state state
+     * @param type Type of compute
+     * @param generateFacets Whether new facets shall be generated.
+     */
+    public Object compute(
+        CallContext   context,
+        String        key,
+        DefaultState  state,
+        ComputeType   type,
+        boolean       generateFacets
+    ) {
+        String stateID = state.getID();
+
+        List<Facet> fs = (generateFacets) ? new ArrayList<Facet>() : null;
+
+        try {
+            Cache cache = CacheFactory.getCache(COMPUTING_CACHE);
+
+            Object old = null;
+
+            if (cache != null) {
+                net.sf.ehcache.Element element = cache.get(key);
+                if (element != null) {
+                    log.debug("Got computation result from cache.");
+                    old = element.getValue();
+                }
+            }
+            else {
+                log.debug("cache not configured.");
+            }
+
+            Object res;
+            switch (type) {
+                case FEED:
+                    res = state.computeFeed(this, key, context, fs, old);
+                    break;
+                case ADVANCE:
+                    res = state.computeAdvance(this, key, context, fs, old);
+                    break;
+                case INIT:
+                    res = state.computeInit(this, key, context, context.getMeta(), fs);
+                default:
+                    res = null;
+            }
+
+            if (cache != null && old != res && res != null) {
+                log.debug("Store computation result to cache.");
+                net.sf.ehcache.Element element =
+                    new net.sf.ehcache.Element(key, res);
+                cache.put(element);
+            }
+
+            return res;
+        }
+        finally {
+            if (generateFacets) {
+                if (fs.isEmpty()) {
+                    facets.remove(stateID);
+                }
+                else {
+                    addFacets(stateID, fs);
+                }
+            }
+        }
+    }
+
+    /**
+     * Sets the facets for an ID
+     *
+     * Normally the id is a state ID.
+     *
+     * @param id ID to map the facets to
+     * @param facets List of facets to be stored
+     */
+    protected void addFacets(String id, List<Facet> facets) {
+        this.facets.put(id, facets);
+    }
+
+
+    /**
+     * Method to dump the artifacts state/data.
+     */
+    protected void dumpArtifact() {
+        log.debug("++++++++++++++ DUMP ARTIFACT DATA +++++++++++++++++");
+        // Include uuid, type, name
+        log.debug(" - Name: " + getName());
+        log.debug(" - UUID: " + identifier());
+        log.debug(" - Class: " + this.getClass().getName());
+
+        log.debug("------ DUMP DATA ------");
+        Collection<StateData> allData = data.values();
+
+        for (StateData d: allData) {
+            String name  = d.getName();
+            String value = (String) d.getValue();
+
+            log.debug("- " + name + ": " + value);
+        }
+
+        log.debug("------ DUMP PREVIOUS STATES ------");
+        List<String> stateIds = getPreviousStateIds();
+
+        for (String id: stateIds) {
+            log.debug("- State: " + id);
+        }
+
+        log.debug("CURRENT STATE: " + getCurrentStateId());
+
+        debugFacets();
+        dumpFilterFacets();
+
+        log.debug("++++++++++++++ END ARTIFACT DUMP +++++++++++++++++");
+    }
+
+
+    protected void debugFacets() {
+        log.debug("######### FACETS #########");
+
+        for (Map.Entry<String, List<Facet>> entry: facets.entrySet()) {
+            String out = entry.getKey();
+            List<Facet> fs = entry.getValue();
+            for (Facet f: fs) {
+                log.debug("  # " + out + " : " + f.getName());
+            }
+        }
+
+        log.debug("######## FACETS END ########");
+    }
+
+
+    protected void dumpFilterFacets() {
+        log.debug("######## FILTER FACETS ########");
+
+        if (filterFacets == null || filterFacets.isEmpty()) {
+            log.debug("No Filter Facets defined.");
+            return;
+        }
+
+        for (Map.Entry<String, List<Facet>> entry: filterFacets.entrySet()) {
+            String      out     = entry.getKey();
+            List<Facet> filters = entry.getValue();
+
+            log.debug("There are " + filters.size() + " filters for: " +out);
+
+            for (Facet filter: filters) {
+                log.debug("  filter: " + filter.getName());
+            }
+        }
+
+        log.debug("######## FILTER FACETS END ########");
+    }
+
+
+    /** Destroy and clean up state with given id. */
+    protected void destroyState(String id, Object context) {
+        State s = getState(context, id);
+        s.endOfLife(this, context);
+    }
+
+
+    /**
+     * Calls endOfLife() for each state in the list <i>ids</i>.
+     *
+     * @param ids The State IDs that should be destroyed.
+     * @param context The FLYSContext.
+     */
+    protected void destroyStates(List<String> ids, Object context) {
+        for (int i = 0, num = ids.size(); i < num; i++) {
+            destroyState(ids.get(i), context);
+        }
+    }
+
+
+    /**
+     * Destroy the states.
+     */
+    @Override
+    public void endOfLife(Object context) {
+        if (log.isDebugEnabled()) {
+            log.debug("FLYSArtifact.endOfLife: " + identifier());
+        }
+
+        ArrayList<String> ids       = (ArrayList<String>) getPreviousStateIds();
+        ArrayList<String> toDestroy = (ArrayList<String>) ids.clone();
+
+        toDestroy.add(getCurrentStateId());
+
+        destroyStates(toDestroy, context);
+    }
+
+    /**
+     * Return the Facets which a state provides
+     * @param stateid String that identifies the state
+     * @return List of Facets belonging to the state identifier
+     */
+    protected List<Facet> getFacets(String stateid) {
+        return this.facets.get(stateid);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/FixationArtifact.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,81 @@
+package org.dive4elements.river.artifacts;
+
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.geom.Lines;
+import org.dive4elements.river.artifacts.StaticWKmsArtifact;
+
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.WKms;
+import org.dive4elements.river.artifacts.model.fixings.FixRealizingResult;
+
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+import org.dive4elements.river.model.FastCrossSectionLine;
+
+import org.apache.log4j.Logger;
+
+/**
+ * The default fixation analysis artifact.
+ *
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class FixationArtifact
+extends      FLYSArtifact
+implements   WaterLineArtifact
+{
+    /** The logger for this class. */
+    private static Logger logger = Logger.getLogger(FixationArtifact.class);
+
+    /** The name of the artifact. */
+    public static final String ARTIFACT_NAME = "fixanalysis";
+
+    /* FacetActivity for this artifact is registered in FixAnalysisCompute . */
+
+    /**
+     * The default constructor.
+     */
+    public FixationArtifact() {
+        logger.debug("ctor()");
+    }
+
+    /**
+     * Returns the name of the concrete artifact.
+     *
+     * @return the name of the concrete artifact.
+     */
+    @Override
+    public String getName() {
+        return ARTIFACT_NAME;
+    }
+
+    @Override
+    public Lines.LineData getWaterLines(
+        int                  facetIdx,
+        FastCrossSectionLine      csl,
+        double                      d,
+        double                      w,
+        CallContext           context
+    ) {
+        FixRealizingResult result = (FixRealizingResult)
+            ((CalculationResult)this.compute(context, ComputeType.ADVANCE, false)).getData();
+
+        WKms wkms = result.getWQKms()[facetIdx];
+
+        double km = csl.getKm();
+
+        // Find W at km.
+        double wAtKm;
+
+        wAtKm = StaticWKmsArtifact.getWAtKm(wkms, km);
+
+        if (wAtKm == -1 || Double.isNaN(wAtKm)) {
+            logger.warn("Waterlevel at km " + km + " unknown.");
+            return new Lines.LineData(new double[][] {{}}, 0d, 0d);
+        }
+
+        // This should be FixRealizationResult, which can be getWQKms()ed
+        return Lines.createWaterLines(csl.getPoints(), wAtKm);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/FlowVelocityMeasurementArtifact.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,194 @@
+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.model.FlowVelocityMeasurementValue;
+import org.dive4elements.river.artifacts.model.minfo.FlowVelocityMeasurementFacet;
+import org.dive4elements.river.artifacts.model.minfo.FlowVelocityMeasurementFactory;
+import org.dive4elements.river.artifacts.states.StaticState;
+
+import org.dive4elements.river.artifacts.model.FacetTypes;
+
+
+/** Artefact to access flow velocity measurements. */
+public class FlowVelocityMeasurementArtifact
+extends      StaticFLYSArtifact
+implements   FacetTypes
+{
+    /** The logger for this class. */
+    private static Logger logger =
+        Logger.getLogger(FlowVelocityMeasurementArtifact.class);
+
+    /** Artifact key name. */
+    private static final String NAME = "flowvelocitymeasurement";
+
+    /** 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.flowvelocitymeasurement.static";
+
+    /** One and only state to be in. */
+    protected transient State state = null;
+
+    protected String DATA_NAME = "ID";
+
+    /**
+     * Trivial Constructor.
+     */
+    public FlowVelocityMeasurementArtifact() {
+        logger.debug("FlowVelocityMeasurementArtifact.FlowVelocityMeasurementArtifact");
+    }
+
+
+    /** Get artifact key name. */
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+
+    /** Create a new state with bogus output. */
+    protected State spawnState() {
+        state = new StaticState(STATIC_STATE_NAME);
+        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("FlowVelocityMeasurementArtifact.setup");
+
+        state = new StaticState(STATIC_STATE_NAME);
+
+        if (logger.isDebugEnabled()) {
+            logger.debug(XMLUtils.toString(data));
+        }
+
+        List<Facet> fs = new ArrayList<Facet>();
+        String code = getDatacageIDValue(data);
+
+        if (code != null) {
+            // parse code, interact with factory, add real facets.
+            // store relevant parts of code as data.
+            Facet facet = new FlowVelocityMeasurementFacet(
+                FLOW_VELOCITY_MEASUREMENT,
+                "flowvelocity-name");
+            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("initialize");
+    }
+
+
+    /** Get the db-unbound flow velocity measurement value with given
+     * id. */
+    public FlowVelocityMeasurementValue.FastFlowVelocityMeasurementValue
+        getFlowVelocityMeasurementValue()
+    {
+        return FlowVelocityMeasurementFactory.getFlowVelocityMeasurement(
+            Integer.parseInt(getDataAsString(DATA_NAME)));
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/GaugeDischargeArtifact.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,187 @@
+package org.dive4elements.river.artifacts;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.ArtifactFactory;
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.WQKms;
+
+import org.dive4elements.river.artifacts.states.DefaultState;
+
+import org.dive4elements.river.artifacts.model.Calculation;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+
+import org.dive4elements.river.artifacts.model.DischargeTables;
+
+import org.dive4elements.river.model.Gauge;
+import org.dive4elements.river.model.River;
+
+import org.dive4elements.river.utils.FLYSUtils;
+
+
+/**
+ * Artifact to get discharge curves at gauges.
+ */
+public class GaugeDischargeArtifact
+extends      WINFOArtifact
+implements   FacetTypes
+{
+    /** The logger for this class. */
+    private static Logger logger = Logger.getLogger(GaugeDischargeArtifact.class);
+
+    /** The name of the artifact. */
+    public static final String ARTIFACT_NAME = "gaugedischarge";
+
+
+    /**
+     * Trivial Constructor.
+     */
+    public GaugeDischargeArtifact() {
+        logger.debug("GaugeDischargeArtifact.GaugeDischargeArtifact()");
+    }
+
+
+    /**
+     * Gets called from factory, to set things up.
+     * Especially, when loaded via datacage mechanisms, provide the
+     * data document.
+     * @param data filled with stuff from dc, if any.
+     */
+    @Override
+    public void setup(
+        String          identifier,
+        ArtifactFactory factory,
+        Object          context,
+        CallMeta        callMeta,
+        Document        data)
+    {
+        logger.debug("GaugeDischargeArtifact.setup");
+        String ids = StaticFLYSArtifact.getDatacageIDValue(data);
+        addStringData("ids", ids);
+        logger.debug("id for gaugedischarge: " + ids);
+        super.setup(identifier, factory, context, callMeta, data);
+    }
+
+
+    /** Return the name of this artifact. */
+    public String getName() {
+        return ARTIFACT_NAME;
+    }
+
+
+    /**
+     * Setup state and facet, copy from master artifact.
+     */
+    @Override
+    protected void initialize(Artifact art, Object context, CallMeta meta) {
+        logger.debug("GaugeDischargeArtifact.initialize");
+        List<Facet> fs = new ArrayList<Facet>();
+        FLYSArtifact artifact = (FLYSArtifact) art;
+        importData(artifact, "river");
+
+        // Get the location(s)
+        //importData(artifact, "ld_mode", ld_from, ld_to, ld_locations
+        addStringData("ld_from", "0");
+        addStringData("ld_to", "1000");
+        addStringData("ld_mode", "distance");
+
+        DefaultState state = (DefaultState) getCurrentState(context);
+        state.computeInit(this, hash(), context, meta, fs);
+        if (!fs.isEmpty()) {
+            logger.debug("Facets to add in GaugeDischargeArtifact.initialize. ("
+                + state.getID() + "/ " + getCurrentStateId() + ").");
+            addFacets(getCurrentStateId(), fs);
+        }
+        else {
+            logger.debug("No facets to add in GaugeDischargeArtifact.initialize ("
+                + state.getID() + "/ "+getCurrentStateId()+").");
+        }
+    }
+
+
+    /** Get the Gauges name which came with datacage data-document. */
+    public String getGaugeName() {
+        return this.getDataAsString("ids");
+    }
+
+
+    /** Get the Gauges which came with datacage data-document. */
+    public Gauge getGauge() {
+        River river = FLYSUtils.getRiver(this);
+        return river.determineGaugeByName(getGaugeName());
+    }
+
+
+    /**
+     * Returns the data that is used to create discharge curves.
+     * @return CalculationResult with WQKms.
+     */
+    public CalculationResult getDischargeCurveData() {
+
+        River river = FLYSUtils.getRiver(this);
+        if (river == null) {
+            return error(new WQKms[0], "no.river.selected");
+        }
+        /*
+        // This one would allow to automatically pick the right Gauge.
+        double [] distance = FLYSUtils.getKmRange(this);
+        logger.debug("getDischargeCurveData: get range");
+
+        if (distance == null) {
+            return error(new WQKms[0], "no.range.found");
+        }
+
+        List<Gauge> gauges = river.determineGauges(distance[0], distance[1]);
+        logger.debug("getDischargeCurveData: got " + gauges.size() + " gauges");
+
+        if (gauges.isEmpty()) {
+            return error(new WQKms[0], "no.gauge.selected");
+        }
+
+        String [] names = new String[gauges.size()];
+
+        for (int i = 0; i < names.length; ++i) {
+            names[i] = gauges.get(i).getName();
+            logger.debug("getDischargeCurveData: name " + names[i]);
+        }
+        */
+
+        DischargeTables dt = new DischargeTables(river.getName(), getDataAsString("ids"));
+
+        Map<String, double [][]> map = dt.getValues(100);
+
+        ArrayList<WQKms> res = new ArrayList<WQKms>();
+
+        Gauge gauge = river.determineGaugeByName(this.getDataAsString("ids"));
+
+        String name = getGaugeName();
+        double [][] values = map.get(name);
+        if (values == null) {
+            logger.error("No values for this gauge / discharge found.");
+            return error(new WQKms[0], "no.gauge.found");
+        }
+        for (int i = 0 ; i < values[0].length; i++) {
+            values[0][i] += gauge.getDatum().doubleValue();
+        }
+        double [] kms = new double[values[0].length];
+        Arrays.fill(kms, gauge.getStation().doubleValue());
+        res.add(new WQKms(kms, values[0], values[1], name));
+
+        return new CalculationResult(
+            res.toArray(new WQKms[res.size()]),
+            new Calculation());
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/GaugeDischargeCurveArtifact.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,211 @@
+package org.dive4elements.river.artifacts;
+
+import java.math.BigDecimal;
+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.data.DefaultStateData;
+
+import org.dive4elements.artifacts.ArtifactFactory;
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.artifacts.common.ArtifactNamespaceContext;
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+
+import org.dive4elements.river.artifacts.model.GaugeDischargeCurveFacet;
+import org.dive4elements.river.artifacts.resources.Resources;
+import org.dive4elements.river.artifacts.states.StaticState;
+
+import org.dive4elements.river.model.Gauge;
+import org.dive4elements.river.utils.FLYSUtils;
+
+
+/**
+ * Artifact to calculate a discharge curve from a gauge overview info
+ *
+ * @author <a href="mailto:bjoern.ricks@intevation.de">Björn Ricks</a>
+ */
+public class GaugeDischargeCurveArtifact
+extends      AbstractStaticStateArtifact
+{
+
+    private static final Logger logger =
+        Logger.getLogger(GaugeDischargeCurveArtifact.class);
+
+    public static final String XPATH_RIVER = "/art:action/art:river/@art:name";
+    public static final String XPATH_GAUGE = "/art:action/art:gauge/@art:reference";
+    public static final String NAME = "gaugedischargecurve";
+    public static final String STATIC_STATE_NAME = "state.gaugedischargecurve.static";
+    public static final String UIPROVIDER = "gauge_discharge_curve";
+    public static final String GAUGE_DISCHARGE_CURVE_FACET =
+        "gauge_discharge_curve";
+    public static final String GAUGE_DISCHARGE_CURVE_AT_EXPORT_FACET =
+        "at";
+    public static final String GAUGE_DISCHARGE_CURVE_OUT =
+        "discharge_curve";
+    public static final String GAUGE_DISCHARGE_CURVE_AT_EXPORT_OUT =
+        "computed_dischargecurve_at_export";
+    public static final String GAUGE_DISCHARGE_CURVE_EXPORT_OUT =
+        "computed_dischargecurve_export";
+    public static final String GAUGE_DISCHARGE_CURVE_CSV_FACET =
+        "csv";
+    public static final String GAUGE_DISCHARGE_CURVE_PDF_FACET =
+        "pdf";
+
+    private Facet atexportfacet;
+    private Facet curvefacet;
+    private Facet csvfacet;
+    private Facet pdffacet;
+
+    /**
+     * Setup initializes the data by extracting the river and gauge from
+     * the XML Document.
+     */
+    @Override
+    public void setup(
+        String          identifier,
+        ArtifactFactory factory,
+        Object          context,
+        CallMeta        callmeta,
+        Document        data)
+    {
+        logger.debug("GaugeDischargeCurveArtifact.setup");
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("GaugeDischargeCurveArtifact.setup" + XMLUtils.toString(data));
+        }
+        String gaugeref = XMLUtils.xpathString(data, XPATH_GAUGE,
+                ArtifactNamespaceContext.INSTANCE);
+        String rivername = XMLUtils.xpathString(data, XPATH_RIVER,
+                ArtifactNamespaceContext.INSTANCE);
+
+        addData("river", new DefaultStateData("river",
+                    Resources.getMsg(callmeta,
+                        "facet.gauge_discharge_curve.river",
+                        "Name of the river"),
+                    "String", rivername));
+        addData("reference_gauge", new DefaultStateData("reference_gauge",
+                    Resources.getMsg(callmeta,
+                        "facet.gauge_discharge_curve.reference_gauge",
+                        "Gauge official number"),
+                    "Long", gaugeref));
+
+        Gauge gauge = FLYSUtils.getReferenceGauge(this);
+        String gaugename = "";
+        Double gaugelocation = null;
+        if (gauge != null) {
+            gaugename = gauge.getName();
+            BigDecimal station = gauge.getStation();
+            if (station != null) {
+                gaugelocation = station.doubleValue();
+            }
+        }
+
+        addData("gauge_name", new DefaultStateData("gauge_name",
+                    Resources.getMsg(callmeta,
+                        "facet.gauge_discharge_curve.gauge_name",
+                        "Name of the gauge"),
+                    "String", gaugename));
+
+        if (gaugelocation != null) {
+            addData("ld_locations", new DefaultStateData("ld_locations",
+                        Resources.getMsg(callmeta,
+                            "facet.gauge_discharge_curve.gauge_location",
+                            "Location of the gauge"),
+                        "Double", gaugelocation.toString()));
+        }
+
+        String description = Resources.format(callmeta,
+                "facet.gauge_discharge_curve.description",
+                "Discharge curve on gauge",
+                rivername,
+                gaugename);
+
+        List<Facet> fs = new ArrayList<Facet>(4);
+        curvefacet = new GaugeDischargeCurveFacet(
+                GAUGE_DISCHARGE_CURVE_FACET, description);
+        fs.add(curvefacet);
+
+        description = Resources.format(callmeta,
+                "facet.gauge_discharge_curve_at_export.description",
+                "Discharge curve AT export on gauge",
+                rivername,
+                gaugename);
+        atexportfacet = new GaugeDischargeCurveFacet(
+                GAUGE_DISCHARGE_CURVE_AT_EXPORT_FACET, description);
+        fs.add(atexportfacet);
+
+        description = Resources.format(callmeta,
+                "facet.computed_dischargecurve_export.csv",
+                "Discharge curve CSV export on gauge",
+                rivername,
+                gaugename);
+        csvfacet = new GaugeDischargeCurveFacet(
+                GAUGE_DISCHARGE_CURVE_CSV_FACET, description);
+        fs.add(csvfacet);
+
+        description = Resources.format(callmeta,
+                "facet.computed_dischargecurve_export.pdf",
+                "Discharge curve PDF export on gauge",
+                rivername,
+                gaugename);
+        pdffacet = new GaugeDischargeCurveFacet(
+                GAUGE_DISCHARGE_CURVE_PDF_FACET, description);
+        fs.add(pdffacet);
+
+        addFacets(STATIC_STATE_NAME, fs);
+
+        super.setup(identifier, factory, context, callmeta, data);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    protected void initStaticState() {
+        StaticState state = new StaticState(STATIC_STATE_NAME);
+
+        List<Facet> fs = new ArrayList<Facet>(1);
+        fs.add(curvefacet);
+
+        DefaultOutput output = new DefaultOutput(
+            GAUGE_DISCHARGE_CURVE_OUT,
+            "output.discharge_curve",
+            "image/png",
+            fs,
+            "chart");
+        state.addOutput(output);
+
+        fs = new ArrayList<Facet>(1);
+        fs.add(atexportfacet);
+        output = new DefaultOutput(
+            GAUGE_DISCHARGE_CURVE_AT_EXPORT_OUT,
+            "output.computed_dischargecurve_at_export",
+            "text/plain",
+            fs,
+            "export");
+        state.addOutput(output);
+
+        fs = new ArrayList<Facet>(2);
+        fs.add(csvfacet);
+        fs.add(pdffacet);
+        output = new DefaultOutput(
+            GAUGE_DISCHARGE_CURVE_EXPORT_OUT,
+            "output.computed_dischargecurve_export",
+            "text/plain",
+            fs,
+            "export");
+        state.addOutput(output);
+
+        state.setUIProvider(UIPROVIDER);
+        setStaticState(state);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/HYKArtifact.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,124 @@
+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.artifacts.Artifact;
+import org.dive4elements.artifacts.ArtifactFactory;
+import org.dive4elements.artifacts.CallMeta;
+
+
+import org.dive4elements.river.artifacts.states.DefaultState;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifactdatabase.state.FacetActivity;
+
+
+/**
+ * Artifact to get hydr zones (HYKs).
+ */
+public class HYKArtifact extends StaticFLYSArtifact {
+
+    /** Name of Artifact. */
+    public static final String HYK_ARTIFACT_NAME = "hyk";
+
+    /** Name of data item keeping the hyk id to load formations from. */
+    public static final String HYK_ID = "hyk_artifact.data.id";
+
+    /** Name of data item keeping the km of cs master. */
+    public static final String HYK_KM = "hyk_artifact.data.km";
+
+    /** Own logger. */
+    private static final Logger logger =
+        Logger.getLogger(HYKArtifact.class);
+
+    static {
+        // TODO: Move to configuration.
+        FacetActivity.Registry.getInstance()
+            .register(HYK_ARTIFACT_NAME, FacetActivity.INACTIVE);
+    }
+
+    /** Return given name. */
+    @Override
+    public String getName() {
+        return HYK_ARTIFACT_NAME;
+    }
+
+
+    /** Store ids, do super.setup. */
+    @Override
+    public void setup(
+        String          identifier,
+        ArtifactFactory factory,
+        Object          context,
+        CallMeta        callMeta,
+        Document        data)
+    {
+        logger.info("HYKArtifact.setup");
+
+        String ids = getDatacageIDValue(data);
+
+        logger.info("HYKArtifact.setup: id is " + ids);
+
+        addStringData(HYK_ID, ids);
+
+        super.setup(identifier, factory, context, callMeta, data);
+    }
+
+
+    /** Set km as Data. */
+    public void setKm(double km) {
+        addStringData(HYK_KM, Double.toString(km));
+    }
+
+
+    /** Get km from state data. */
+    public double getKm() {
+        Double km = getDataAsDouble(HYK_KM);
+        if (km == null) {
+            // XXX returning 0 is to be compatible to older versions that had an
+            // own method getDataAsDouble that returned 0 if parsing the
+            // parameter failed.
+            return 0;
+        }
+        else {
+            return km;
+        }
+    }
+
+
+    /** Get hyk-id from state data. */
+    public int getHykId() {
+        return getDataAsInteger(HYK_ID);
+    }
+
+
+    /** Do not copy data from daddyfact. */
+    @Override
+    protected void initialize(
+        Artifact artifact,
+        Object   context,
+        CallMeta callMeta)
+    {
+        logger.debug("HYKArtifact.initialize");
+        importData((FLYSArtifact)artifact, "river");
+
+        List<Facet> fs = new ArrayList<Facet>();
+
+        DefaultState state = (DefaultState) getCurrentState(context);
+        state.computeInit(this, hash(), context, callMeta, fs);
+        if (!fs.isEmpty()) {
+            logger.debug("Facets to add in HYKArtifact.initialize .");
+            addFacets(getCurrentStateId(), fs);
+        }
+        else {
+            logger.debug("No facets to add in HYKArtifact.initialize ("
+                + state.getID() + ").");
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/MINFOArtifact.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,31 @@
+package org.dive4elements.river.artifacts;
+
+/**
+ * The default MINFO artifact.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class MINFOArtifact
+extends      FLYSArtifact
+{
+    /** The name of the artifact. */
+    public static final String ARTIFACT_NAME = "minfo";
+
+
+    /**
+     * The default constructor.
+     */
+    public MINFOArtifact() {
+    }
+
+    /**
+     * Returns the name of the concrete artifact.
+     *
+     * @return the name of the concrete artifact.
+     */
+    @Override
+    public String getName() {
+        return ARTIFACT_NAME;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/MainValuesArtifact.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,394 @@
+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.data.DefaultStateData;
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifactdatabase.state.FacetActivity;
+import org.dive4elements.artifactdatabase.state.DefaultOutput;
+import org.dive4elements.artifactdatabase.state.State;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.ArtifactFactory;
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.river.model.Gauge;
+import org.dive4elements.river.model.MainValue;
+import org.dive4elements.river.model.River;
+
+import org.dive4elements.river.artifacts.access.RangeAccess;
+import org.dive4elements.river.artifacts.model.Calculation;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.MainValuesQFacet;
+import org.dive4elements.river.artifacts.model.MainValuesWFacet;
+import org.dive4elements.river.artifacts.model.NamedDouble;
+import org.dive4elements.river.artifacts.model.WstValueTable;
+import org.dive4elements.river.artifacts.model.WstValueTableFactory;
+
+import org.dive4elements.river.artifacts.states.StaticState;
+import org.dive4elements.river.artifacts.resources.Resources;
+
+import org.dive4elements.river.utils.FLYSUtils;
+
+
+/**
+ * Artifact to access main and extreme values of a river.
+ * This artifact neglects (Static)FLYSArtifacts capabilities of interaction
+ * with the StateEngine by overriding the getState*-methods.
+ */
+public class MainValuesArtifact
+extends      StaticFLYSArtifact
+implements   FacetTypes
+{
+    /** The logger for this class. */
+    private static Logger logger = Logger.getLogger(MainValuesArtifact.class);
+
+    /** The name of the artifact. */
+    public static final String ARTIFACT_NAME = "mainvalue";
+
+    /** The name of the static state for this artifact. */
+    public static final String STATIC_STATE_NAME = "state.mainvalue.static";
+
+    /** One and only state to be in. */
+    protected transient State state = null;
+
+
+    static {
+        // TODO: Move to configuration.
+        FacetActivity.Registry.getInstance().register(
+            ARTIFACT_NAME,
+            new FacetActivity() {
+                @Override
+                public Boolean isInitialActive(
+                    Artifact artifact,
+                    Facet    facet,
+                    String   outputName
+                ) {
+                    return outputName.equals("computed_discharge_curve")
+                        || outputName.equals("duration_curve");
+                }
+            });
+    }
+
+
+    /**
+     * Trivial Constructor.
+     */
+    public MainValuesArtifact() {
+        logger.debug("MainValuesArtifact.MainValuesartifact()");
+    }
+
+
+    /**
+     * Gets called from factory, to set things up.
+     */
+    @Override
+    public void setup(
+        String          identifier,
+        ArtifactFactory factory,
+        Object          context,
+        CallMeta        callMeta,
+        Document        data)
+    {
+        logger.debug("MainValuesArtifact.setup");
+        state = new StaticState(STATIC_STATE_NAME);
+
+        List<Facet> fs = new ArrayList<Facet>();
+        addFacets(state.getID(), fs);
+        spawnState();
+        super.setup(identifier, factory, context, callMeta, data);
+        String restriction = getDatacageIDValue(data);
+        logger.debug("mainvalue restriction " + restriction);
+        boolean restricted = restriction.endsWith("q") || restriction.endsWith("w");
+        if (!restricted || restriction.endsWith("q")) {
+            Facet qfacet0 = new MainValuesQFacet(
+                DURATION_MAINVALUES_Q,
+                Resources.getMsg(
+                    callMeta,
+                    "facet.discharge_curves.mainvalues.q",
+                    "facet.discharge_curves.mainvalues.q"),
+                false);
+            Facet qfacet1 = new MainValuesQFacet(
+                COMPUTED_DISCHARGE_MAINVALUES_Q,
+                Resources.getMsg(
+                    callMeta,
+                    "facet.discharge_curves.mainvalues.q",
+                    "facet.discharge_curves.mainvalues.q"),
+                false);
+            Facet qfacet2 = new MainValuesQFacet(
+                MAINVALUES_Q,
+                Resources.getMsg(
+                    callMeta,
+                    "facet.discharge_curves.mainvalues.q",
+                    "facet.discharge_curves.mainvalues.q"),
+                true);
+            Facet qfacet3 = new MainValuesQFacet(
+                HISTORICAL_DISCHARGE_MAINVALUES_Q,
+                Resources.getMsg(
+                    callMeta,
+                    "historical_discharge.mainvalues.q",
+                    "historical_discharge.mainvalues.q"),
+                false);
+
+            fs.add(qfacet0);
+            fs.add(qfacet1);
+            fs.add(qfacet2);
+            fs.add(qfacet3);
+        }
+        if (!restricted || restriction.endsWith("w")) {
+            Facet wfacet1 = new MainValuesWFacet(
+                COMPUTED_DISCHARGE_MAINVALUES_W,
+                Resources.getMsg(
+                    callMeta,
+                    "facet.discharge_curves.mainvalues.w",
+                    "facet.discharge_curves.mainvalues.w"),
+                false);
+            Facet wfacet2 = new MainValuesWFacet(
+                MAINVALUES_W,
+                Resources.getMsg(
+                    callMeta,
+                    "facet.discharge_curves.mainvalues.w",
+                    "facet.discharge_curves.mainvalues.w"),
+                true);
+            Facet wfacet3 = new MainValuesWFacet(
+                HISTORICAL_DISCHARGE_MAINVALUES_W,
+                Resources.getMsg(
+                    callMeta,
+                    "historical_discharge.mainvalues.w",
+                    "historical_discharge.mainvalues.w"),
+                true);
+            fs.add(wfacet1);
+            fs.add(wfacet2);
+            fs.add(wfacet3);
+        }
+    }
+
+
+    /**
+     * Create "the" state.
+     */
+    protected State spawnState() {
+        state = new StaticState(STATIC_STATE_NAME);
+        List<Facet> fs = (List<Facet>) getFacets(STATIC_STATE_NAME);
+
+        DefaultOutput mainValuesOutput = new DefaultOutput(
+            "computed_discharge_curve",
+            "output.computed_discharge_curve", "image/png",
+            fs,
+            "chart");
+
+        state.getOutputs().add(mainValuesOutput);
+        return state;
+    }
+
+
+    /** Get important data from the 'calling' artifact. */
+    @Override
+    protected void initialize(Artifact artifact, Object context, CallMeta meta) {
+        logger.debug("MainValuesArtifact.initialize");
+        FLYSArtifact winfo = (FLYSArtifact) artifact;
+        RangeAccess rangeAccess = new RangeAccess(winfo, null);
+        double [] locations = rangeAccess.getKmRange();
+
+        if (locations != null) {
+            double location = locations[0];
+            addData("ld_locations", new DefaultStateData("ld_locations", null, null,
+                    String.valueOf(location)));
+        }
+        else {
+            logger.error("No location for mainvalues given.");
+        }
+        importData(winfo, "river");
+        // In the case of DischargeWQCurves, there are no locations, but a gauge.
+        if (getDataAsString("ld_locations") == null) {
+            // TODO its a tad difficult to remodel Range/Gauge-Access to
+            // do this.
+            String refGaugeID = winfo.getDataAsString("reference_gauge");
+            if (refGaugeID != null) {
+                Gauge g = Gauge.getGaugeByOfficialNumber(Integer.parseInt(refGaugeID));
+                addData("ld_locations", new DefaultStateData("ld_locations", null, null,
+                    String.valueOf(g.getStation())));
+            }
+            else {
+                logger.error("MainValuesArtifact: No location/gauge.");
+            }
+        }
+    }
+
+
+    /**
+     * 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.
+     * @param cc ignored.
+     * @return the "current" 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) {
+        if (state != null)
+            return state;
+        else
+            return spawnState();
+    }
+
+
+    /**
+     * Access the Gauge that the mainvalues are taken from.
+     * @return Gauge that main values are taken from or null in case of
+     *         invalid parameterization.
+     */
+    protected Gauge getGauge() {
+        River river = FLYSUtils.getRiver(this);
+
+        // TODO use helper to get location as double
+        String locationStr = getDataAsString("ld_locations");
+
+        if (river == null) {
+            logger.error("River is null");
+            return null;
+        }
+
+        if (locationStr == null) {
+            logger.error("Locationstr is null");
+            return null;
+        }
+
+        double location = Double.parseDouble(locationStr);
+
+        return river.determineGaugeByPosition(location);
+    }
+
+
+    /**
+     * Get current location.
+     * @return the location.
+     */
+    public double getLocation() {
+        double location = Double.parseDouble(getDataAsString("ld_locations"));
+        return location;
+    }
+
+
+    /**
+     * Get a list of "Q" main values.
+     * @return list of Q main values.
+     */
+    public List<NamedDouble> getMainValuesQ(boolean atGauge) {
+        List<NamedDouble> filteredList = new ArrayList<NamedDouble>();
+        Gauge gauge = getGauge();
+        WstValueTable interpolator = WstValueTableFactory.getTable(FLYSUtils.getRiver(this));
+        Calculation c = new Calculation();
+        double w_out[] = {0.0f};
+        double q_out[] = {0.0f};
+        double kms[] = {getLocation()};
+        double gaugeStation = gauge.getStation().doubleValue();
+        if (gauge != null) {
+            List<MainValue> orig = gauge.getMainValues();
+            for (MainValue mv : orig) {
+                if (mv.getMainValue().getType().getName().equals("Q")) {
+                    if (atGauge) {
+                        q_out[0] = mv.getValue().doubleValue();
+                    }
+                    else {
+                        interpolator.interpolate(mv.getValue().doubleValue(),
+                            gaugeStation, kms, w_out, q_out, c);
+                    }
+                    filteredList.add(new NamedDouble(
+                                mv.getMainValue().getName(),
+                                q_out[0]
+                                ));
+                }
+            }
+        }
+        return filteredList;
+    }
+
+
+    /**
+     * Get a list of "W" main values.
+     * @param atGauge if true, do not interpolate
+     * @return list of W main values.
+     */
+    public List<NamedDouble> getMainValuesW(boolean atGauge) {
+        List<NamedDouble> filteredList = new ArrayList<NamedDouble>();
+        Gauge gauge = getGauge();
+        WstValueTable interpolator = WstValueTableFactory.getTable(FLYSUtils.getRiver(this));
+        Calculation c = new Calculation();
+
+        double gaugeStation = gauge.getStation().doubleValue();
+        double w_out[] = {0.0f};
+        double q_out[] = {0.0f};
+        double kms[] = {getLocation()};
+        if (gauge != null) {
+            List<MainValue> orig = gauge.getMainValues();
+            for (MainValue mv : orig) {
+                if (atGauge) {
+                    if (mv.getMainValue().getType().getName().equals("W")) {
+                        filteredList.add(new NamedDouble(mv.getMainValue().getName(),
+                                mv.getValue().doubleValue()));
+
+                    }
+                } else
+                // We cannot interpolate the W values, so derive them
+                // from given Q values.
+                if (mv.getMainValue().getType().getName().equals("Q")) {
+                    interpolator.interpolate(mv.getValue().doubleValue(),
+                            gaugeStation, kms, w_out, q_out, c);
+                    filteredList.add(new NamedDouble(
+                                "W(" + mv.getMainValue().getName() +")",
+                                w_out[0]
+                                ));
+                }
+            }
+        }
+        return filteredList;
+    }
+
+
+    /**
+     * Returns the name of this artifact ('mainvalue').
+     *
+     * @return 'mainvalue'
+     */
+    public String getName() {
+        return ARTIFACT_NAME;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/ManualPointsArtifact.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,145 @@
+package org.dive4elements.river.artifacts;
+
+import java.awt.geom.Point2D;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.w3c.dom.Document;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.ArtifactFactory;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.river.artifacts.geom.Lines;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.states.DefaultState;
+import org.dive4elements.river.model.FastCrossSectionLine;
+
+
+/**
+ * Artifact to store user-added points and water lines.
+ */
+public class ManualPointsArtifact
+extends      StaticFLYSArtifact
+implements   FacetTypes, WaterLineArtifact
+{
+    private static final long serialVersionUID = 7096025125474986011L;
+
+    /** The logger for this class. */
+    private static Logger logger = Logger.getLogger(ManualPointsArtifact.class);
+
+    /** The name of the artifact. */
+    public static final String ARTIFACT_NAME = "manualpoints";
+
+
+    public ManualPointsArtifact() {
+        logger.debug("ManualPointsArtifact.ManualPointsArtifact()");
+    }
+
+
+    /**
+     * Gets called from factory to set things up.
+     */
+    @Override
+    public void setup(
+            String          identifier,
+            ArtifactFactory factory,
+            Object          context,
+            CallMeta        callMeta,
+            Document        data)
+    {
+        logger.debug("ManualPointsArtifact.setup");
+        super.setup(identifier, factory, context, callMeta, data);
+        initialize(null, context, callMeta);
+    }
+
+
+    /** Return the name of this artifact. */
+    @Override
+    public String getName() {
+        return ARTIFACT_NAME;
+    }
+
+
+    /** Access state data storing the jsonstring with points. */
+    public String getPointsData(String facetName) {
+        return getDataAsString(facetName + ".data");
+    }
+
+
+    /**
+     * Access state data storing the jsonstring with lines.
+     * @param facetName Name of facet or null if the so far
+     *                  only known case should be picked.
+     * @return (String) value of data element (expect json).
+     */
+    public String getLinesData(String facetName) {
+        if (facetName == null)
+            return getDataAsString("cross_section.manualpoints.lines");
+        // TODO .lineS?
+        return getDataAsString(facetName + ".line");
+    }
+
+
+    /** Setup state and facet. */
+    @Override
+    protected void initialize(Artifact artifact, Object context, CallMeta meta) {
+        logger.debug("ManualPointsArtifact.initialize");
+        List<Facet> fs = new ArrayList<Facet>();
+
+        DefaultState state = (DefaultState) getCurrentState(context);
+        state.computeInit(this, hash(), context, meta, fs);
+        if (!fs.isEmpty()) {
+            logger.debug("Facets to add in ManualPointsArtifact.initialize .");
+            addFacets(getCurrentStateId(), fs);
+        }
+        else {
+            logger.debug("No facets to add in ManualPointsArtifact.initialize ("
+                    + state.getID() + ").");
+        }
+    }
+
+
+    /**
+     * Get value of line at index.
+     * @param index index in json array defining lines.
+     * @return water height of line at given index.
+     */
+    protected double getLine(int index) {
+        try {
+            JSONArray lines = new JSONArray(getLinesData(null));
+            JSONArray array = lines.getJSONArray(index);
+
+            return array.getDouble(0);
+        }
+        catch(JSONException e){
+            logger.error("Could not decode json for line.");
+            return 0d;
+        }
+    }
+
+
+    /**
+     * Get the water line "surface".
+     * @param index index of facets data.
+     * @param csl 'ground' against which to determine water surface.
+     * @param a (ignored in this implementation).
+     * @param b (ignored in this implementation).
+     * @param context (ignored in this implementation).
+     */
+    @Override
+    public Lines.LineData getWaterLines(
+            int                  index,
+            FastCrossSectionLine csl,
+            double a, double b,
+            CallContext context
+            ) {
+        List<Point2D> points = csl.getPoints();
+        return Lines.createWaterLines(points, getLine(index));
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/MapArtifact.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,188 @@
+package org.dive4elements.river.artifacts;
+
+import org.dive4elements.artifactdatabase.ProtocolUtils;
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifactdatabase.state.Output;
+import org.dive4elements.artifactdatabase.state.State;
+import org.dive4elements.artifactdatabase.state.StateEngine;
+import org.dive4elements.artifacts.ArtifactFactory;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator;
+import org.dive4elements.river.artifacts.RiverAxisArtifact.RiverAxisState;
+import org.dive4elements.river.artifacts.context.FLYSContext;
+import org.dive4elements.river.artifacts.model.map.WMSDBLayerFacet;
+import org.dive4elements.river.artifacts.states.DefaultState;
+import org.dive4elements.river.model.River;
+import org.dive4elements.river.utils.FLYSUtils;
+import org.dive4elements.river.utils.MapUtils;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+
+public class MapArtifact extends FLYSArtifact {
+
+    private static final Logger logger =
+        Logger.getLogger(MapArtifact.class);
+
+    @Override
+    public void setup(
+        String          identifier,
+        ArtifactFactory factory,
+        Object          context,
+        CallMeta        callmeta,
+        Document        data)
+    {
+        logger.debug("MapArtifact.setup");
+        this.identifier = identifier;
+        name = "new_map";
+
+        List<State> states = getStates(context);
+
+        setCurrentState(states.get(0));
+    }
+
+
+    @Override
+    protected void appendBackgroundActivity(
+        ElementCreator cr,
+        Element        root,
+        CallContext    context
+    ) {
+        Element inBackground = cr.create("background-processing");
+        root.appendChild(inBackground);
+
+        cr.addAttr(
+            inBackground,
+            "value",
+            String.valueOf(context.isInBackground()),
+            true);
+    }
+
+
+    /**
+     * Append output mode nodes to a document.
+     */
+    @Override
+    protected void appendOutputModes(
+        Document    doc,
+        Element     outs,
+        CallContext context,
+        String      uuid)
+    {
+        List<String> stateIds = getPreviousStateIds();
+
+        FLYSContext flysContext = FLYSUtils.getFlysContext(context);
+        StateEngine engine      = (StateEngine) flysContext.get(
+            FLYSContext.STATE_ENGINE_KEY);
+
+        for (String stateId: stateIds) {
+            logger.debug("Append output modes for state: " + stateId);
+            DefaultState state = (DefaultState) engine.getState(stateId);
+
+            List<Output> list = state.getOutputs();
+            if (list == null || list.isEmpty()) {
+                logger.debug("-> No output modes for this state.");
+                continue;
+            }
+
+            List<Facet> fs = getFacets(stateId);
+
+            if (fs == null || fs.isEmpty()) {
+                logger.debug("No facets for previous state found.");
+                continue;
+            }
+
+            logger.debug("Found " + fs.size() + " facets in previous states.");
+
+            List<Output> generated = generateOutputs(list, fs);
+
+            ProtocolUtils.appendOutputModes(doc, outs, generated);
+        }
+
+        try {
+            DefaultState cur = (DefaultState) getCurrentState(context);
+            if (cur.validate(this)) {
+                List<Output> list = cur.getOutputs();
+                if (list != null && list.size() > 0) {
+                    logger.debug(
+                        "Append output modes for current state: " + cur.getID());
+
+                    List<Facet> fs = getFacets(cur.getID());
+
+                    if (fs != null && fs.size() > 0) {
+                        List<Output> generated = generateOutputs(list, fs);
+
+                        logger.debug("Found " + fs.size() + " current facets.");
+                        if (!generated.isEmpty()) {
+                            ProtocolUtils.appendOutputModes(
+                                doc, outs, generated);
+                        }
+                    }
+                    else {
+                        logger.debug("No facets found for the current state.");
+                    }
+                }
+            }
+        }
+        catch (IllegalArgumentException iae) {
+            // state is not valid, so we do not append its outputs.
+        }
+    }
+
+    public static class MapState extends RiverAxisState {
+
+        @Override
+        public Object computeAdvance(
+            FLYSArtifact artifact,
+            String       hash,
+            CallContext  context,
+            List<Facet>  facets,
+            Object       old)
+        {
+            logger.debug("MapState.computeAdvance");
+
+            this.artifact = artifact;
+
+            String type = getFacetType();
+
+            WMSDBLayerFacet facet = new WMSDBLayerFacet(
+                0,
+                type,
+                getTitle(context.getMeta()),
+                ComputeType.ADVANCE,
+                getID(), hash,
+                getUrl());
+
+            String name = artifact.getDataAsString("river");
+
+            facet.addLayer(name);
+            facet.setExtent(getExtent(false));
+            facet.setOriginalExtent(getExtent(true));
+            facet.setSrid(getSrid());
+            facet.setData(getDataString());
+            facet.setFilter(getFilter());
+            facet.setGeometryType(getGeometryType());
+            facet.setConnection(MapUtils.getConnection());
+            facet.setConnectionType(MapUtils.getConnectionType());
+            facet.setLabelItem(getLabelItem());
+
+            facets.add(facet);
+
+            return null;
+        }
+
+        @Override
+        public int getRiverId() {
+            River r = FLYSUtils.getRiver(artifact);
+            int riverId = r.getId();
+
+            return riverId;
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/QSectorArtifact.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,131 @@
+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.Facet;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.ArtifactFactory;
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.model.GaugeFinder;
+import org.dive4elements.river.artifacts.model.GaugeFinderFactory;
+import org.dive4elements.river.artifacts.model.GaugeRange;
+import org.dive4elements.river.artifacts.model.NamedDouble;
+
+import org.dive4elements.river.artifacts.services.FixingsKMChartService;
+
+import org.dive4elements.river.artifacts.states.DefaultState;
+
+import org.dive4elements.river.artifacts.resources.Resources;
+
+
+/**
+ * Artifact to produce sector markers.
+ */
+public class QSectorArtifact
+extends      StaticFLYSArtifact
+{
+    /** The logger for this class. */
+    private static Logger logger = Logger.getLogger(QSectorArtifact.class);
+
+    /** The name of the artifact. */
+    public static final String ARTIFACT_NAME = "qsector";
+
+
+    /**
+     * Trivial Constructor.
+     */
+    public QSectorArtifact() {
+        logger.debug("QSectorArtifact.QSectorArtifact()");
+    }
+
+
+    /**
+     * Gets called from factory, to set things up.
+     */
+    @Override
+    public void setup(
+        String          identifier,
+        ArtifactFactory factory,
+        Object          context,
+        CallMeta        callMeta,
+        Document        data)
+    {
+        logger.debug("QSectorArtifact.setup");
+        super.setup(identifier, factory, context, callMeta, data);
+        initialize(null, context, callMeta);
+    }
+
+
+    /** Return the name of this artifact. */
+    public String getName() {
+        return ARTIFACT_NAME;
+    }
+
+
+    /** Get list of NamedDouble s (QSectors). */
+    public Object getQSectors(double km, CallContext context) {
+
+        String river = getDataAsString("river");
+        List<NamedDouble> qsectors = new ArrayList<NamedDouble>();
+
+        GaugeFinderFactory ggf = GaugeFinderFactory.getInstance();
+        GaugeFinder        gf  = ggf.getGaugeFinder(river);
+
+        if (gf == null) {
+            logger.warn("No gauge finder found for river '" + river + "'");
+            return null;
+        }
+
+        GaugeRange gr = gf.find(km);
+        if (gr == null) {
+            logger.debug("No gauge range found for km "
+                + km + " on river " + river + ".");
+            return null;
+        }
+
+        if (logger.isDebugEnabled()) {
+            logger.debug(gr);
+        }
+
+        for (int i = 0; i < FixingsKMChartService.I18N_Q_SECTOR_BOARDERS.length; ++i) {
+            String key   = FixingsKMChartService.I18N_Q_SECTOR_BOARDERS[i];
+            String def   = FixingsKMChartService.DEFAULT_Q_SECTOR_BORDERS[i];
+            String label = Resources.getMsg(context.getMeta(), key, def);
+
+            qsectors.add(new NamedDouble(label, gr.getSectorBorder(i)));
+        }
+
+        return qsectors;
+    }
+
+
+    /** Setup state and facet. */
+    @Override
+    protected void initialize(Artifact artifact, Object context, CallMeta meta) {
+        logger.debug("QSectorArtifact.initialize");
+        List<Facet> fs = new ArrayList<Facet>();
+
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+        importData(flys, "river");
+
+        DefaultState state = (DefaultState) getCurrentState(context);
+        state.computeInit(this, hash(), context, meta, fs);
+        if (!fs.isEmpty()) {
+            logger.debug("Facets to add in QSectorArtifact.initialize .");
+            addFacets(getCurrentStateId(), fs);
+        }
+        else {
+            logger.debug("No facets to add in QSectorArtifact.initialize ("
+                + state.getID() + ").");
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/RiverAxisArtifact.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,185 @@
+package org.dive4elements.river.artifacts;
+
+import com.vividsolutions.jts.geom.Envelope;
+import com.vividsolutions.jts.geom.Geometry;
+
+import org.dive4elements.artifactdatabase.state.DefaultOutput;
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifactdatabase.state.State;
+import org.dive4elements.artifacts.ArtifactFactory;
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.RiverFactory;
+import org.dive4elements.river.model.River;
+import org.dive4elements.river.model.RiverAxis;
+import org.dive4elements.river.utils.FLYSUtils;
+import org.dive4elements.river.utils.GeometryUtils;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.w3c.dom.Document;
+
+@Deprecated
+public class RiverAxisArtifact extends WMSDBArtifact {
+
+    public static final String NAME = "riveraxis";
+
+
+    private static final Logger logger =
+        Logger.getLogger(RiverAxisArtifact.class);
+
+
+    @Override
+    public void setup(
+        String          identifier,
+        ArtifactFactory factory,
+        Object          context,
+        CallMeta        callMeta,
+        Document        data)
+    {
+        logger.debug("RiverAxisArtifact.setup");
+
+        super.setup(identifier, factory, context, callMeta, data);
+    }
+
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+
+    @Override
+    public State getCurrentState(Object cc) {
+        State s = new RiverAxisState(this);
+
+        List<Facet> fs = getFacets(getCurrentStateId());
+
+        DefaultOutput o = new DefaultOutput(
+            "floodmap",
+            "floodmap",
+            "image/png",
+            fs,
+            "map");
+
+        s.getOutputs().add(o);
+
+        return s;
+    }
+
+
+    public static class RiverAxisState extends WMSDBState implements FacetTypes
+    {
+        private static final Logger logger =
+            Logger.getLogger(RiverAxisState.class);
+
+        protected Geometry geom;
+        protected int      riverId;
+
+        public RiverAxisState(){}
+
+        public RiverAxisState(FLYSArtifact artifact) {
+            super(artifact);
+        }
+
+        @Override
+        protected String getFacetType() {
+            return FLOODMAP_RIVERAXIS;
+        }
+
+        @Override
+        protected String getLayer() {
+            return RiverFactory.getRiver(getRiverId()).getName();
+        }
+
+        @Override
+        protected String getUrl() {
+            return FLYSUtils.getRiverWMSUrl();
+        }
+
+        @Override
+        protected String getSrid() {
+            River river = RiverFactory.getRiver(getRiverId());
+            return FLYSUtils.getRiverSrid(river.getName());
+        }
+
+        @Override
+        protected Envelope getExtent(boolean reproject) {
+            River river = RiverFactory.getRiver(getRiverId());
+            List<RiverAxis> axes;
+
+            String kind = getIdPart(2);
+
+            if (kind != null && kind.equals("1")) {
+                axes = RiverAxis.getRiverAxis(river.getName(),
+                        Integer.parseInt(kind));
+            } else if (kind != null) {
+                axes = RiverAxis.getRiverAxis(river.getName(),
+                        getName(), Integer.parseInt(kind));
+            } else {
+                if (reproject) {
+                    logger.debug("Query extent for RiverAxis with Srid: " + getSrid());
+                    return GeometryUtils.transform(
+                            GeometryUtils.getRiverBoundary(river.getName()),
+                            getSrid());
+                } else {
+                    return GeometryUtils.transform(
+                            GeometryUtils.getRiverBoundary(river.getName()),
+                            "31467");
+                }
+            }
+
+            Envelope max = null;
+
+            for (RiverAxis ax: axes) {
+                Envelope env = ax.getGeom().getEnvelopeInternal();
+
+                if (max == null) {
+                    max = env;
+                    continue;
+                }
+
+                max.expandToInclude(env);
+            }
+
+            return max != null && reproject
+                ? GeometryUtils.transform(max, getSrid())
+                : max;
+        }
+
+        @Override
+        protected String getFilter() {
+            String kind = getIdPart(2);
+            if (kind != null && kind.equals("1")) {
+                return "river_id=" + String.valueOf(getRiverId()) +
+                    " AND kind_id=" + kind;
+            } else if (kind != null) {
+                return "river_id=" + String.valueOf(getRiverId()) +
+                    " AND kind_id=" + kind +
+                    " AND name='" + getName() + "'";
+            } else if (getIdPart(1) != null) {
+                return "river_id=" + String.valueOf(getRiverId()) +
+                    " AND name='" + getName() + "'";
+            }
+            return "river_id=" + String.valueOf(getRiverId()) +
+                " AND kind_id=" + kind;
+        }
+
+        @Override
+        protected String getDataString() {
+            if (FLYSUtils.isUsingOracle()) {
+                return "geom FROM river_axes USING SRID " + getSrid();
+            }
+            else {
+                return "geom FROM river_axes USING UNIQUE id";
+            }
+        }
+
+        @Override
+        protected String getGeometryType() {
+            return "LINE";
+        }
+    } // end of WMSKmState
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/SQRelationArtifact.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,158 @@
+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.data.DefaultStateData;
+import org.dive4elements.artifactdatabase.state.DefaultOutput;
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifacts.ArtifactFactory;
+import org.dive4elements.artifacts.ArtifactNamespaceContext;
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+import org.dive4elements.river.artifacts.model.sq.StaticSQRelation;
+import org.dive4elements.river.artifacts.resources.Resources;
+import org.dive4elements.river.artifacts.states.StaticState;
+import org.dive4elements.river.artifacts.states.sq.SQStaticState;
+
+
+public class SQRelationArtifact
+extends AbstractStaticStateArtifact
+{
+    private static final Logger logger =
+        Logger.getLogger(SQRelationArtifact.class);
+
+    public static final String XPATH_RIVER = "/art:action/art:river/@art:name";
+    public static final String XPATH_STATION =
+        "/art:action/art:measurement_station/@art:number";
+    public static final String NAME = "staticsqrelation";
+    public static final String STATIC_STATE_NAME = "state.sqrelation.static";
+    public static final String UIPROVIDER = "static_sqrelation";
+    public static final String SQ_RELATION_OUT_A = "sq_relation_a";
+    public static final String SQ_RELATION_OUT_B = "sq_relation_b";
+    public static final String SQ_RELATION_OUT_C = "sq_relation_c";
+    public static final String SQ_RELATION_OUT_D = "sq_relation_d";
+    public static final String SQ_RELATION_OUT_E = "sq_relation_e";
+    public static final String SQ_RELATION_OUT_F = "sq_relation_f";
+
+
+    @Override
+    public void setup(
+        String          identifier,
+        ArtifactFactory factory,
+        Object          context,
+        CallMeta        callmeta,
+        Document        data
+    ) {
+        logger.debug("SQRelationArtifact.setup()");
+
+        String river = XMLUtils.xpathString(
+            data,
+            XPATH_RIVER,
+            ArtifactNamespaceContext.INSTANCE);
+        String station = XMLUtils.xpathString(
+            data,
+            XPATH_STATION,
+            ArtifactNamespaceContext.INSTANCE);
+
+        addData(
+            "river",
+            new DefaultStateData(
+                "river",
+                Resources.getMsg(callmeta, "static.sq.river", "Rivername"),
+                "String",
+                river));
+        addData(
+            "station",
+            new DefaultStateData(
+                "station",
+                Resources.getMsg(callmeta, "static.sq.station", "Station"),
+                "String",
+                station));
+        super.setup(identifier, factory, context, callmeta, data);
+    }
+
+    @Override
+    protected void initStaticState() {
+        StaticState state = new SQStaticState(STATIC_STATE_NAME);
+
+        List<Facet> fs = new ArrayList<Facet>();
+        state.staticCompute(fs, this);
+
+        if (hasParameter(StaticSQRelation.Parameter.A, fs)) {
+            DefaultOutput outputA = new DefaultOutput(
+                SQ_RELATION_OUT_A,
+                "output.static.sqrelation.a",
+                "image/png",
+                fs,
+                "chart");
+            state.addOutput(outputA);
+        }
+        if (hasParameter(StaticSQRelation.Parameter.B, fs)) {
+            DefaultOutput outputB = new DefaultOutput(
+                SQ_RELATION_OUT_B,
+                "output.static.sqrelation.b",
+                "image/png",
+                fs,
+                "chart");
+            state.addOutput(outputB);
+        }
+        if (hasParameter(StaticSQRelation.Parameter.C, fs)) {
+            DefaultOutput outputC = new DefaultOutput(
+                SQ_RELATION_OUT_C,
+                "output.static.sqrelation.c",
+                "image/png",
+                fs,
+                "chart");
+            state.addOutput(outputC);
+        }
+        if (hasParameter(StaticSQRelation.Parameter.D, fs)) {
+            DefaultOutput outputD = new DefaultOutput(
+                SQ_RELATION_OUT_D,
+                "output.static.sqrelation.d",
+                "image/png",
+                fs,
+                "chart");
+            state.addOutput(outputD);
+        }
+        if (hasParameter(StaticSQRelation.Parameter.E, fs)) {
+            DefaultOutput outputE = new DefaultOutput(
+                SQ_RELATION_OUT_E,
+                "output.static.sqrelation.e",
+                "image/png",
+                fs,
+                "chart");
+            state.addOutput(outputE);
+        }
+        if (hasParameter(StaticSQRelation.Parameter.F, fs)) {
+            DefaultOutput outputF = new DefaultOutput(
+                SQ_RELATION_OUT_F,
+                "output.static.sqrelation.f",
+                "image/png",
+                fs,
+                "chart");
+            state.addOutput(outputF);
+        }
+        addFacets(STATIC_STATE_NAME, fs);
+        state.setUIProvider(UIPROVIDER);
+        setStaticState(state);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    private boolean hasParameter(StaticSQRelation.Parameter p, List<Facet> fs) {
+        for (Facet f : fs) {
+            if (f.getName().equals("sq_" +
+                p.toString().toLowerCase() + "_curve")) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/StaticFLYSArtifact.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,135 @@
+package org.dive4elements.river.artifacts;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import org.dive4elements.artifacts.ArtifactNamespaceContext;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.artifactdatabase.data.StateData;
+import org.dive4elements.artifactdatabase.ProtocolUtils;
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifactdatabase.state.Output;
+import org.dive4elements.artifactdatabase.state.State;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator;
+
+/**
+ * A basic FLYSArtifact.
+ */
+public abstract class StaticFLYSArtifact extends FLYSArtifact {
+
+    /** Private logger. */
+    private static final Logger logger =
+        Logger.getLogger(StaticFLYSArtifact.class);
+
+    /** Path to 'ids' (data) in doc that comes from datacage. */
+    public static final String XPATH_IDS = "/art:action/art:ids/@value";
+
+    /**
+     * Create description document which includes outputmodes.
+     * @param data ignored.
+     */
+    @Override
+    public Document describe(Document data, CallContext cc) {
+        logger.debug("Describe artifact: " + identifier());
+
+        Document desc = XMLUtils.newDocument();
+
+        ElementCreator creator = new ElementCreator(
+            desc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element root = ProtocolUtils.createRootNode(creator);
+        desc.appendChild(root);
+
+        Element name = ProtocolUtils.createArtNode(
+            creator, "name",
+            new String[] { "value" },
+            new String[] { getName() });
+
+        root.appendChild(name);
+
+        ProtocolUtils.appendDescribeHeader(creator, root, identifier(), hash());
+        root.appendChild(createOutputModes(cc, desc, creator));
+
+        // Add the data to an anonymous state.
+        Collection<StateData> datas = getAllData();
+        if (datas.size() > 0) {
+            Element ui = creator.create("ui");
+            Element staticE = creator.create("static");
+            Element state = creator.create("state");
+            ui.appendChild(staticE);
+            staticE.appendChild(state);
+            root.appendChild(ui);
+
+            for (StateData dataItem : datas) {
+                Element itemelent = creator.create("data");
+                creator.addAttr(itemelent, "name", dataItem.getName(), true);
+                creator.addAttr(itemelent, "type", dataItem.getType(), true);
+                state.appendChild(itemelent);
+                Element valuement = creator.create("item");
+                creator.addAttr(valuement, "label", dataItem.getDescription(), true);
+                creator.addAttr(valuement, "value", dataItem.getValue().toString(), true);
+                itemelent.appendChild(valuement);
+            }
+        }
+
+        return desc;
+    }
+
+
+    /**
+     * Return the value of id element in Datacage data document.
+     * @param data Document as passed by datacage.
+     * @return the id element value of data document.
+     */
+    public static String getDatacageIDValue(Document data) {
+        return XMLUtils.xpathString(data, XPATH_IDS,
+            ArtifactNamespaceContext.INSTANCE);
+    }
+
+
+    protected Element createOutputModes(
+        CallContext    cc,
+        Document       doc,
+        ElementCreator creator)
+    {
+        logger.debug("createOutputModes");
+
+        Element outs = ProtocolUtils.createArtNode(
+            creator, "outputmodes", null, null);
+
+        State state       = getCurrentState(cc);
+
+        logger.debug("Current state is " + state.getID());
+
+        List<Output> list = state.getOutputs();
+
+        if (list != null && list.size() > 0) {
+            List<Facet> fs = getFacets(state.getID());
+            if (fs != null && fs.size() > 0) {
+                List<Output> generated = generateOutputs(list, fs);
+
+                logger.debug("Found " + fs.size() + " current facets.");
+                if (!generated.isEmpty()) {
+                    ProtocolUtils.appendOutputModes(
+                        doc, outs, generated);
+                }
+            }
+            else {
+                logger.debug("No facets found for the current state.");
+            }
+        }
+
+        return outs;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/StaticMorphWidthArtifact.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,108 @@
+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.artifacts.ArtifactFactory;
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+import org.dive4elements.river.artifacts.model.minfo.MorphologicWidthFacet;
+import org.dive4elements.river.artifacts.resources.Resources;
+import org.dive4elements.river.artifacts.states.StaticState;
+
+public class StaticMorphWidthArtifact
+extends      AbstractStaticStateArtifact
+{
+    /** The logger for this class. */
+    private static Logger logger =
+        Logger.getLogger(StaticMorphWidthArtifact.class);
+
+    private static final String NAME = "morph-width";
+    private static final String STATIC_FACET_NAME = "morph-width";
+
+    static {
+        // TODO: Move to configuration.
+        FacetActivity.Registry.getInstance()
+            .register(NAME, FacetActivity.INACTIVE);
+    }
+
+    public static final String STATIC_STATE_NAME =
+        "state.morph-width.static";
+
+    /**
+     * Trivial Constructor.
+     */
+    public StaticMorphWidthArtifact() {
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    /**
+     * Gets called from factory, to set things up.
+     */
+    @Override
+    public void setup(
+        String          identifier,
+        ArtifactFactory factory,
+        Object          context,
+        CallMeta        callMeta,
+        Document        data)
+    {
+        logger.debug("setup");
+
+        if (logger.isDebugEnabled()) {
+            logger.debug(XMLUtils.toString(data));
+        }
+
+        String code = getDatacageIDValue(data);
+
+        if (code != null) {
+                Facet facet = new MorphologicWidthFacet(
+                        STATIC_FACET_NAME,
+                        Resources.getMsg(
+                            callMeta,
+                            "facet.morphologic.width",
+                            "morphologische Breite"));
+                addStringData("width_id", code);
+                ArrayList<Facet> facets = new ArrayList<Facet>(1);
+                facets.add(facet);
+
+                addFacets(STATIC_STATE_NAME, facets);
+        }
+        super.setup(identifier, factory, context, callMeta, data);
+    }
+
+    @Override
+    protected void initStaticState() {
+
+        logger.debug("initStaticState " + getName() + " " + identifier());
+
+        StaticState state = new StaticState(STATIC_STATE_NAME);
+        DefaultOutput output = new DefaultOutput(
+                "general",
+                "general",
+                "image/png",
+                "chart");
+
+        List<Facet> facets = getFacets(STATIC_STATE_NAME);
+        output.addFacets(facets);
+        state.addOutput(output);
+
+        setStaticState(state);
+    }
+
+    @Override
+    protected void initialize(Artifact artifact, Object context, CallMeta meta) {
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/StaticWKmsArtifact.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,427 @@
+package org.dive4elements.river.artifacts;
+
+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.CallContext;
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+
+import org.dive4elements.river.artifacts.geom.Lines;
+
+import org.dive4elements.river.artifacts.math.Distance;
+import org.dive4elements.river.artifacts.math.Linear;
+
+import org.dive4elements.river.artifacts.model.CrossSectionWaterLineFacet;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.RelativePointFacet;
+import org.dive4elements.river.artifacts.model.WKms;
+import org.dive4elements.river.artifacts.model.WKmsFacet;
+import org.dive4elements.river.artifacts.model.WKmsFactory;
+
+import org.dive4elements.river.artifacts.resources.Resources;
+
+import org.dive4elements.river.artifacts.states.StaticState;
+
+import org.dive4elements.river.model.FastCrossSectionLine;
+
+import java.awt.geom.Point2D;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+
+/**
+ * Artifact to access additional "waterlevel"-type of data, like the height
+ * of protective measures (dikes).
+ *
+ * This artifact neglects (Static)FLYSArtifacts capabilities of interaction
+ * with the StateEngine by overriding the getState*-methods.
+ */
+public class StaticWKmsArtifact
+extends      StaticFLYSArtifact
+implements   FacetTypes, WaterLineArtifact
+{
+    /** The logger for this class. */
+    private static Logger logger =
+        Logger.getLogger(StaticWKmsArtifact.class);
+
+    private static final String NAME = "staticwkms";
+
+    static {
+        // TODO: Move to configuration.
+        FacetActivity.Registry.getInstance()
+            .register(NAME, FacetActivity.INACTIVE);
+    }
+
+    public static final String STATIC_STATE_NAME =
+        "state.additional_wkms.static";
+
+    /** Data Item name to know whether we are Heighmarks and reveive
+     * some data slightly different. */
+    public static final String DATA_HEIGHT_TYPE =
+        "height_marks";
+
+    /** One and only state to be in. */
+    protected transient State state = null;
+
+
+    /**
+     * Trivial Constructor.
+     */
+    public StaticWKmsArtifact() {
+        logger.debug("StaticWKmsArtifact.StaticWKmsArtifact");
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    /**
+     * Gets called from factory, to set things up.
+     */
+    @Override
+    public void setup(
+        String          identifier,
+        ArtifactFactory factory,
+        Object          context,
+        CallMeta        callMeta,
+        Document        data)
+    {
+        logger.debug("StaticWKmsArtifact.setup");
+
+        state = new StaticState(STATIC_STATE_NAME);
+
+        if (logger.isDebugEnabled()) {
+            logger.debug(XMLUtils.toString(data));
+        }
+
+        List<Facet> fs = new ArrayList<Facet>();
+        String code = getDatacageIDValue(data);
+
+        // TODO Go for JSON, one day.
+        //ex.: flood_protection-wstv-114-12
+        if (code != null) {
+            String [] parts = code.split("-");
+
+            if (parts.length >= 4) {
+                int col = -1;
+                int wst = Integer.parseInt(parts[3]);
+
+                if (!parts[2].equals("A")) {
+                    col = Integer.parseInt(parts[2]);
+                }
+
+                addStringData("col_pos", parts[2]);
+                addStringData("wst_id",  parts[3]);
+
+                String wkmsName;
+                if (col >= 0) {
+                    wkmsName = WKmsFactory.getWKmsName(col, wst);
+                }
+                else {
+                    wkmsName = WKmsFactory.getWKmsName(wst);
+                }
+
+                String name;
+                if (parts[0].equals(HEIGHTMARKS_POINTS)) {
+                    name = HEIGHTMARKS_POINTS;
+                    addStringData(DATA_HEIGHT_TYPE, "true");
+                }
+                else if (parts[0].equals("additionalsmarks")) {
+                    name = STATIC_WKMS_MARKS;
+                }
+                else {
+                    name = STATIC_WKMS;
+                }
+
+                String facetDescription = Resources.getMsg(
+                    callMeta, wkmsName, wkmsName);
+                Facet wKmsFacet = new WKmsFacet(
+                    name,
+                    facetDescription);
+                Facet csFacet = new CrossSectionWaterLineFacet(0,
+                    facetDescription);
+                Facet rpFacet = new RelativePointFacet(facetDescription);
+
+                fs.add(wKmsFacet);
+                fs.add(csFacet);
+                fs.add(rpFacet);
+                addFacets(state.getID(), fs);
+            }
+        }
+
+        spawnState();
+        super.setup(identifier, factory, context, callMeta, data);
+    }
+
+
+    /**
+     * Initialize the static state with output.
+     * @return static state
+     */
+    protected State spawnState() {
+        state = new StaticState(STATIC_STATE_NAME);
+        List<Facet> fs = getFacets(STATIC_STATE_NAME);
+        DefaultOutput output = new DefaultOutput(
+            "general",
+            "general", "image/png",
+            fs,
+            "chart");
+
+        state.getOutputs().add(output);
+        return state;
+    }
+
+
+    /**
+     * Called via setup.
+     *
+     * @param artifact The master-artifact.
+     */
+    @Override
+    protected void initialize(
+        Artifact artifact,
+        Object context,
+        CallMeta meta)
+    {
+        logger.debug("StaticWKmsArtifact.initialize");
+        FLYSArtifact winfo = (FLYSArtifact) artifact;
+        // TODO: The river is of no interest, so far.
+        addData("river", winfo.getData("river"));
+    }
+
+
+    /**
+     * 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();
+    }
+
+
+    /**
+     * Get WKms from factory.
+     * @param idx param is not needed (TODO?)
+     * @return WKms according to parameterization (can be null);
+     */
+    public WKms getWKms(int idx) {
+        logger.debug("StaticWKmsArtifact.getWKms");
+
+        return WKmsFactory.getWKms(
+            Integer.parseInt(getDataAsString("col_pos")),
+            Integer.parseInt(getDataAsString("wst_id")));
+    }
+
+
+    /**
+     * Returns W at Km of WKms, linearly interpolated.
+     * Returns -1 if not found.
+     */
+    public static double getWAtKmLin(WKms wkms, double km) {
+        // Uninformed search.
+        int size = wkms.size();
+        if (size == 0) {
+            return -1;
+        }
+        int idx = 0;
+
+        boolean kmIncreasing;
+        if (size == 1) {
+            kmIncreasing = true;
+        }
+        else {
+            kmIncreasing = (wkms.getKm(0) < wkms.getKm(wkms.size()-1))
+                ? true : false;
+        }
+        if (kmIncreasing) {
+            while (idx < size && wkms.getKm(idx) < km) {
+                idx++;
+            }
+        }
+        else {
+            idx = wkms.size() -1;
+            while (idx > 0 && wkms.getKm(idx) > km) {
+                idx--;
+            }
+        }
+
+       if (wkms.getKm(idx) == km) {
+           return wkms.getW(idx);
+       }
+
+        if (idx == size -1 || idx == 0) {
+            return -1;
+        }
+
+        // Do linear interpolation
+        int mod = kmIncreasing ? -1 : +1;
+        return Linear.linear(km, wkms.getKm(idx+mod), wkms.getKm(idx), wkms.getW(idx+mod), wkms.getW(idx));
+    }
+
+
+    /**
+     * Get the W at a specific km, only if it is closer to km than to any of
+     * the other given km.
+     * Return Double.NaN otherwise
+     *
+     * @param wkms WKms in which to search for a spatially close W value.
+     * @param km the input km, which is compared to values from wkms.
+     * @param next the next available input km (-1 if unavailable).
+     * @param prev the previous available input km (-1 if unavailable).
+     *
+     * @return W in wkms that is closer to km than to next and prev, or Double.NaN.
+     */
+    public double getWAtCloseKm(WKms wkms, double km, double next, double prev) {
+        // TODO symbolic "-1" pr next/prev is a bad idea (tm), as we compare
+        //      distances to these values later.
+        // TODO issue888
+
+        int size = wkms.size();
+        for (int i = 0; i < size; i++) {
+            double wkmsKm = wkms.getKm(i);
+            double dist = Distance.distance(wkmsKm, km);
+            if (dist == 0d) {
+                return wkms.getW(i);
+            }
+
+            // Problematic Cases:
+            // X == km , | and | == prev and next, (?) == wkmsKm
+            //
+            // Standard case:
+            // ----------|----X-----|-------
+            //     (1)    (2)    (3)   (4)
+            //
+            // With prev==-1
+            // -1 ------X-------|------
+            //    (5)      (6)     (7)
+            //
+            // With next==-1
+            //
+            // ---|-----X----- -1
+            // (8)  (9)   (10)
+
+            if (dist <= Distance.distance(wkmsKm, prev)
+                && dist <= Distance.distance(wkmsKm, next)) {
+                return wkms.getW(i);
+            }
+        }
+
+        return Double.NaN;
+    }
+
+
+    /**
+     * Returns W at Km of WKms, searching linearly.
+     * Returns -1 if not found.
+     * @param wkms the WKms object to search for given km.
+     * @param km The searched km.
+     * @return W at given km if in WKms, -1 if not found.
+     */
+    public static double getWAtKm(WKms wkms, double km) {
+        // Uninformed search, intolerant.
+        double TOLERANCE = 0.0d;
+        int size = wkms.size();
+        for (int i = 0; i < size; i++) {
+            if (Distance.within(wkms.getKm(i), km, TOLERANCE)) {
+                return wkms.getW(i);
+            }
+        }
+
+        return -1;
+    }
+
+
+    /**
+     * Get points of line describing the surface of water at cross section.
+     *
+     * @param idx Index of facet and in wkms array.
+     * @param csl FastCrossSectionLine to compute water surface agains.
+     * @param next The km of the next crosssectionline.
+     * @param prev The km of the previous crosssectionline.
+     * @param context Ignored in this implementation.
+     *
+     * @return an array holding coordinates of points of surface of water (
+     *         in the form {{x1, x2}, {y1, y2}} ).
+     */
+    @Override
+    public Lines.LineData getWaterLines(int idx, FastCrossSectionLine csl,
+        double next, double prev, CallContext context
+    ) {
+        logger.debug("getWaterLines(" + idx + ")/" + identifier());
+
+        List<Point2D> points = csl.getPoints();
+
+        WKms wkms = getWKms(0);
+
+        double km = csl.getKm();
+
+        // Find W at km.
+        double wAtKm;
+
+        // If heightmarks, only deliver if data snaps.
+        if (getDataAsString(DATA_HEIGHT_TYPE) != null &&
+            getDataAsString(DATA_HEIGHT_TYPE).equals("true")) {
+            wAtKm = getWAtCloseKm(wkms, km, next, prev);
+        }
+        else {
+            wAtKm = getWAtKm(wkms, km);
+        }
+
+        if (wAtKm == -1 || Double.isNaN(wAtKm)) {
+            logger.warn("Waterlevel at km " + km + " unknown.");
+            return new Lines.LineData(new double[][] {{}}, 0d, 0d);
+        }
+
+        return Lines.createWaterLines(points, wAtKm);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/StaticWQKmsArtifact.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,209 @@
+package org.dive4elements.river.artifacts;
+
+import java.awt.geom.Point2D;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifactdatabase.state.FacetActivity;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.ArtifactFactory;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+
+import org.dive4elements.river.artifacts.geom.Lines;
+
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.model.FastCrossSectionLine;
+import org.dive4elements.river.artifacts.model.WKms;
+import org.dive4elements.river.artifacts.model.WQKms;
+import org.dive4elements.river.artifacts.model.WKmsFactory;
+import org.dive4elements.river.artifacts.model.WQKmsFacet;
+import org.dive4elements.river.artifacts.model.WQKmsFactory;
+
+import org.dive4elements.river.artifacts.states.DefaultState;
+
+
+/**
+ * Artifact to access additional "waterlevel/discharge"-type of data, like
+ * fixation measurements.
+ *
+ * This artifact neglects (Static)FLYSArtifacts capabilities of interaction
+ * with the StateEngine by overriding the getState*-methods.
+ */
+public class StaticWQKmsArtifact
+extends      StaticFLYSArtifact
+implements   FacetTypes, WaterLineArtifact
+{
+    /** The logger for this class. */
+    private static Logger logger =
+        Logger.getLogger(StaticWQKmsArtifact.class);
+
+    public static final String STATIC_STATE_NAME =
+        "state.additional_wqkms.static";
+
+    private static final String NAME = "staticwqkms";
+
+    static {
+        // TODO: Move to configuration.
+        FacetActivity.Registry.getInstance()
+            .register(NAME, FacetActivity.INACTIVE);
+    }
+
+    /**
+     * Trivial Constructor.
+     */
+    public StaticWQKmsArtifact() {
+        logger.debug("StaticWQKmsArtifact.StaticWQKmsArtifact");
+    }
+
+
+    /**
+     * Gets called from factory, to set things up.
+     */
+    @Override
+    public void setup(
+        String          identifier,
+        ArtifactFactory factory,
+        Object          context,
+        CallMeta        callMeta,
+        Document        data)
+    {
+        logger.debug("StaticWQKmsArtifact.setup");
+
+        // Store the 'ids' (from datacage).
+        if (logger.isDebugEnabled()) {
+            logger.debug("StaticWQKmsArtiact.setup" + XMLUtils.toString(data));
+        }
+
+        String code = getDatacageIDValue(data);
+        addStringData("ids", code);
+        if (code != null) {
+            String [] parts = code.split("-");
+
+            if (parts.length >= 4) {
+                int col = Integer.parseInt(parts[2]);
+                int wst = Integer.parseInt(parts[3]);
+
+                addStringData("col_pos", parts[2]);
+                addStringData("wst_id",  parts[3]);
+            }
+        }
+
+        // Do this AFTER we have set the col_pos etc.
+        super.setup(identifier, factory, context, callMeta, data);
+    }
+
+
+    /**
+     * Called via setup.
+     *
+     * @param artifact The master-artifact.
+     */
+    @Override
+    protected void initialize(
+        Artifact artifact,
+        Object context,
+        CallMeta meta)
+    {
+        logger.debug("StaticWQKmsArtifact.initialize");
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+        // TODO: The river is of no interest, so far., also use importData
+        importData(flys, "river");
+
+        List<Facet> fs = new ArrayList<Facet>();
+
+        DefaultState state = (DefaultState) getCurrentState(context);
+        state.computeInit(this, hash(), context, meta, fs);
+        if (!fs.isEmpty()) {
+            logger.debug("Facets to add in StaticWQKmsArtifact.initialize .");
+            addFacets(getCurrentStateId(), fs);
+        }
+        else {
+            logger.debug("No facets to add in StaticWQKmsArtifact.initialize ("
+                + state.getID() + ").");
+        }
+    }
+
+
+    /**
+     * Get WQKms from factory.
+     * @return WQKms according to parameterization (can be null);
+     */
+    public WQKms getWQKms() {
+        logger.debug("StaticWQKmsArtifact.getWQKms");
+
+        int col = Integer.parseInt(getDataAsString("col_pos"));
+        int wst = Integer.parseInt(getDataAsString("wst_id"));
+
+        /** TODO do not run twice against db to do this. */
+        String wkmsName = WKmsFactory.getWKmsName(col, wst);
+
+        WQKms res = WQKmsFactory.getWQKms(col, wst);
+        res.setName(wkmsName);
+        return res;
+    }
+
+    /** Return specific name. */
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+
+    /**
+     * Get points of line describing the surface of water at cross section.
+     *
+     * @param idx Index of facet and in wkms array.
+     * @param csl FastCrossSectionLine to compute water surface agains.
+     * @param next The km of the next crosssectionline.
+     * @param prev The km of the previous crosssectionline.
+     * @param context Ignored in this implementation.
+     *
+     * @return an array holding coordinates of points of surface of water (
+     *         in the form {{x1, x2}, {y1, y2}} ).
+     */
+    @Override
+    public Lines.LineData getWaterLines(int idx, FastCrossSectionLine csl,
+        double next, double prev, CallContext context
+    ) {
+        logger.debug("getWaterLines(" + idx + ")/" + identifier());
+
+        List<Point2D> points = csl.getPoints();
+
+        WKms wkms = getWQKms();
+
+        double km = csl.getKm();
+
+        // Find W at km.
+        double wAtKm;
+
+        // If heightmarks, only deliver if data snaps.
+        /*
+        if (getDataAsString(DATA_HEIGHT_TYPE) != null &&
+            getDataAsString(DATA_HEIGHT_TYPE).equals("true")) {
+            wAtKm = getWAtCloseKm(wkms, km, next, prev);
+        }
+        else {
+        */
+            wAtKm = StaticWKmsArtifact.getWAtKm(wkms, km);
+        //}
+
+        if (wAtKm == -1 || Double.isNaN(wAtKm)) {
+            logger.warn("Waterlevel at km " + km + " unknown.");
+            return new Lines.LineData(new double[][] {{}}, 0d, 0d);
+        }
+
+        return Lines.createWaterLines(points, wAtKm);
+    }
+    // TODO implement deepCopy.
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/WINFOArtifact.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,1185 @@
+package org.dive4elements.river.artifacts;
+
+import org.dive4elements.artifactdatabase.data.StateData;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifactdatabase.state.FacetActivity;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.artifacts.common.utils.StringUtils;
+
+import org.dive4elements.river.artifacts.access.Calculation4Access;
+import org.dive4elements.river.artifacts.access.RangeAccess;
+
+import org.dive4elements.river.artifacts.geom.Lines;
+
+import org.dive4elements.river.artifacts.model.Calculation1;
+import org.dive4elements.river.artifacts.model.Calculation2;
+import org.dive4elements.river.artifacts.model.Calculation3;
+import org.dive4elements.river.artifacts.model.Calculation4;
+import org.dive4elements.river.artifacts.model.Calculation5;
+import org.dive4elements.river.artifacts.model.Calculation6;
+import org.dive4elements.river.artifacts.model.Calculation;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.DischargeTables;
+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.artifacts.model.WW;
+import org.dive4elements.river.artifacts.model.WstValueTable;
+import org.dive4elements.river.artifacts.model.WstValueTableFactory;
+
+import org.dive4elements.river.artifacts.model.extreme.ExtremeResult;
+
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+import org.dive4elements.river.artifacts.states.LocationDistanceSelect;
+
+import org.dive4elements.river.model.DischargeTable;
+import org.dive4elements.river.model.FastCrossSectionLine;
+import org.dive4elements.river.model.Gauge;
+import org.dive4elements.river.model.River;
+
+import org.dive4elements.river.utils.DoubleUtil;
+import org.dive4elements.river.utils.FLYSUtils;
+
+import gnu.trove.TDoubleArrayList;
+
+import java.awt.geom.Point2D;
+
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+
+
+/**
+ * The default WINFO artifact.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class WINFOArtifact
+extends      FLYSArtifact
+implements   FacetTypes, WaterLineArtifact {
+
+    /** The logger for this class. */
+    private static Logger logger = Logger.getLogger(WINFOArtifact.class);
+
+    /** The name of the artifact. */
+    public static final String ARTIFACT_NAME = "winfo";
+
+    /** XPath */
+    public static final String XPATH_STATIC_UI ="/art:result/art:ui/art:static";
+
+    /** The default number of steps between the start end end of a selected Q
+     * range. */
+    public static final int DEFAULT_Q_STEPS = 30;
+
+    /** The default step width between the start end end kilometer. */
+    public static final double DEFAULT_KM_STEPS = 0.1;
+
+    private static final String [] INACTIVES = new String[] {
+        LONGITUDINAL_Q,
+        DURATION_Q,
+        HISTORICAL_DISCHARGE_MAINVALUES_W,
+        HISTORICAL_DISCHARGE_MAINVALUES_Q
+    };
+
+    static {
+        // TODO: Move to configuration.
+        FacetActivity.Registry.getInstance().register(
+            ARTIFACT_NAME,
+            new FacetActivity() {
+                @Override
+                public Boolean isInitialActive(
+                    Artifact artifact,
+                    Facet    facet,
+                    String   outputName
+                ) {
+                    String fname = facet.getName();
+                    if ((fname.equals(COMPUTED_DISCHARGE_MAINVALUES_Q)
+                        || fname.equals(COMPUTED_DISCHARGE_MAINVALUES_W)
+                        || fname.equals(MAINVALUES_Q)
+                        || fname.equals(MAINVALUES_W))
+                        && outputName.equals("computed_discharge_curve"))
+                    {
+                        return Boolean.FALSE;
+                    }
+                    return !StringUtils.contains(fname, INACTIVES);
+                }
+            });
+    }
+
+    /**
+     * The default constructor.
+     */
+    public WINFOArtifact() {
+    }
+
+
+
+    /**
+     * Returns the name of the concrete artifact.
+     *
+     * @return the name of the concrete artifact.
+     */
+    @Override
+    public String getName() {
+        return ARTIFACT_NAME;
+    }
+
+    protected static boolean reportGeneratedWs(
+        Calculation report,
+        double []   ws
+    ) {
+        if (ws == null || ws.length < 2) {
+            return false;
+        }
+
+        double  lastW = ws[0];
+        boolean alreadyReported = false;
+
+        for (int i = 1; i < ws.length; ++i) {
+            if (Math.abs(lastW - ws[i]) < 1e-5) {
+                if (!alreadyReported) {
+                    alreadyReported = true;
+                    report.addProblem("more.than.one.q.for.w", ws[i]);
+                }
+            }
+            else {
+                alreadyReported = false;
+            }
+            lastW = ws[i];
+        }
+
+        return true;
+    }
+
+
+    //
+    // METHODS FOR RETRIEVING COMPUTED DATA FOR DIFFERENT CHART TYPES
+    //
+    //
+    /**
+     * Returns the data that is computed by a waterlevel computation.
+     *
+     * @return an array of data triples that consist of W, Q and Kms.
+     */
+    public CalculationResult getWaterlevelData() {
+        return this.getWaterlevelData(null);
+    }
+
+    // THIS IS FREAKY BULLSHIT! Felix, why do you call the calculation directly????
+    protected CalculationResult getDischargeLongitudinalSectionData() {
+        // XXX: THIS AN _EXPENSIVE_ CALCULATION! CACHE IT!
+        return new Calculation4(new Calculation4Access(this)).calculate();
+    }
+
+    /**
+     * Returns the data that is computed by a waterlevel computation.
+     *
+     * @return an array of data triples that consist of W, Q and Kms.
+     */
+    public CalculationResult getWaterlevelData(CallContext context)
+    {
+        logger.debug("WINFOArtifact.getWaterlevelData");
+
+        String calculationMode = getDataAsString("calculation_mode");
+
+        if (calculationMode.equals("calc.discharge.longitudinal.section")
+        ) {
+            return getDischargeLongitudinalSectionData();
+        }
+        else if (calculationMode.equals("calc.extreme.curve")) {
+            return (CalculationResult)
+                this.compute(context, ComputeType.ADVANCE, false);
+        }
+
+        River river = FLYSUtils.getRiver(this);
+        if (river == null) {
+            return error(new WQKms[0], "no.river.selected");
+        }
+
+        double[] kms = getKms();
+        if (kms == null) {
+            return error(new WQKms[0], "no.kms.selected");
+        }
+
+        double[] qs   = getQs();
+        double[] ws   = null;
+        boolean  qSel = true;
+
+        Calculation report = new Calculation();
+
+        if (qs == null) {
+            logger.debug("Determine Q values based on a set of W values.");
+            qSel = false;
+            ws   = getWs();
+            double [][] qws = getQsForWs(ws);
+            if (qws == null || qws.length == 0) {
+                return error(new WQKms[0], "converting.ws.to.qs.failed");
+            }
+            qs = qws[0];
+
+            if (reportGeneratedWs(report, qws[1])) {
+                ws = qws[1];
+            }
+        }
+
+        WstValueTable wst = WstValueTableFactory.getTable(river);
+        if (wst == null) {
+            return error(new WQKms[0], "no.wst.for.selected.river");
+        }
+
+        RangeAccess rangeAccess = new RangeAccess(this, null);
+        double [] range = rangeAccess.getKmRange();
+        if (range == null) {
+            return error(new WQKms[0], "no.range.found");
+        }
+
+        double refKm;
+
+        if (isFreeQ() || isFreeW()) {
+            refKm = range[0];
+            logger.debug("'free' calculation (km " + refKm + ")");
+        }
+        else {
+            Gauge gauge = river.determineGaugeByPosition(range[0]);
+            if (gauge == null) {
+                return error(
+                    new WQKms[0], "no.gauge.found.for.km", range[0]);
+            }
+
+            refKm = gauge.getStation().doubleValue();
+
+            logger.debug(
+                "reference gauge: " + gauge.getName() + " (km " + refKm + ")");
+        }
+
+        return computeWaterlevelData(kms, qs, ws, wst, refKm, report);
+    }
+
+
+    /**
+     * Computes the data of a waterlevel computation based on the interpolation
+     * in WstValueTable.
+     *
+     * @param kms The kilometer values.
+     * @param qs The discharge values.
+     * @param wst The WstValueTable used for the interpolation.
+     *
+     * @return an array of data triples that consist of W, Q and Kms.
+     */
+    public static CalculationResult computeWaterlevelData(
+        double []     kms,
+        double []     qs,
+        double []     ws,
+        WstValueTable wst,
+        double        refKm,
+        Calculation   report
+    ) {
+        logger.info("WINFOArtifact.computeWaterlevelData");
+
+        Calculation1 calc1 = new Calculation1(kms, qs, ws, refKm);
+
+        if (report != null) {
+            calc1.addProblems(report);
+        }
+
+        return calc1.calculate(wst);
+    }
+
+
+    /**
+     * Returns the data that is computed by a duration curve computation.
+     *
+     * @return the data computed by a duration curve computation.
+     */
+    public CalculationResult getDurationCurveData() {
+        logger.debug("WINFOArtifact.getDurationCurveData");
+
+        River r = FLYSUtils.getRiver(this);
+
+        if (r == null) {
+            return error(null, "no.river.selected");
+        }
+
+        Gauge g = getGauge();
+
+        if (g == null) {
+           return error(null, "no.gauge.selected");
+        }
+
+        RangeAccess rangeAccess = new RangeAccess(this, null);
+        double[] locations = rangeAccess.getLocations();
+
+        if (locations == null) {
+            return error(null, "no.locations.selected");
+        }
+
+        WstValueTable wst = WstValueTableFactory.getTable(r);
+        if (wst == null) {
+            return error(null, "no.wst.for.river");
+        }
+
+        return computeDurationCurveData(g, wst, locations[0]);
+    }
+
+
+    /**
+     * Computes the data used to create duration curves.
+     *
+     * @param gauge The selected gauge.
+     * @param location The selected location.
+     *
+     * @return the computed data.
+     */
+    public static CalculationResult computeDurationCurveData(
+        Gauge         gauge,
+        WstValueTable wst,
+        double        location)
+    {
+        logger.info("WINFOArtifact.computeDurationCurveData");
+
+        Object[] obj = gauge.fetchDurationCurveData();
+
+        int[]    days = (int[]) obj[0];
+        double[] qs   = (double[]) obj[1];
+
+        Calculation3 calculation = new Calculation3(location, days, qs);
+
+        return calculation.calculate(wst);
+    }
+
+
+    /**
+     * Returns the data that is computed by a discharge curve computation.
+     *
+     * @return the data computed by a discharge curve computation.
+     */
+    public CalculationResult getComputedDischargeCurveData()
+    throws NullPointerException
+    {
+        logger.debug("WINFOArtifact.getComputedDischargeCurveData");
+
+        River r = FLYSUtils.getRiver(this);
+
+        if (r == null) {
+            return error(new WQKms[0], "no.river.selected");
+        }
+
+        RangeAccess rangeAccess = new RangeAccess(this, null);
+        double[] locations = rangeAccess.getLocations();
+
+        if (locations == null) {
+            return error(new WQKms[0], "no.locations.selected");
+        }
+
+        WstValueTable wst = WstValueTableFactory.getTable(r);
+        if (wst == null) {
+            return error(new WQKms[0], "no.wst.for.river");
+        }
+
+        return computeDischargeCurveData(wst, locations[0]);
+    }
+
+
+    /**
+     * Computes the data used to create computed discharge curves.
+     *
+     * @param wst The WstValueTable that is used for the interpolation (river-
+     *            bound).
+     * @param location The location where the computation should be based on.
+     *
+     * @return an object that contains tuples of W/Q values at the specified
+     * location.
+     */
+    public static CalculationResult computeDischargeCurveData(
+        WstValueTable wst,
+        double location)
+    {
+        logger.info("WINFOArtifact.computeDischargeCurveData");
+
+        Calculation2 calculation = new Calculation2(location);
+
+        return calculation.calculate(wst);
+    }
+
+
+    /** Create CalculationResult with data and message. */
+    protected static final CalculationResult error(Object data, String msg) {
+        return new CalculationResult(data, new Calculation(msg));
+    }
+
+    /** Create CalculationResult with data and message with args. */
+    protected static final CalculationResult error(Object data, String msg, Object ... args) {
+        return new CalculationResult(data, new Calculation(msg, args));
+    }
+
+
+    /**
+     * Returns the data that is computed by a reference curve computation.
+     *
+     * @return the data computed by a reference curve computation.
+     */
+    public CalculationResult getReferenceCurveData(CallContext context) {
+
+        Double startKm = getReferenceStartKm();
+
+        if (startKm == null) {
+            return error(new WW[0], "no.reference.start.km");
+        }
+
+        double [] endKms = getReferenceEndKms();
+
+        if (endKms == null || endKms.length == 0) {
+            return error(new WW[0], "no.reference.end.kms");
+        }
+
+        Calculation5 calc5 = new Calculation5(startKm, endKms);
+
+        River r = FLYSUtils.getRiver(this);
+        if (r == null) {
+            return error(new WW[0], "no.river.found");
+        }
+
+        WstValueTable wst = WstValueTableFactory.getTable(r);
+        if (wst == null) {
+            return error(new WW[0], "no.wst.for.river");
+        }
+
+        Map<Double, Double> kms2gaugeDatums = r.queryGaugeDatumsKMs();
+
+        return calc5.calculate(wst, kms2gaugeDatums, context);
+    }
+
+
+    /** Get reference (start) km. */
+    public Double getReferenceStartKm() {
+        StateData sd = getData("reference_startpoint");
+
+        if (sd == null) {
+            logger.warn("no reference start given.");
+            return null;
+        }
+
+        logger.debug("Reference start km given: " + sd.getValue());
+
+        String input = (String) sd.getValue();
+
+        if (input == null || (input = input.trim()).length() == 0) {
+            logger.warn("reference start string is empty.");
+            return null;
+        }
+
+        try {
+            return Double.valueOf(input);
+        }
+        catch (NumberFormatException nfe) {
+            logger.warn("reference start string is not numeric.");
+        }
+
+        return null;
+    }
+
+
+    /**
+     * Get end kms for reference curve (null if none).
+     */
+    public double [] getReferenceEndKms() {
+        StateData sd = getData("reference_endpoint");
+
+        if (sd == null) {
+            logger.warn("no reference end given.");
+            return null;
+        }
+        else {
+            logger.debug("Reference end km : " + sd.getValue());
+        }
+
+        String input = (String) sd.getValue();
+
+        if (input == null || (input = input.trim()).length() == 0) {
+            logger.warn("reference end string is empty.");
+            return null;
+        }
+
+        TDoubleArrayList endKms = new TDoubleArrayList();
+
+        for (String part: input.split("\\s+")) {
+            try {
+                double km = Double.parseDouble(part);
+                if (!endKms.contains(km)) {
+                    endKms.add(km);
+                }
+            }
+            catch (NumberFormatException nfe) {
+                logger.warn("reference end string is not numeric.");
+            }
+        }
+
+        return endKms.toNativeArray();
+    }
+
+
+    public CalculationResult getHistoricalDischargeData() {
+        Gauge  gauge        = FLYSUtils.getReferenceGauge(this);
+        String rawTimerange = getDataAsString("year_range");
+        String rawValues    = getDataAsString("historical_values");
+        int    mode         = getDataAsInteger("historical_mode");
+
+        long[]   timerange = FLYSUtils.longArrayFromString(rawTimerange);
+        double[] values    = FLYSUtils.doubleArrayFromString(rawValues);
+
+        Calendar start = new GregorianCalendar();
+        start.setTimeInMillis(timerange[0]);
+        Calendar end   = new GregorianCalendar();
+        end.setTimeInMillis(timerange[1]);
+
+        Calculation6 calc = new Calculation6(
+            mode,
+            new long[] { start.getTimeInMillis(), end.getTimeInMillis() },
+            values);
+
+        return calc.calculate(gauge);
+    }
+
+
+    /**
+     * Get corrected waterline against surface/profile.
+     */
+    public Lines.LineData waterLineC(int idx, FastCrossSectionLine csl) {
+        List<Point2D> points = csl.getPoints();
+
+        WQKms[] wqckms = (WQKms[])
+            getDischargeLongitudinalSectionData().getData();
+
+        // Find index of km.
+        double wishKM = csl.getKm();
+
+        // Find W/C at km, linear naive approach.
+        WQCKms triple = (WQCKms) wqckms[idx-1];
+
+        int old_idx = 0;
+
+        if (triple.size() == 0) {
+            logger.warn("Calculation of c/waterline is empty.");
+            return Lines.createWaterLines(points, 0.0f);
+        }
+
+        // Linear seach in WQKms for closest km.
+        double old_dist_wish = Math.abs(wishKM - triple.getKm(0));
+        double last_c = triple.getC(0);
+
+        for (int i = 0, T = triple.size(); i < T; i++) {
+            double diff = Math.abs(wishKM - triple.getKm(i));
+            if (diff > old_dist_wish) {
+                break;
+            }
+            last_c = triple.getC(i);
+            old_dist_wish = diff;
+        }
+
+        return Lines.createWaterLines(points, last_c);
+    }
+
+
+    /**
+     * Get points of line describing the surface of water at cross section.
+     *
+     * @param idx Index for getWaterlevelData.
+     * @param csl The profile/surface to fill with water.
+     * @param nextIgnored Ignored in this implementation of WaterLineArtifact.
+     * @param prevIgnored Ignored in this implementation of WaterLineArtifact.
+     *
+     * @return an array holding coordinates of points of surface of water (
+     *         in the form {{x1, x2} {y1, y2}} ).
+     */
+    @Override
+    public Lines.LineData getWaterLines(int idx, FastCrossSectionLine csl,
+        double nextIgnored, double prevIgnored, CallContext context) {
+        logger.debug("getWaterLines(" + idx + ")");
+
+        List<Point2D> points = csl.getPoints();
+
+        // Need W at km
+        Object waterlevelResult = getWaterlevelData(context).getData();
+        WQKms [] wqkms;
+
+        if (waterlevelResult instanceof ExtremeResult) {
+            wqkms = ((ExtremeResult) waterlevelResult).getWQKms();
+        }
+        else {
+            wqkms = (WQKms[]) waterlevelResult;
+        }
+
+        if (wqkms.length == 0) {
+            logger.error("No WQKms found.");
+            return Lines.createWaterLines(points, 0.0f);
+        }
+
+        if (wqkms.length <= idx) {
+            logger.error("getWaterLines() requested index ("
+                         + idx + " not found.");
+            return waterLineC(idx, csl);
+        }
+
+        // Find W at km, linear naive approach.
+        WQKms triple = wqkms[idx];
+
+        // Find index of km.
+        double wishKM = csl.getKm();
+
+        if (triple.size() == 0) {
+            logger.warn("Calculation of waterline is empty.");
+            return Lines.createWaterLines(points, 0.0f);
+        }
+
+        // Early abort if we would need to extrapolate.
+        int T = triple.size();
+        double max_km = triple.getKm(T-1), min_km = triple.getKm(0);
+        if (wishKM < min_km || wishKM > max_km) {
+            // TODO Does this have to be done in the other WaterlineArtifact
+            //      implementations, too?
+            logger.warn("Will not extrapolate waterlevels.");
+            return Lines.createWaterLines(points, 0.0f);
+        }
+
+        int old_idx = 0;
+
+        // Linear seach in WQKms for closest km.
+        double old_dist_wish = Math.abs(wishKM - triple.getKm(0));
+        double last_w = triple.getW(0);
+
+        for (int i = 0; i < T; i++) {
+            double diff = Math.abs(wishKM - triple.getKm(i));
+            if (diff > old_dist_wish) {
+                break;
+            }
+            last_w = triple.getW(i);
+            old_dist_wish = diff;
+        }
+
+        return Lines.createWaterLines(points, last_w);
+    }
+
+
+    /**
+     * Returns the Qs for a number of Ws. This method makes use of
+     * DischargeTables.getQForW().
+     *
+     * @param ws An array of W values.
+     *
+     * @return an array of Q values.
+     */
+    public double [][] getQsForWs(double[] ws) {
+
+        if (ws == null) {
+            logger.error("getQsForWs: ws == null");
+            return null;
+        }
+
+        boolean debug = logger.isDebugEnabled();
+
+        if (debug) {
+            logger.debug("FLYSArtifact.getQsForWs");
+        }
+
+        River r = FLYSUtils.getRiver(this);
+        if (r == null) {
+            logger.warn("no river found");
+            return null;
+        }
+
+        RangeAccess rangeAccess = new RangeAccess(this, null);
+        double [] range = rangeAccess.getKmRange();
+        if (range == null) {
+            logger.warn("no ranges found");
+            return null;
+        }
+
+        if (isFreeW()) {
+            logger.debug("Bezugslinienverfahren I: W auf freier Strecke");
+            // The simple case of the "Bezugslinienverfahren"
+            // "W auf freier Strecke".
+            WstValueTable wst = WstValueTableFactory.getTable(r);
+            if (wst == null) {
+                logger.warn("no wst value table found");
+                return null;
+            }
+            double km = range[0];
+
+            TDoubleArrayList outQs = new TDoubleArrayList(ws.length);
+            TDoubleArrayList outWs = new TDoubleArrayList(ws.length);
+
+            boolean generatedWs = false;
+
+            for (int i = 0; i < ws.length; ++i) {
+                double w = ws[i];
+                if (debug) {
+                    logger.debug("getQsForWs: lookup Q for W: " + w);
+                }
+                // There could bemore than one Q per W.
+                double [] qs = wst.findQsForW(km, w);
+                for (int j = 0; j < qs.length; ++j) {
+                    outWs.add(ws[i]);
+                    outQs.add(qs[j]);
+                }
+                generatedWs |= qs.length != 1;
+            }
+
+            if (debug) {
+                logger.debug("getQsForWs: number of Qs: " + outQs.size());
+            }
+
+            return new double [][] {
+                outQs.toNativeArray(),
+                generatedWs ? outWs.toNativeArray() : null };
+        }
+
+        if (debug) {
+            logger.debug("range: " + Arrays.toString(range));
+        }
+
+        Gauge g = r.determineGaugeByPosition(range[0]);
+        if (g == null) {
+            logger.warn("no gauge found for km: " + range[0]);
+            return null;
+        }
+
+        if (debug) {
+            logger.debug("convert w->q with gauge '" + g.getName() + "'");
+        }
+
+        DischargeTable dt = g.fetchMasterDischargeTable();
+
+        if (dt == null) {
+            logger.warn("No master discharge table found for gauge '"
+                + g.getName() + "'");
+            return null;
+        }
+
+        double [][] values = DischargeTables.loadDischargeTableValues(dt, 1);
+
+        TDoubleArrayList wsOut = new TDoubleArrayList(ws.length);
+        TDoubleArrayList qsOut = new TDoubleArrayList(ws.length);
+
+        boolean generatedWs = false;
+
+        for (int i = 0; i < ws.length; i++) {
+            if (Double.isNaN(ws[i])) {
+                logger.warn("W is NaN: ignored");
+                continue;
+            }
+            double w = ws[i] / 100d;
+            double [] qs = DischargeTables.getQsForW(values, w);
+
+            if (qs.length == 0) {
+                logger.warn("No Qs found for W = " + ws[i]);
+            }
+            else {
+                for (double q: qs) {
+                    wsOut.add(ws[i]);
+                    qsOut.add(q * 100d);
+                }
+            }
+            generatedWs |= qs.length != 1;
+        }
+
+        return new double [][] {
+            qsOut.toNativeArray(),
+            generatedWs ? wsOut.toNativeArray() : null
+        };
+    }
+
+
+    /**
+     * Determines the selected mode of distance/range input.
+     *
+     * @return true, if the range mode is selected otherwise false.
+     */
+    public boolean isRange() {
+        StateData mode = getData("ld_mode");
+
+        if (mode == null) {
+            logger.warn("No mode location/range chosen. Defaults to range.");
+            return true;
+        }
+
+        String value = (String) mode.getValue();
+
+        return value.equals("distance");
+    }
+
+
+    /**
+     * Returns the selected distance based on a given range (from, to).
+     *
+     * @param dFrom The StateData that contains the lower value.
+     * @param dTo The StateData that contains the upper value.
+     *
+     * @return the selected distance.
+     */
+    protected double[] getDistanceByRange(StateData dFrom, StateData dTo) {
+        double from = Double.parseDouble((String) dFrom.getValue());
+        double to   = Double.parseDouble((String) dTo.getValue());
+
+        return new double[] { from, to };
+    }
+
+
+    /**
+     * Returns the selected Kms.
+     *
+     * @param distance An 2dim array with [lower, upper] values.
+     *
+     * @return the selected Kms.
+     */
+    public double[] getKms(double[] distance) {
+        StateData dStep = getData("ld_step");
+
+        if (dStep == null) {
+            logger.warn("No step width given. Cannot compute Kms.");
+            return null;
+        }
+
+        double step = Double.parseDouble((String) dStep.getValue());
+
+        // transform step from 'm' into 'km'
+        step = step / 1000;
+
+        if (step == 0d) {
+            step = DEFAULT_KM_STEPS;
+        }
+
+        return DoubleUtil.explode(distance[0], distance[1], step);
+    }
+
+
+    /**
+     * Returns the selected Kms.
+     *
+     * @return the selected kms.
+     */
+    public double[] getKms() {
+        if (isRange()) {
+            RangeAccess rangeAccess = new RangeAccess(this, null);
+            double [] distance = rangeAccess.getKmRange();
+            return getKms(distance);
+
+        }
+        else {
+            return LocationDistanceSelect.getLocations(this);
+        }
+    }
+
+
+    public double [] getFromToStep() {
+        if (!isRange()) {
+            return null;
+        }
+        RangeAccess rangeAccess = new RangeAccess(this, null);
+        double [] fromTo = rangeAccess.getKmRange();
+
+        if (fromTo == null) {
+            return null;
+        }
+
+        StateData dStep = getData("ld_step");
+        if (dStep == null) {
+            return null;
+        }
+
+        double [] result = new double[3];
+        result[0] = fromTo[0];
+        result[1] = fromTo[1];
+
+        try {
+            String step = (String)dStep.getValue();
+            result[2] = DoubleUtil.round(Double.parseDouble(step) / 1000d);
+        }
+        catch (NumberFormatException nfe) {
+            return null;
+        }
+
+        return result;
+    }
+
+
+    /**
+     * Returns the gauge based on the current distance and river.
+     *
+     * @return the gauge.
+     */
+    public Gauge getGauge() {
+        return FLYSUtils.getGauge(this);
+    }
+
+
+
+
+    /**
+     * This method returns the Q values.
+     *
+     * @return the selected Q values or null, if no Q values are selected.
+     */
+    public double[] getQs() {
+        StateData dMode      = getData("wq_isq");
+        StateData dSelection = getData("wq_isrange");
+
+        boolean isRange = dSelection != null
+            ? Boolean.valueOf((String)dSelection.getValue())
+            : false;
+
+        if (isQ()) {
+            if (!isRange) {
+                return getSingleWQValues();
+            }
+            else {
+                return getWQTriple();
+            }
+        }
+        else {
+            logger.warn("You try to get Qs, but W has been inserted.");
+            return null;
+        }
+    }
+
+
+    public boolean isQ() {
+        StateData mode = getData("wq_isq");
+        String value = (mode != null) ? (String) mode.getValue() : null;
+        return value != null ? Boolean.valueOf(value) : false;
+    }
+
+    public boolean isW() {
+        StateData mode = getData("wq_isq");
+        String value = (mode != null) ? (String) mode.getValue() : null;
+        return value != null ? !Boolean.valueOf(value) : false;
+    }
+
+    public boolean isFreeW() {
+        if(!isW()) {
+            return false;
+        }
+        StateData mode = getData("wq_isfree");
+        String value =  (mode != null) ? (String) mode.getValue() : null;
+
+        return value != null ? Boolean.valueOf(value) : false;
+    }
+
+
+    /**
+     * Returns true, if the parameter is set to compute data on a free range.
+     * Otherwise it returns false, which tells the calculation that it is bound
+     * to a gauge.
+     *
+     * @return true, if the calculation should compute on a free range otherwise
+     * false and the calculation is bound to a gauge.
+     */
+    public boolean isFreeQ() {
+        if(!isQ()) {
+            return false;
+        }
+        StateData mode  = getData("wq_isfree");
+        String    value = (mode != null) ? (String) mode.getValue() : null;
+
+        logger.debug("isFreeQ: " + value);
+
+        return value != null && Boolean.valueOf(value);
+    }
+
+
+    /**
+     * Returns the Q values based on a specified kilometer range.
+     *
+     * @param range A 2dim array with lower and upper kilometer range.
+     *
+     * @return an array of Q values.
+     */
+    public double[] getQs(double[] range) {
+        StateData dMode   = getData("wq_isq");
+
+        if (isQ()) {
+            return getWQForDist(range);
+        }
+
+        logger.warn("You try to get Qs, but Ws has been inserted.");
+        return null;
+    }
+
+
+    /**
+     * Returns the W values based on a specified kilometer range.
+     *
+     * @param range A 2dim array with lower and upper kilometer range.
+     *
+     * @return an array of W values.
+     */
+    public double[] getWs(double[] range) {
+        if (isW()) {
+            return getWQForDist(range);
+        }
+
+        logger.warn("You try to get Ws, but Qs has been inserted.");
+        return null;
+    }
+
+
+    /**
+     * This method returns the W values.
+     *
+     * @return the selected W values or null, if no W values are selected.
+     */
+    public double[] getWs() {
+        if (isW()) {
+            StateData dSingle = getData("wq_single");
+            if (dSingle != null) {
+                return getSingleWQValues();
+            }
+            else {
+                return getWQTriple();
+            }
+        }
+        else {
+            logger.warn("You try to get Ws, but Q has been inserted.");
+            return null;
+        }
+    }
+
+    /**
+     * This method returns the given W or Q values for a specific range
+     * (inserted in the WQ input panel for discharge longitudinal sections).
+     *
+     * @param dist A 2dim array with lower und upper kilometer values.
+     *
+     * @return an array of W or Q values.
+     */
+    protected double[] getWQForDist(double[] dist) {
+        logger.debug("Search wq values for range: " + dist[0] + " - " + dist[1]);
+        StateData data = getData("wq_values");
+
+        if (data == null) {
+            logger.warn("Missing wq values!");
+            return null;
+        }
+
+        String dataString = (String) data.getValue();
+        String[]   ranges = dataString.split(":");
+
+        for (String range: ranges) {
+            String[] parts = range.split(";");
+
+            double lower = Double.parseDouble(parts[0]);
+            double upper = Double.parseDouble(parts[1]);
+
+            if (lower <= dist[0] && upper >= dist[1]) {
+                String[] values = parts[2].split(",");
+
+                int      num = values.length;
+                double[] res = new double[num];
+
+                for (int i = 0; i < num; i++) {
+                    try {
+                        res[i] = Double.parseDouble(values[i]);
+                    }
+                    catch (NumberFormatException nfe) {
+                        logger.warn(nfe, nfe);
+                    }
+                }
+
+                return res;
+            }
+        }
+
+        logger.warn("Specified range for WQ not found!");
+
+        return null;
+    }
+
+
+    /**
+     * This method returns an array of inserted WQ triples that consist of from,
+     * to and the step width.
+     *
+     * @return an array of from, to and step width.
+     */
+    protected double[] getWQTriple() {
+        StateData dFrom = getData("wq_from");
+        StateData dTo   = getData("wq_to");
+
+        if (dFrom == null || dTo == null) {
+            logger.warn("Missing start or end value for range.");
+            return null;
+        }
+
+        double from = Double.parseDouble((String) dFrom.getValue());
+        double to   = Double.parseDouble((String) dTo.getValue());
+
+        StateData dStep = getData("wq_step");
+
+        if (dStep == null) {
+            logger.warn("No step width given. Cannot compute Qs.");
+            return null;
+        }
+
+        double step  = Double.parseDouble((String) dStep.getValue());
+
+        // if no width is given, the DEFAULT_Q_STEPS is used to compute the step
+        // width. Maybe, we should round the value to a number of digits.
+        if (step == 0d) {
+            double diff = to - from;
+            step = diff / DEFAULT_Q_STEPS;
+        }
+
+        return DoubleUtil.explode(from, to, step);
+    }
+
+
+    /**
+     * Returns an array of inserted WQ double values stored as whitespace
+     * separated list.
+     *
+     * @return an array of W or Q values.
+     */
+    protected double[] getSingleWQValues() {
+        StateData dSingle = getData("wq_single");
+
+        if (dSingle == null) {
+            logger.warn("Cannot determine single WQ values. No data given.");
+            return null;
+        }
+
+        String   tmp       = (String) dSingle.getValue();
+        String[] strValues = tmp.split(" ");
+
+        TDoubleArrayList values = new TDoubleArrayList();
+
+        for (String strValue: strValues) {
+            try {
+                values.add(Double.parseDouble(strValue));
+            }
+            catch (NumberFormatException nfe) {
+                logger.warn(nfe, nfe);
+            }
+        }
+
+        values.sort();
+
+        return values.toNativeArray();
+    }
+
+    /**
+     * Returns the WstValueTable of current river.
+     */
+    public WstValueTable getWstValueTable() {
+        River r = FLYSUtils.getRiver(this);
+        return WstValueTableFactory.getTable(r);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/WMSBackgroundArtifact.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,48 @@
+package org.dive4elements.river.artifacts;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+
+import org.dive4elements.river.artifacts.states.DefaultState;
+
+
+public class WMSBackgroundArtifact extends StaticFLYSArtifact {
+
+    public static final String NAME = "wmsbackground";
+
+    private static final Logger logger =
+        Logger.getLogger(WMSBackgroundArtifact.class);
+
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+
+    @Override
+    protected void initialize(Artifact artifact, Object context, CallMeta meta) {
+        logger.debug("Initialize internal state with: "+ artifact.identifier());
+
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+        addData("river", flys.getData("river"));
+
+        List<Facet> fs = new ArrayList<Facet>();
+
+        // TODO Add CallMeta
+        DefaultState state = (DefaultState) getCurrentState(context);
+        state.computeInit(this, hash(), context, meta, fs);
+
+        if (!fs.isEmpty()) {
+            addFacets(getCurrentStateId(), fs);
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/WMSBuildingsArtifact.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,165 @@
+package org.dive4elements.river.artifacts;
+
+import java.util.List;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+import com.vividsolutions.jts.geom.Envelope;
+
+import org.dive4elements.artifacts.ArtifactFactory;
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.artifactdatabase.state.DefaultOutput;
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifactdatabase.state.State;
+
+import org.dive4elements.river.model.River;
+import org.dive4elements.river.model.Building;
+
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.RiverFactory;
+import org.dive4elements.river.utils.FLYSUtils;
+import org.dive4elements.river.utils.GeometryUtils;
+
+
+public class WMSBuildingsArtifact extends WMSDBArtifact {
+
+    public static final String NAME = "buildings";
+
+
+    private static final Logger logger =
+        Logger.getLogger(WMSBuildingsArtifact.class);
+
+
+    @Override
+    public void setup(
+        String          identifier,
+        ArtifactFactory factory,
+        Object          context,
+        CallMeta        callMeta,
+        Document        data)
+    {
+        logger.debug("WMSBuildingsArtifact.setup");
+
+        super.setup(identifier, factory, context, callMeta, data);
+    }
+
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+
+    @Override
+    public State getCurrentState(Object cc) {
+        State s = new BuildingsState(this);
+
+        List<Facet> fs = getFacets(getCurrentStateId());
+
+        DefaultOutput o = new DefaultOutput(
+            "floodmap",
+            "floodmap",
+            "image/png",
+            fs,
+            "map");
+
+        s.getOutputs().add(o);
+
+        return s;
+    }
+
+
+    public static class BuildingsState extends WMSDBState implements FacetTypes
+    {
+        private static final Logger logger =
+            Logger.getLogger(BuildingsState.class);
+
+        protected int riverId;
+
+        public BuildingsState(WMSDBArtifact artifact) {
+            super(artifact);
+            riverId = 0;
+        }
+
+        @Override
+        protected String getFacetType() {
+            return FLOODMAP_BUILDINGS;
+        }
+
+        @Override
+        protected String getUrl() {
+            return FLYSUtils.getUserWMSUrl(artifact.identifier());
+        }
+
+        @Override
+        protected String getSrid() {
+            River river = RiverFactory.getRiver(getRiverId());
+            return FLYSUtils.getRiverSrid(river.getName());
+        }
+
+        @Override
+        protected Envelope getExtent(boolean reproject) {
+            List<Building> buildings;
+            String kind = getIdPart(2);
+
+            if (kind != null) {
+                buildings = Building.getBuildings(getRiverId(), Integer.parseInt(kind));
+            } else {
+                buildings = Building.getBuildings(getRiverId(), name);
+            }
+
+            Envelope max = null;
+
+            for (Building b: buildings) {
+                Envelope env = b.getGeom().getEnvelopeInternal();
+
+                if (max == null) {
+                    max = env;
+                    continue;
+                }
+
+                max.expandToInclude(env);
+            }
+
+            return max != null && reproject
+                ? GeometryUtils.transform(max, getSrid())
+                : max;
+        }
+
+        @Override
+        protected String getFilter() {
+            // The expected id string is:
+            // river_id;layer-name;kind
+
+            String kind = getIdPart(2);
+            if (kind != null) {
+                // If kind is provided we filter by kind
+                return "river_id=" + String.valueOf(getRiverId()) +
+                    " AND kind_id=" + kind;
+            }
+            return "river_id=" + String.valueOf(getRiverId()) +
+                " AND name='" + getName() + "'";
+        }
+
+        @Override
+        protected String getDataString() {
+            String srid = getSrid();
+
+            if (FLYSUtils.isUsingOracle()) {
+                return "geom FROM buildings USING SRID " + srid;
+            }
+            else {
+                return "geom FROM buildings USING UNIQUE id USING SRID " + srid;
+            }
+        }
+
+        @Override
+        protected String getGeometryType() {
+            return "LINE";
+        }
+    } // end of WMSKmState
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/WMSDBArtifact.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,281 @@
+package org.dive4elements.river.artifacts;
+
+import com.vividsolutions.jts.geom.Envelope;
+
+import org.dive4elements.artifactdatabase.data.DefaultStateData;
+import org.dive4elements.artifactdatabase.state.Facet;
+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.FileTools;
+import org.dive4elements.river.artifacts.model.map.WMSDBLayerFacet;
+import org.dive4elements.river.artifacts.resources.Resources;
+import org.dive4elements.river.artifacts.states.DefaultState;
+import org.dive4elements.river.utils.FLYSUtils;
+import org.dive4elements.river.utils.MapUtils;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.w3c.dom.Document;
+
+
+public abstract class WMSDBArtifact extends StaticFLYSArtifact {
+
+    private static final Logger logger = Logger.getLogger(WMSDBArtifact.class);
+
+    @Override
+    public void setup(
+        String          identifier,
+        ArtifactFactory factory,
+        Object          context,
+        CallMeta        callMeta,
+        Document        data)
+    {
+        logger.debug("WMSDBArtifact.setup");
+
+        super.setup(identifier, factory, context, callMeta, data);
+
+        String ids = getDatacageIDValue(data);
+
+        if (ids != null && ids.length() > 0) {
+            addData("ids", new DefaultStateData("ids", null, null, ids));
+        }
+        else {
+            throw new IllegalArgumentException("No attribute 'ids' found!");
+        }
+
+        List<Facet> fs = new ArrayList<Facet>();
+
+        WMSDBState state = (WMSDBState) getCurrentState(context);
+        state.computeInit(this, hash(), context, callMeta, fs);
+
+        if (!fs.isEmpty()) {
+            addFacets(getCurrentStateId(), fs);
+        }
+    }
+
+
+    @Override
+    protected void initialize(
+        Artifact artifact,
+        Object   context,
+        CallMeta callMeta)
+    {
+        // do nothing
+    }
+
+
+    @Override
+    protected State getState(Object context, String stateID) {
+        return getCurrentState(context);
+    }
+
+
+    /**
+     * 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(getCurrentState(context));
+
+        return states;
+    }
+
+
+
+    public static abstract class WMSDBState extends DefaultState {
+        private static final Logger logger = Logger.getLogger(WMSDBState.class);
+
+        protected FLYSArtifact artifact;
+
+        protected String name;
+        protected int    riverId;
+
+
+        public WMSDBState() {}
+
+        public WMSDBState(FLYSArtifact artifact) {
+            this.artifact = artifact;
+            this.name     = null;
+            this.riverId  = 0;
+        }
+
+        @Override
+        public Object computeInit(
+            FLYSArtifact artifact,
+            String       hash,
+            Object       context,
+            CallMeta     meta,
+            List<Facet>  facets
+        ) {
+            logger.debug("WMSDBState.computeInit");
+
+            String type = getFacetType();
+
+            WMSDBLayerFacet facet = new WMSDBLayerFacet(
+                0,
+                type,
+                getTitle(meta),
+                ComputeType.INIT,
+                getID(), hash,
+                getUrl());
+
+            facet.addLayer(getLayer());
+            facet.setExtent(getExtent());
+            facet.setOriginalExtent(getExtent(true));
+            facet.setSrid(getSrid());
+            facet.setData(getDataString());
+            facet.setFilter(getFilter());
+            facet.setGeometryType(getGeometryType());
+            facet.setConnection(MapUtils.getConnection());
+            facet.setConnectionType(MapUtils.getConnectionType());
+            facet.setLabelItem(getLabelItem());
+
+            facets.add(facet);
+
+            return null;
+        }
+
+        protected String getLabelItem() {
+            return null;
+        }
+
+        public int getRiverId() {
+            if (riverId == 0) {
+                String rid = getIdPart(0);
+
+                try {
+                    riverId = Integer.parseInt(rid);
+                }
+                catch (NumberFormatException nfe) {
+                    logger.error("Cannot parse river id from '" +
+                            artifact.getDataAsString("ids") + "'");
+                }
+            }
+            return riverId;
+        }
+
+        protected String getLayer() {
+            String type = getFacetType();
+            String name = type + "-" + artifact.identifier();
+            return name;
+        }
+
+        /**
+         * Returns the name of the WMS layer. This method extracts the name
+         * from 'ids' data string. It is expected, that the 'ids' string is
+         * seperated by ';' and that the name is placed at index 1.
+         *
+         * @return the name of the WMS layer.
+         */
+        public String getName() {
+            if (name == null) {
+                name = getIdPart(1);
+            }
+
+            return name;
+        }
+
+        /**
+         * Returns a part of the ID string. This method splits the
+         * 'ids' data string. It is expected, that the 'ids' string is
+         * seperated by ';'.
+         *
+         * @param number the position of the id data string
+         *
+         * @return the part of the id string at position number.
+         *         Null if number was out of bounds.
+         */
+        public String getIdPart(int number) {
+            String ids = artifact.getDataAsString("ids");
+
+            String parts[] = ids != null ? ids.split(";") : null;
+
+            if (parts != null && parts.length >= number + 1) {
+                return parts[number];
+            }
+            return null;
+        }
+
+
+        /**
+         * Returns the name of the layer (returned by getName()) or the layer
+         * type if the name is empty. The layer type is created by an i18n
+         * string of getFacetType().
+         *
+         * @param meta A CallMeta used for i18n.
+         *
+         * @return the name of the layer or its type if name is empty.
+         */
+        protected String getTitle(CallMeta meta) {
+            String name = getName();
+
+            return name != null && name.length() > 0
+                ? name
+                : Resources.getMsg(
+                    meta,
+                    getFacetType(),
+                    getFacetType());
+        }
+
+
+        @Override
+        public void endOfLife(Artifact owner, Object context) {
+            logger.info("Destroy WMSDBState: " + getID());
+
+            String p = FLYSUtils.getXPathString(FLYSUtils.XPATH_FLOODMAP_SHAPEFILE_DIR);
+            File dir = new File(p, owner.identifier());
+
+            if (dir != null && dir.exists()) {
+                logger.debug("Try to delete directory '" + dir + "'");
+
+                FileTools.deleteRecursive(dir);
+            }
+        }
+
+        /**
+         * This method returns the extent of a DB layer in the projection of the
+         * database.
+         *
+         * @return the extent of the DB layer in the projection of the database.
+         */
+        protected Envelope getExtent() {
+            return getExtent(false);
+        }
+
+
+        protected abstract String getFacetType();
+
+        protected abstract String getUrl();
+
+        protected abstract String getSrid();
+
+        /**
+         * This method returns the extent of the DB layer. The projection of the
+         * extent depends on the <i>reproject</i> parameter. If reproject is set,
+         * the extent is reprojected into the original projection which is
+         * specified in the configuration. Otherwise, the projection of the
+         * database is used.
+         *
+         * @param reproject True, to reproject the extent into the projection
+         * specified in the configuration.
+         *
+         * @return the extent of the database layer.
+         */
+        protected abstract Envelope getExtent(boolean reproject);
+
+        protected abstract String getFilter();
+
+        protected abstract String getDataString();
+
+        protected abstract String getGeometryType();
+    } // end of WMSDBState
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/WMSFixpointsArtifact.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,148 @@
+package org.dive4elements.river.artifacts;
+
+import java.util.List;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+import com.vividsolutions.jts.geom.Envelope;
+
+import org.dive4elements.artifacts.ArtifactFactory;
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.artifactdatabase.state.DefaultOutput;
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifactdatabase.state.State;
+
+import org.dive4elements.river.model.River;
+import org.dive4elements.river.model.Fixpoint;
+
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.RiverFactory;
+import org.dive4elements.river.utils.FLYSUtils;
+import org.dive4elements.river.utils.GeometryUtils;
+
+
+public class WMSFixpointsArtifact extends WMSDBArtifact {
+
+    public static final String NAME = "fixpoints";
+
+
+    private static final Logger logger =
+        Logger.getLogger(WMSFixpointsArtifact.class);
+
+
+    @Override
+    public void setup(
+        String          identifier,
+        ArtifactFactory factory,
+        Object          context,
+        CallMeta        callMeta,
+        Document        data)
+    {
+        logger.debug("WMSFixpointsArtifact.setup");
+
+        super.setup(identifier, factory, context, callMeta, data);
+    }
+
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+
+    @Override
+    public State getCurrentState(Object cc) {
+        State s = new FixpointsState(this);
+
+        List<Facet> fs = getFacets(getCurrentStateId());
+
+        DefaultOutput o = new DefaultOutput(
+            "floodmap",
+            "floodmap",
+            "image/png",
+            fs,
+            "map");
+
+        s.getOutputs().add(o);
+
+        return s;
+    }
+
+
+    public static class FixpointsState extends WMSDBState implements FacetTypes
+    {
+        private static final Logger logger =
+            Logger.getLogger(FixpointsState.class);
+
+        protected int riverId;
+
+        public FixpointsState(WMSDBArtifact artifact) {
+            super(artifact);
+            riverId = 0;
+        }
+
+        @Override
+        protected String getFacetType() {
+            return FLOODMAP_FIXPOINTS;
+        }
+
+        @Override
+        protected String getUrl() {
+            return FLYSUtils.getUserWMSUrl(artifact.identifier());
+        }
+
+        @Override
+        protected String getSrid() {
+            River river = RiverFactory.getRiver(getRiverId());
+            return FLYSUtils.getRiverSrid(river.getName());
+        }
+
+        @Override
+        protected Envelope getExtent(boolean reproject) {
+            List<Fixpoint> fixpoints = Fixpoint.getFixpoints(getRiverId());
+
+            Envelope max = null;
+
+            for (Fixpoint f: fixpoints) {
+                Envelope env = f.getGeom().getEnvelopeInternal();
+
+                if (max == null) {
+                    max = env;
+                    continue;
+                }
+
+                max.expandToInclude(env);
+            }
+
+            return max != null && reproject
+                ? GeometryUtils.transform(max, getSrid())
+                : max;
+        }
+
+        @Override
+        protected String getFilter() {
+            return "river_id=" + String.valueOf(getRiverId());
+        }
+
+        @Override
+        protected String getDataString() {
+            String srid = getSrid();
+
+            if (FLYSUtils.isUsingOracle()) {
+                return "geom FROM fixpoints USING SRID " + srid;
+            }
+            else {
+                return "geom FROM fixpoints USING UNIQUE id USING SRID " + srid;
+            }
+        }
+
+        @Override
+        protected String getGeometryType() {
+            return "POINT";
+        }
+    } // end of WMSKmState
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/WMSFloodmapsArtifact.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,150 @@
+package org.dive4elements.river.artifacts;
+
+import java.util.List;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+import com.vividsolutions.jts.geom.Envelope;
+
+import org.dive4elements.artifacts.ArtifactFactory;
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.artifactdatabase.state.DefaultOutput;
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifactdatabase.state.State;
+
+import org.dive4elements.river.model.River;
+import org.dive4elements.river.model.Floodmaps;
+
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.RiverFactory;
+import org.dive4elements.river.utils.FLYSUtils;
+import org.dive4elements.river.utils.GeometryUtils;
+
+
+public class WMSFloodmapsArtifact extends WMSDBArtifact {
+
+    public static final String NAME = "floodmaps";
+
+
+    private static final Logger logger =
+        Logger.getLogger(WMSFloodmapsArtifact.class);
+
+
+    @Override
+    public void setup(
+        String          identifier,
+        ArtifactFactory factory,
+        Object          context,
+        CallMeta        callMeta,
+        Document        data)
+    {
+        logger.debug("WMSFloodmapsArtifact.setup");
+
+        super.setup(identifier, factory, context, callMeta, data);
+    }
+
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+
+    @Override
+    public State getCurrentState(Object cc) {
+        State s = new FloodmapsState(this);
+
+        List<Facet> fs = getFacets(getCurrentStateId());
+
+        DefaultOutput o = new DefaultOutput(
+            "floodmap",
+            "floodmap",
+            "image/png",
+            fs,
+            "map");
+
+        s.getOutputs().add(o);
+
+        return s;
+    }
+
+
+    public static class FloodmapsState extends WMSDBState implements FacetTypes
+    {
+        private static final Logger logger =
+            Logger.getLogger(FloodmapsState.class);
+
+        protected int    riverId;
+
+        public FloodmapsState(WMSDBArtifact artifact) {
+            super(artifact);
+            riverId      = 0;
+        }
+
+        @Override
+        protected String getFacetType() {
+            return FLOODMAP_FLOODMAPS;
+        }
+
+        @Override
+        protected String getUrl() {
+            return FLYSUtils.getUserWMSUrl(artifact.identifier());
+        }
+
+        @Override
+        protected String getSrid() {
+            River river = RiverFactory.getRiver(getRiverId());
+            return FLYSUtils.getRiverSrid(river.getName());
+        }
+
+        @Override
+        protected Envelope getExtent(boolean reproject) {
+            List<Floodmaps> floodmaps =
+                Floodmaps.getFloodmaps(getRiverId(), getName());
+
+            Envelope max = null;
+
+            for (Floodmaps f: floodmaps) {
+                Envelope env = f.getGeom().getEnvelopeInternal();
+
+                if (max == null) {
+                    max = env;
+                    continue;
+                }
+
+                max.expandToInclude(env);
+            }
+
+            return max != null && reproject
+                ? GeometryUtils.transform(max, getSrid())
+                : max;
+        }
+
+        @Override
+        protected String getFilter() {
+            return "river_id=" + String.valueOf(getRiverId())
+                + " AND name='" + getName() + "'";
+        }
+
+        @Override
+        protected String getDataString() {
+            String srid = getSrid();
+
+            if (FLYSUtils.isUsingOracle()) {
+                return "geom FROM floodmaps USING SRID " + srid;
+            }
+            else {
+                return "geom FROM floodmaps USING UNIQUE id USING SRID " + srid;
+            }
+        }
+
+        @Override
+        protected String getGeometryType() {
+            return "POLYGON";
+        }
+    } // end of WMSKmState
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/WMSFloodmarksArtifact.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,159 @@
+package org.dive4elements.river.artifacts;
+
+import java.util.List;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+import com.vividsolutions.jts.geom.Envelope;
+
+import org.dive4elements.artifacts.ArtifactFactory;
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.artifactdatabase.state.DefaultOutput;
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifactdatabase.state.State;
+
+import org.dive4elements.river.model.River;
+import org.dive4elements.river.model.Floodmark;
+
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.RiverFactory;
+import org.dive4elements.river.utils.FLYSUtils;
+import org.dive4elements.river.utils.GeometryUtils;
+
+
+public class WMSFloodmarksArtifact extends WMSDBArtifact {
+
+    public static final String NAME = "floodmarks";
+
+
+    private static final Logger logger =
+        Logger.getLogger(WMSFloodmarksArtifact.class);
+
+
+    @Override
+    public void setup(
+        String          identifier,
+        ArtifactFactory factory,
+        Object          context,
+        CallMeta        callMeta,
+        Document        data)
+    {
+        logger.debug("WMSFloodmarksArtifact.setup");
+
+        super.setup(identifier, factory, context, callMeta, data);
+    }
+
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+
+    @Override
+    public State getCurrentState(Object cc) {
+        State s = new FloodmarksState(this);
+
+        List<Facet> fs = getFacets(getCurrentStateId());
+
+        DefaultOutput o = new DefaultOutput(
+            "floodmap",
+            "floodmap",
+            "image/png",
+            fs,
+            "map");
+
+        s.getOutputs().add(o);
+
+        return s;
+    }
+
+
+    public static class FloodmarksState extends WMSDBState implements FacetTypes
+    {
+        private static final Logger logger =
+            Logger.getLogger(FloodmarksState.class);
+
+        protected int riverId;
+
+        public FloodmarksState(WMSDBArtifact artifact) {
+            super(artifact);
+        }
+
+        @Override
+        protected String getFacetType() {
+            return FLOODMAP_FLOODMARKS;
+        }
+
+        @Override
+        protected String getUrl() {
+            return FLYSUtils.getUserWMSUrl(artifact.identifier());
+        }
+
+        @Override
+        protected String getSrid() {
+            River river = RiverFactory.getRiver(getRiverId());
+            return FLYSUtils.getRiverSrid(river.getName());
+        }
+
+        @Override
+        protected Envelope getExtent(boolean reproject) {
+            String year = getIdPart(2);
+            List<Floodmark> floodmarks;
+            if (year != null) {
+                floodmarks = Floodmark.getFloodmarks(getRiverId(),
+                        Integer.parseInt(year));
+            } else {
+                floodmarks = Floodmark.getFloodmarks(getRiverId());
+            }
+
+            Envelope max = null;
+
+            for (Floodmark f: floodmarks) {
+                Envelope env = f.getGeom().getEnvelopeInternal();
+
+                if (max == null) {
+                    max = env;
+                    continue;
+                }
+
+                max.expandToInclude(env);
+            }
+
+            return max != null && reproject
+                ? GeometryUtils.transform(max, getSrid())
+                : max;
+        }
+
+        @Override
+        protected String getFilter() {
+            String year = getIdPart(2);
+            if (year != null) {
+                return "river_id=" + String.valueOf(getRiverId()) +
+                    " AND year=" + getIdPart(2);
+            }
+            return "river_id=" + String.valueOf(getRiverId());
+        }
+
+        @Override
+        protected String getDataString() {
+            String srid = getSrid();
+
+            if (FLYSUtils.isUsingOracle()) {
+                return "geom FROM flood_marks USING SRID " + srid;
+            }
+            else {
+                return "geom FROM flood_marks USING UNIQUE id USING SRID " + srid;
+            }
+        }
+
+        @Override
+        protected String getGeometryType() {
+            return "POINT";
+        }
+    } // end of WMSKmState
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/WMSFloodplainArtifact.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,169 @@
+package org.dive4elements.river.artifacts;
+
+import java.util.List;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+import com.vividsolutions.jts.geom.Envelope;
+
+import org.dive4elements.artifacts.ArtifactFactory;
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.artifactdatabase.state.DefaultOutput;
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifactdatabase.state.State;
+
+import org.dive4elements.river.model.Floodplain;
+import org.dive4elements.river.model.River;
+
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.RiverFactory;
+import org.dive4elements.river.utils.FLYSUtils;
+import org.dive4elements.river.utils.GeometryUtils;
+
+
+public class WMSFloodplainArtifact extends WMSDBArtifact {
+
+    public static final String NAME = "floodplain";
+
+
+    private static final Logger logger =
+        Logger.getLogger(WMSFloodplainArtifact.class);
+
+
+    @Override
+    public void setup(
+        String          identifier,
+        ArtifactFactory factory,
+        Object          context,
+        CallMeta        callMeta,
+        Document        data)
+    {
+        logger.debug("WMSFloodplainArtifact.setup");
+
+        super.setup(identifier, factory, context, callMeta, data);
+    }
+
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+
+    @Override
+    public State getCurrentState(Object cc) {
+        State s = new FloodplainState(this);
+
+        List<Facet> fs = getFacets(getCurrentStateId());
+
+        DefaultOutput o = new DefaultOutput(
+            "floodmap",
+            "floodmap",
+            "image/png",
+            fs,
+            "map");
+
+        s.getOutputs().add(o);
+
+        return s;
+    }
+
+
+    public static class FloodplainState extends WMSDBState implements FacetTypes
+    {
+        private static final Logger logger =
+            Logger.getLogger(FloodplainState.class);
+
+        protected int riverId;
+
+        public FloodplainState(WMSDBArtifact artifact) {
+            super(artifact);
+            riverId = 0;
+        }
+
+        protected River getRiver() {
+            return RiverFactory.getRiver(getRiverId());
+        }
+
+        @Override
+        protected String getFacetType() {
+            return FLOODMAP_FLOODPLAIN;
+        }
+
+        @Override
+        protected String getUrl() {
+            return FLYSUtils.getUserWMSUrl(artifact.identifier());
+        }
+
+        @Override
+        protected String getSrid() {
+            River river = getRiver();
+            return FLYSUtils.getRiverSrid(river.getName());
+        }
+
+        @Override
+        protected Envelope getExtent(boolean reproject) {
+            River river = getRiver();
+            List<Floodplain> fps;
+
+            String kind = getIdPart(2);
+
+            if (kind != null && ! kind.equals("1")) {
+                fps = Floodplain.getFloodplains(river.getName(),
+                        getName(), Integer.parseInt(kind));
+            } else {
+                fps = Floodplain.getFloodplains(river.getName(), 1);
+            }
+
+            Envelope max = null;
+
+            for (Floodplain fp: fps) {
+                Envelope env = fp.getGeom().getEnvelopeInternal();
+
+                if (max == null) {
+                    max = env;
+                    continue;
+                }
+
+                max.expandToInclude(env);
+            }
+
+            return max != null && reproject
+                ? GeometryUtils.transform(max, getSrid())
+                : max;
+        }
+
+        @Override
+        protected String getFilter() {
+            String kind = getIdPart(2);
+            if (kind != null && ! kind.equals("1")) {
+                return "river_id=" + String.valueOf(getRiverId()) +
+                    " AND kind_id=" + kind +
+                    " AND name='" + getName() + "'";
+            }
+            return "river_id=" + String.valueOf(getRiverId()) +
+                    " AND kind_id=1";
+        }
+
+        @Override
+        protected String getDataString() {
+            String srid = getSrid();
+
+            if (FLYSUtils.isUsingOracle()) {
+                return "geom FROM floodplain USING SRID " + srid;
+            }
+            else {
+                return "geom FROM floodplain USING UNIQUE id USING SRID " +srid;
+            }
+        }
+
+        @Override
+        protected String getGeometryType() {
+            return "POLYGON";
+        }
+    } // end of WMSKmState
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/WMSGaugeLocationArtifact.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,153 @@
+package org.dive4elements.river.artifacts;
+
+import java.util.List;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+import com.vividsolutions.jts.geom.Envelope;
+import com.vividsolutions.jts.geom.Geometry;
+
+import org.dive4elements.artifacts.ArtifactFactory;
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifactdatabase.state.DefaultOutput;
+import org.dive4elements.artifactdatabase.state.State;
+
+import org.dive4elements.river.model.River;
+import org.dive4elements.river.model.GaugeLocation;
+
+import org.dive4elements.river.artifacts.WMSDBArtifact.WMSDBState;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.RiverFactory;
+import org.dive4elements.river.utils.FLYSUtils;
+import org.dive4elements.river.utils.GeometryUtils;
+
+
+public class WMSGaugeLocationArtifact extends WMSDBArtifact {
+
+    public static final String NAME = "wmsgaugelocation";
+
+
+    private static final Logger logger =
+        Logger.getLogger(WMSGaugeLocationArtifact.class);
+
+
+    @Override
+    public void setup(
+        String          identifier,
+        ArtifactFactory factory,
+        Object          context,
+        CallMeta        callMeta,
+        Document        data)
+    {
+        logger.debug("WMSGaugeLocationArtifact.setup");
+
+        super.setup(identifier, factory, context, callMeta, data);
+    }
+
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+
+    @Override
+    public State getCurrentState(Object cc) {
+        State s = new WMSGaugeLocationState(this);
+
+        List<Facet> fs = getFacets(getCurrentStateId());
+
+        DefaultOutput o = new DefaultOutput(
+            "floodmap",
+            "floodmap",
+            "image/png",
+            fs,
+            "map");
+
+        s.getOutputs().add(o);
+
+        return s;
+    }
+
+
+
+    public static class WMSGaugeLocationState extends WMSDBState implements FacetTypes {
+
+        private static final Logger logger =
+            Logger.getLogger(WMSGaugeLocationState.class);
+
+        protected Geometry geom;
+
+        public WMSGaugeLocationState(WMSDBArtifact artifact) {
+            super(artifact);
+        }
+
+        @Override
+        protected String getFacetType() {
+            return FLOODMAP_GAUGE_LOCATION;
+        }
+
+        @Override
+        protected String getUrl() {
+            return FLYSUtils.getUserWMSUrl(artifact.identifier());
+        }
+
+        @Override
+        protected String getSrid() {
+            River river = RiverFactory.getRiver(getRiverId());
+            return FLYSUtils.getRiverSrid(river.getName());
+        }
+
+        @Override
+        protected Envelope getExtent(boolean reproject) {
+            List<GaugeLocation> gauges =
+                GaugeLocation.getGaugeLocations(getRiverId(), getName());
+
+            Envelope max = null;
+
+            for (GaugeLocation gauge: gauges) {
+                Envelope env = gauge.getGeom().getEnvelopeInternal();
+
+                if (max == null) {
+                    max = env;
+                    continue;
+                }
+
+                max.expandToInclude(env);
+            }
+
+            return max != null && reproject
+                ? GeometryUtils.transform(max, getSrid())
+                : max;
+        }
+
+        @Override
+        protected String getFilter() {
+            return "river_id=" + String.valueOf(getRiverId()) +
+                " and name='" + getName() + "'";
+        }
+
+        @Override
+        protected String getDataString() {
+            String srid = getSrid();
+
+            if (FLYSUtils.isUsingOracle()) {
+                return "geom FROM gauge_location USING SRID " + srid;
+            }
+            else {
+                return "geom FROM gauge_location " +
+                       "USING UNIQUE id USING SRID " + srid;
+            }
+        }
+
+        @Override
+        protected String getGeometryType() {
+            return "POINT";
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/WMSHWSLinesArtifact.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,165 @@
+package org.dive4elements.river.artifacts;
+
+import java.util.List;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+import com.vividsolutions.jts.geom.Envelope;
+
+import org.dive4elements.artifacts.ArtifactFactory;
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.artifactdatabase.state.DefaultOutput;
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifactdatabase.state.State;
+
+import org.dive4elements.river.model.HWSLine;
+import org.dive4elements.river.model.River;
+
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.RiverFactory;
+import org.dive4elements.river.utils.FLYSUtils;
+import org.dive4elements.river.utils.GeometryUtils;
+
+
+public class WMSHWSLinesArtifact extends WMSDBArtifact {
+
+    public static final String NAME = "hws_lines";
+
+
+    private static final Logger logger =
+        Logger.getLogger(WMSHWSLinesArtifact.class);
+
+
+    @Override
+    public void setup(
+        String          identifier,
+        ArtifactFactory factory,
+        Object          context,
+        CallMeta        callMeta,
+        Document        data)
+    {
+        logger.debug("WMSHWSLinesArtifact.setup");
+
+        super.setup(identifier, factory, context, callMeta, data);
+    }
+
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+
+    @Override
+    public State getCurrentState(Object cc) {
+        State s = new HWSLinesState(this);
+
+        List<Facet> fs = getFacets(getCurrentStateId());
+
+        DefaultOutput o = new DefaultOutput(
+            "floodmap",
+            "floodmap",
+            "image/png",
+            fs,
+            "map");
+
+        s.getOutputs().add(o);
+
+        return s;
+    }
+
+
+    public static class HWSLinesState extends WMSDBState implements FacetTypes
+    {
+        private static final Logger logger =
+            Logger.getLogger(HWSLinesState.class);
+
+        protected int riverId;
+
+        public HWSLinesState(WMSDBArtifact artifact) {
+            super(artifact);
+            riverId = 0;
+        }
+
+        public int getRiverId() {
+            if (riverId == 0) {
+                String   ids   = artifact.getDataAsString("ids");
+                String[] parts = ids.split(";");
+
+                try {
+                    riverId = Integer.parseInt(parts[0]);
+                }
+                catch (NumberFormatException nfe) {
+                    logger.error("Cannot parse river id from '" + parts[0] + "'");
+                }
+            }
+
+            return riverId;
+        }
+
+        @Override
+        protected String getFacetType() {
+            return FLOODMAP_HWS_LINES;
+        }
+
+        @Override
+        protected String getUrl() {
+            return FLYSUtils.getUserWMSUrl(artifact.identifier());
+        }
+
+        @Override
+        protected String getSrid() {
+            River river = RiverFactory.getRiver(getRiverId());
+            return FLYSUtils.getRiverSrid(river.getName());
+        }
+
+        @Override
+        protected Envelope getExtent(boolean reproject) {
+            List<HWSLine> hws = HWSLine.getLines(getRiverId(), getName());
+
+            Envelope max = null;
+
+            for (HWSLine h: hws) {
+                Envelope env = h.getGeom().getEnvelopeInternal();
+
+                if (max == null) {
+                    max = env;
+                    continue;
+                }
+
+                max.expandToInclude(env);
+            }
+
+            return max != null && reproject
+                ? GeometryUtils.transform(max, getSrid())
+                : max;
+        }
+
+        @Override
+        protected String getFilter() {
+            return "river_id=" + String.valueOf(getRiverId())
+                + " AND name='" + getName() + "'";
+        }
+
+        @Override
+        protected String getDataString() {
+            String srid = getSrid();
+
+            if (FLYSUtils.isUsingOracle()) {
+                return "geom FROM hws_lines USING SRID " + srid;
+            }
+            else {
+                return "geom FROM hws_lines USING UNIQUE id USING SRID " + srid;
+            }
+        }
+
+        @Override
+        protected String getGeometryType() {
+            return "LINE";
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/WMSHWSPointsArtifact.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,165 @@
+package org.dive4elements.river.artifacts;
+
+import java.util.List;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+import com.vividsolutions.jts.geom.Envelope;
+
+import org.dive4elements.artifacts.ArtifactFactory;
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.artifactdatabase.state.DefaultOutput;
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifactdatabase.state.State;
+
+import org.dive4elements.river.model.HWSPoint;
+import org.dive4elements.river.model.River;
+
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.RiverFactory;
+import org.dive4elements.river.utils.FLYSUtils;
+import org.dive4elements.river.utils.GeometryUtils;
+
+
+public class WMSHWSPointsArtifact extends WMSDBArtifact {
+
+    public static final String NAME = "hws_points";
+
+
+    private static final Logger logger =
+        Logger.getLogger(WMSHWSPointsArtifact.class);
+
+
+    @Override
+    public void setup(
+        String          identifier,
+        ArtifactFactory factory,
+        Object          context,
+        CallMeta        callMeta,
+        Document        data)
+    {
+        logger.debug("WMSHWSPointsArtifact.setup");
+
+        super.setup(identifier, factory, context, callMeta, data);
+    }
+
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+
+    @Override
+    public State getCurrentState(Object cc) {
+        State s = new HWSPointsState(this);
+
+        List<Facet> fs = getFacets(getCurrentStateId());
+
+        DefaultOutput o = new DefaultOutput(
+            "floodmap",
+            "floodmap",
+            "image/png",
+            fs,
+            "map");
+
+        s.getOutputs().add(o);
+
+        return s;
+    }
+
+
+    public static class HWSPointsState extends WMSDBState implements FacetTypes
+    {
+        private static final Logger logger =
+            Logger.getLogger(HWSPointsState.class);
+
+        protected int riverId;
+
+        public HWSPointsState(WMSDBArtifact artifact) {
+            super(artifact);
+            riverId = 0;
+        }
+
+        public int getRiverId() {
+            if (riverId == 0) {
+                String   ids   = artifact.getDataAsString("ids");
+                String[] parts = ids.split(";");
+
+                try {
+                    riverId = Integer.parseInt(parts[0]);
+                }
+                catch (NumberFormatException nfe) {
+                    logger.error("Cannot parse river id from '" + parts[0] + "'");
+                }
+            }
+
+            return riverId;
+        }
+
+        @Override
+        protected String getFacetType() {
+            return FLOODMAP_HWS_POINTS;
+        }
+
+        @Override
+        protected String getUrl() {
+            return FLYSUtils.getUserWMSUrl(artifact.identifier());
+        }
+
+        @Override
+        protected String getSrid() {
+            River river = RiverFactory.getRiver(getRiverId());
+            return FLYSUtils.getRiverSrid(river.getName());
+        }
+
+        @Override
+        protected Envelope getExtent(boolean reproject) {
+            List<HWSPoint> hws = HWSPoint.getPoints(getRiverId(), getName());
+
+            Envelope max = null;
+
+            for (HWSPoint h: hws) {
+                Envelope env = h.getGeom().getEnvelopeInternal();
+
+                if (max == null) {
+                    max = env;
+                    continue;
+                }
+
+                max.expandToInclude(env);
+            }
+
+            return max != null && reproject
+                ? GeometryUtils.transform(max, getSrid())
+                : max;
+        }
+
+        @Override
+        protected String getFilter() {
+            return "river_id=" + String.valueOf(getRiverId())
+                + " AND name='" + getName() + "'";
+        }
+
+        @Override
+        protected String getDataString() {
+            String srid = getSrid();
+
+            if (FLYSUtils.isUsingOracle()) {
+                return "geom FROM hws_points USING SRID " + srid;
+            }
+            else {
+                return "geom FROM hws_points USING UNIQUE id USING SRID " + srid;
+            }
+        }
+
+        @Override
+        protected String getGeometryType() {
+            return "POINT";
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/WMSHydrBoundaryArtifact.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,162 @@
+package org.dive4elements.river.artifacts;
+
+import java.util.List;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+import com.vividsolutions.jts.geom.Envelope;
+
+import org.dive4elements.artifacts.ArtifactFactory;
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.artifactdatabase.state.DefaultOutput;
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifactdatabase.state.State;
+
+import org.dive4elements.river.model.River;
+import org.dive4elements.river.model.HydrBoundary;
+
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.RiverFactory;
+import org.dive4elements.river.utils.FLYSUtils;
+import org.dive4elements.river.utils.GeometryUtils;
+
+
+public class WMSHydrBoundaryArtifact extends WMSDBArtifact {
+
+    public static final String NAME = "hydr_boundary";
+
+
+    private static final Logger logger =
+        Logger.getLogger(WMSHydrBoundaryArtifact.class);
+
+
+    @Override
+    public void setup(
+        String          identifier,
+        ArtifactFactory factory,
+        Object          context,
+        CallMeta        callMeta,
+        Document        data)
+    {
+        logger.debug("WMSHydrBoundaryArtifact.setup");
+
+        super.setup(identifier, factory, context, callMeta, data);
+    }
+
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+
+    @Override
+    public State getCurrentState(Object cc) {
+        State s = new HydrBoundaryState(this);
+
+        List<Facet> fs = getFacets(getCurrentStateId());
+
+        DefaultOutput o = new DefaultOutput(
+            "floodmap",
+            "floodmap",
+            "image/png",
+            fs,
+            "map");
+
+        s.getOutputs().add(o);
+
+        return s;
+    }
+
+
+    public static class HydrBoundaryState extends WMSDBState implements FacetTypes
+    {
+        private static final Logger logger =
+            Logger.getLogger(HydrBoundaryState.class);
+
+        protected int riverId;
+
+        public HydrBoundaryState(WMSDBArtifact artifact) {
+            super(artifact);
+            riverId = 0;
+        }
+
+        @Override
+        protected String getFacetType() {
+            return FLOODMAP_HYDR_BOUNDARY;
+        }
+
+        @Override
+        protected String getUrl() {
+            return FLYSUtils.getUserWMSUrl(artifact.identifier());
+        }
+
+        @Override
+        protected String getSrid() {
+            River river = RiverFactory.getRiver(getRiverId());
+            return FLYSUtils.getRiverSrid(river.getName());
+        }
+
+        @Override
+        protected Envelope getExtent(boolean reproject) {
+            List<HydrBoundary> boundaries;
+            String kind = getIdPart(2);
+            if (kind != null) {
+                boundaries = HydrBoundary.getHydrBoundaries(getRiverId(),
+                        getName(), Integer.parseInt(kind));
+            } else {
+                boundaries = HydrBoundary.getHydrBoundaries(getRiverId(), getName());
+            }
+
+            Envelope max = null;
+
+            for (HydrBoundary b: boundaries) {
+                Envelope env = b.getGeom().getEnvelopeInternal();
+
+                if (max == null) {
+                    max = env;
+                    continue;
+                }
+
+                max.expandToInclude(env);
+            }
+
+            return max != null && reproject
+                ? GeometryUtils.transform(max, getSrid())
+                : max;
+        }
+
+        @Override
+        protected String getFilter() {
+            String kind = getIdPart(2);
+            if (kind != null) {
+                return "river_id=" + String.valueOf(getRiverId())
+                    + " AND name='" + getName() + "'"
+                    + " AND kind = " + kind;
+            }
+            return "river_id=" + String.valueOf(getRiverId())
+                + " AND name='" + getName() + "'";
+        }
+
+        @Override
+        protected String getDataString() {
+            String srid = getSrid();
+
+            if (FLYSUtils.isUsingOracle()) {
+                return "geom FROM hydr_boundaries USING SRID " + srid;
+            }
+            else {
+                return "geom FROM hydr_boundaries USING UNIQUE id USING SRID " + srid;
+            }
+        }
+
+        @Override
+        protected String getGeometryType() {
+            return "LINE";
+        }
+    } // end of HydrBoundaryState
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/WMSHydrBoundaryPolyArtifact.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,194 @@
+package org.dive4elements.river.artifacts;
+
+import java.util.List;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+import com.vividsolutions.jts.geom.Envelope;
+
+import org.dive4elements.artifacts.ArtifactFactory;
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.artifactdatabase.state.DefaultOutput;
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifactdatabase.state.State;
+
+import org.dive4elements.river.model.River;
+import org.dive4elements.river.model.HydrBoundaryPoly;
+
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.RiverFactory;
+import org.dive4elements.river.utils.FLYSUtils;
+import org.dive4elements.river.utils.GeometryUtils;
+
+
+public class WMSHydrBoundaryPolyArtifact extends WMSDBArtifact {
+
+    public static final String NAME = "hydr_boundary_poly";
+
+
+    private static final Logger logger =
+        Logger.getLogger(WMSHydrBoundaryPolyArtifact.class);
+
+
+    @Override
+    public void setup(
+        String          identifier,
+        ArtifactFactory factory,
+        Object          context,
+        CallMeta        callMeta,
+        Document        data)
+    {
+        logger.debug("WMSHydrBoundaryArtifact.setup");
+
+        super.setup(identifier, factory, context, callMeta, data);
+    }
+
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+
+    @Override
+    public State getCurrentState(Object cc) {
+        State s = new HydrBoundaryPolyState(this);
+
+        List<Facet> fs = getFacets(getCurrentStateId());
+
+        DefaultOutput o = new DefaultOutput(
+            "floodmap",
+            "floodmap",
+            "image/png",
+            fs,
+            "map");
+
+        s.getOutputs().add(o);
+
+        return s;
+    }
+
+
+    public static class HydrBoundaryPolyState extends WMSDBState implements FacetTypes
+    {
+        private static final Logger logger =
+            Logger.getLogger(HydrBoundaryPolyState.class);
+
+        protected int riverId;
+
+        public HydrBoundaryPolyState(WMSDBArtifact artifact) {
+            super(artifact);
+            riverId = 0;
+        }
+
+        @Override
+        protected String getFacetType() {
+            return FLOODMAP_HYDR_BOUNDARY_POLY;
+        }
+
+        @Override
+        protected String getUrl() {
+            return FLYSUtils.getUserWMSUrl(artifact.identifier());
+        }
+
+        @Override
+        protected String getSrid() {
+            River river = RiverFactory.getRiver(getRiverId());
+            return FLYSUtils.getRiverSrid(river.getName());
+        }
+
+        @Override
+        protected Envelope getExtent(boolean reproject) {
+            String kind = getIdPart(2);
+            String sectie = getIdPart(3);
+            String sobek = getIdPart(4);
+            int kindId = -1;
+            int sectieId = -1;
+            int sobekId = -1;
+
+            if (kind != null) {
+                kindId = Integer.parseInt(kind);
+            }
+            if (sectie != null && !sectie.equals("-1")) {
+                sectieId = Integer.parseInt(sectie);
+            }
+            if (sobek != null && !sobek.equals("-1")) {
+                sobekId = Integer.parseInt(sobek);
+            }
+
+            List<HydrBoundaryPoly> boundaries;
+            if (kindId == -1 && sobekId == -1 && sectieId == -1) {
+                boundaries = HydrBoundaryPoly.getHydrBoundaries(
+                        getRiverId(), getName());
+            } else {
+                boundaries = HydrBoundaryPoly.getHydrBoundaries(
+                        getRiverId(), kindId, sectieId, sobekId);
+            }
+
+            Envelope max = null;
+
+            for (HydrBoundaryPoly b: boundaries) {
+                Envelope env = b.getGeom().getEnvelopeInternal();
+
+                if (max == null) {
+                    max = env;
+                    continue;
+                }
+
+                max.expandToInclude(env);
+            }
+
+            return max != null && reproject
+                ? GeometryUtils.transform(max, getSrid())
+                : max;
+        }
+
+        @Override
+        protected String getFilter() {
+            // Expected id string:
+            // river_id;layer_name;kind;sectie;sobek
+            String kind = getIdPart(2);
+            String sectie = getIdPart(3);
+            String sobek = getIdPart(4);
+
+            String filter = "";
+            if (kind != null && !kind.equals("-1")) {
+                filter += " AND kind = " + kind;
+            }
+            if (sectie != null && !sectie.equals("-1")) {
+                filter += " AND sectie = " + sectie;
+            }
+            if (sobek != null && !sobek.equals("-1")) {
+                filter += " AND sobek = " + sobek;
+            }
+
+            if (filter.isEmpty()) {
+                filter = " AND name='" + getName() + "'";
+            }
+
+            return "river_id=" + String.valueOf(getRiverId())
+                + filter;
+        }
+
+        @Override
+        protected String getDataString() {
+            String srid = getSrid();
+
+            if (FLYSUtils.isUsingOracle()) {
+                return "geom FROM hydr_boundaries_poly USING SRID " + srid;
+            }
+            else {
+                return "geom FROM hydr_boundaries_poly USING UNIQUE id USING SRID " + srid;
+            }
+        }
+
+        @Override
+        protected String getGeometryType() {
+            return "POLYGON";
+        }
+    } // end of HydrBoundaryState
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/WMSJettiesArtifact.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,156 @@
+package org.dive4elements.river.artifacts;
+
+import java.util.List;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+import com.vividsolutions.jts.geom.Envelope;
+
+import org.dive4elements.artifacts.ArtifactFactory;
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.artifactdatabase.state.DefaultOutput;
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifactdatabase.state.State;
+
+import org.dive4elements.river.model.River;
+import org.dive4elements.river.model.Jetty;
+
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.RiverFactory;
+import org.dive4elements.river.utils.FLYSUtils;
+import org.dive4elements.river.utils.GeometryUtils;
+
+
+public class WMSJettiesArtifact extends WMSDBArtifact {
+
+    public static final String NAME = "jetties";
+
+
+    private static final Logger logger =
+        Logger.getLogger(WMSJettiesArtifact.class);
+
+
+    @Override
+    public void setup(
+        String          identifier,
+        ArtifactFactory factory,
+        Object          context,
+        CallMeta        callMeta,
+        Document        data)
+    {
+        logger.debug("WMSJettiesArtifact.setup");
+
+        super.setup(identifier, factory, context, callMeta, data);
+    }
+
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+
+    @Override
+    public State getCurrentState(Object cc) {
+        State s = new JettiesState(this);
+
+        List<Facet> fs = getFacets(getCurrentStateId());
+
+        DefaultOutput o = new DefaultOutput(
+            "floodmap",
+            "floodmap",
+            "image/png",
+            fs,
+            "map");
+
+        s.getOutputs().add(o);
+
+        return s;
+    }
+
+
+    public static class JettiesState extends WMSDBState implements FacetTypes
+    {
+        private static final Logger logger =
+            Logger.getLogger(JettiesState.class);
+
+        protected int riverId;
+
+        public JettiesState(WMSDBArtifact artifact) {
+            super(artifact);
+            riverId = 0;
+        }
+
+        @Override
+        protected String getFacetType() {
+            return FLOODMAP_JETTIES;
+        }
+
+        @Override
+        protected String getUrl() {
+            return FLYSUtils.getUserWMSUrl(artifact.identifier());
+        }
+
+        @Override
+        protected String getSrid() {
+            River river = RiverFactory.getRiver(getRiverId());
+            return FLYSUtils.getRiverSrid(river.getName());
+        }
+
+        @Override
+        protected Envelope getExtent(boolean reproject) {
+            List<Jetty> jetties;
+            String kind = getIdPart(2);
+
+            jetties = Jetty.getJetties(getRiverId(), Integer.parseInt(kind));
+
+            Envelope max = null;
+
+            for (Jetty b: jetties) {
+                Envelope env = b.getGeom().getEnvelopeInternal();
+
+                if (max == null) {
+                    max = env;
+                    continue;
+                }
+
+                max.expandToInclude(env);
+            }
+
+            return max != null && reproject
+                ? GeometryUtils.transform(max, getSrid())
+                : max;
+        }
+
+        @Override
+        protected String getFilter() {
+            // The expected id string is:
+            // river_id;layer-name;kind
+
+            String kind = getIdPart(2);
+            return "river_id=" + String.valueOf(getRiverId()) +
+                " AND kind_id=" + kind;
+        }
+
+        @Override
+        protected String getDataString() {
+            String srid = getSrid();
+
+            if (FLYSUtils.isUsingOracle()) {
+                return "geom FROM jetties USING SRID " + srid;
+            }
+            else {
+                return "geom FROM jetties USING UNIQUE id USING SRID " + srid;
+            }
+        }
+
+        @Override
+        protected String getGeometryType() {
+            return "POINT";
+        }
+    } // end of WMSKmState
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/WMSKmArtifact.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,171 @@
+package org.dive4elements.river.artifacts;
+
+import java.util.List;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+import com.vividsolutions.jts.geom.Envelope;
+import com.vividsolutions.jts.geom.Geometry;
+
+import org.dive4elements.artifacts.ArtifactFactory;
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifactdatabase.state.DefaultOutput;
+import org.dive4elements.artifactdatabase.state.State;
+
+import org.dive4elements.river.model.River;
+import org.dive4elements.river.model.RiverAxisKm;
+
+import org.dive4elements.river.artifacts.WMSDBArtifact.WMSDBState;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.RiverFactory;
+import org.dive4elements.river.utils.FLYSUtils;
+import org.dive4elements.river.utils.GeometryUtils;
+
+
+public class WMSKmArtifact extends WMSDBArtifact {
+
+    public static final String NAME = "wmskm";
+
+
+    private static final Logger logger = Logger.getLogger(WMSKmArtifact.class);
+
+
+    @Override
+    public void setup(
+        String          identifier,
+        ArtifactFactory factory,
+        Object          context,
+        CallMeta        callMeta,
+        Document        data)
+    {
+        logger.debug("WMSKmArtifact.setup");
+
+        super.setup(identifier, factory, context, callMeta, data);
+    }
+
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+
+    @Override
+    public State getCurrentState(Object cc) {
+        State s = new WMSKmState(this);
+
+        List<Facet> fs = getFacets(getCurrentStateId());
+
+        DefaultOutput o = new DefaultOutput(
+            "floodmap",
+            "floodmap",
+            "image/png",
+            fs,
+            "map");
+
+        s.getOutputs().add(o);
+
+        return s;
+    }
+
+
+
+    public static class WMSKmState extends WMSDBState implements FacetTypes {
+
+        private static final Logger logger = Logger.getLogger(WMSKmState.class);
+
+        protected Geometry geom;
+        protected int      riverId;
+
+        public WMSKmState(WMSDBArtifact artifact) {
+            super(artifact);
+            riverId = 0;
+        }
+
+        public int getRiverId() {
+            if (riverId == 0) {
+                String ids = artifact.getDataAsString("ids");
+
+                try {
+                    riverId = Integer.parseInt(ids);
+                }
+                catch (NumberFormatException nfe) {
+                    logger.error("Cannot parse river id from '" + ids + "'");
+                }
+            }
+
+            return riverId;
+        }
+
+        @Override
+        protected String getFacetType() {
+            return FLOODMAP_KMS;
+        }
+
+        @Override
+        protected String getUrl() {
+            return FLYSUtils.getUserWMSUrl(artifact.identifier());
+        }
+
+        @Override
+        protected String getSrid() {
+            River river = RiverFactory.getRiver(getRiverId());
+            return FLYSUtils.getRiverSrid(river.getName());
+        }
+
+        @Override
+        protected Envelope getExtent(boolean reproject) {
+            List<RiverAxisKm> kms = RiverAxisKm.getRiverAxisKms(getRiverId());
+
+            Envelope max = null;
+
+            for (RiverAxisKm km: kms) {
+                Envelope env = km.getGeom().getEnvelopeInternal();
+
+                if (max == null) {
+                    max = env;
+                    continue;
+                }
+
+                max.expandToInclude(env);
+            }
+
+            return max != null && reproject
+                ? GeometryUtils.transform(max, getSrid())
+                : max;
+        }
+
+        @Override
+        protected String getFilter() {
+            return "river_id=" + String.valueOf(getRiverId());
+        }
+
+        @Override
+        protected String getDataString() {
+            String srid = getSrid();
+
+            if (FLYSUtils.isUsingOracle()) {
+                return "geom FROM river_axes_km USING SRID " + srid;
+            }
+            else {
+                return "geom FROM river_axes_km " +
+                       "USING UNIQUE id USING SRID " + srid;
+            }
+        }
+
+        @Override
+        protected String getLabelItem() {
+            return "km";
+        }
+
+        @Override
+        protected String getGeometryType() {
+            return "POINT";
+        }
+    } // end of WMSKmState
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/WMSLineArtifact.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,165 @@
+package org.dive4elements.river.artifacts;
+
+import java.util.List;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+import com.vividsolutions.jts.geom.Envelope;
+
+import org.dive4elements.artifacts.ArtifactFactory;
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.artifactdatabase.state.DefaultOutput;
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifactdatabase.state.State;
+
+import org.dive4elements.river.model.River;
+import org.dive4elements.river.model.HWSLine;
+
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.RiverFactory;
+import org.dive4elements.river.utils.FLYSUtils;
+import org.dive4elements.river.utils.GeometryUtils;
+
+
+public class WMSLineArtifact extends WMSDBArtifact {
+
+    public static final String NAME = "lines";
+
+
+    private static final Logger logger =
+        Logger.getLogger(WMSLineArtifact.class);
+
+
+    @Override
+    public void setup(
+        String          identifier,
+        ArtifactFactory factory,
+        Object          context,
+        CallMeta        callMeta,
+        Document        data)
+    {
+        logger.debug("WMSLineArtifact.setup");
+
+        super.setup(identifier, factory, context, callMeta, data);
+    }
+
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+
+    @Override
+    public State getCurrentState(Object cc) {
+        State s = new LineState(this);
+
+        List<Facet> fs = getFacets(getCurrentStateId());
+
+        DefaultOutput o = new DefaultOutput(
+            "floodmap",
+            "floodmap",
+            "image/png",
+            fs,
+            "map");
+
+        s.getOutputs().add(o);
+
+        return s;
+    }
+
+
+    public static class LineState extends WMSDBState implements FacetTypes
+    {
+        private static final Logger logger =
+            Logger.getLogger(LineState.class);
+
+        protected int riverId;
+
+        public LineState(WMSDBArtifact artifact) {
+            super(artifact);
+            riverId = 0;
+        }
+
+        public int getRiverId() {
+            if (riverId == 0) {
+                String   ids   = artifact.getDataAsString("ids");
+                String[] parts = ids.split(";");
+
+                try {
+                    riverId = Integer.parseInt(parts[0]);
+                }
+                catch (NumberFormatException nfe) {
+                    logger.error("Cannot parse river id from '" + parts[0] + "'");
+                }
+            }
+
+            return riverId;
+        }
+
+        @Override
+        protected String getFacetType() {
+            return FLOODMAP_LINES;
+        }
+
+        @Override
+        protected String getUrl() {
+            return FLYSUtils.getUserWMSUrl(artifact.identifier());
+        }
+
+        @Override
+        protected String getSrid() {
+            River river = RiverFactory.getRiver(getRiverId());
+            return FLYSUtils.getRiverSrid(river.getName());
+        }
+
+        @Override
+        protected Envelope getExtent(boolean reproject) {
+            List<HWSLine> lines = HWSLine.getLines(getRiverId(), getName());
+
+            Envelope max = null;
+
+            for (HWSLine l: lines) {
+                Envelope env = l.getGeom().getEnvelopeInternal();
+
+                if (max == null) {
+                    max = env;
+                    continue;
+                }
+
+                max.expandToInclude(env);
+            }
+
+            return max != null && reproject
+                ? GeometryUtils.transform(max, getSrid())
+                : max;
+        }
+
+        @Override
+        protected String getFilter() {
+            return "river_id=" + String.valueOf(getRiverId()) +
+                " AND name='" + getName() + "'";
+        }
+
+        @Override
+        protected String getDataString() {
+            String srid = getSrid();
+
+            if (FLYSUtils.isUsingOracle()) {
+                return "geom FROM lines USING SRID " + srid;
+            }
+            else {
+                return "geom FROM lines USING UNIQUE id USING SRID " + srid;
+            }
+        }
+
+        @Override
+        protected String getGeometryType() {
+            return "LINE";
+        }
+    } // end of WMSKmState
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/WMSQPSArtifact.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,171 @@
+package org.dive4elements.river.artifacts;
+
+import java.util.List;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+import com.vividsolutions.jts.geom.Envelope;
+
+import org.dive4elements.artifacts.ArtifactFactory;
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.artifactdatabase.state.DefaultOutput;
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifactdatabase.state.State;
+
+import org.dive4elements.river.model.River;
+import org.dive4elements.river.model.CrossSectionTrack;
+
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.RiverFactory;
+import org.dive4elements.river.utils.FLYSUtils;
+import org.dive4elements.river.utils.GeometryUtils;
+
+
+public class WMSQPSArtifact extends WMSDBArtifact {
+
+    public static final String NAME = "qps";
+
+
+    private static final Logger logger =
+        Logger.getLogger(WMSQPSArtifact.class);
+
+
+    @Override
+    public void setup(
+        String          identifier,
+        ArtifactFactory factory,
+        Object          context,
+        CallMeta        callMeta,
+        Document        data)
+    {
+        logger.debug("WMSQPSArtifact.setup");
+
+        super.setup(identifier, factory, context, callMeta, data);
+    }
+
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+
+    @Override
+    public State getCurrentState(Object cc) {
+        State s = new WMSQPSState(this);
+
+        List<Facet> fs = getFacets(getCurrentStateId());
+
+        DefaultOutput o = new DefaultOutput(
+            "floodmap",
+            "floodmap",
+            "image/png",
+            fs,
+            "map");
+
+        s.getOutputs().add(o);
+
+        return s;
+    }
+
+
+    public static class WMSQPSState extends WMSDBState implements FacetTypes {
+
+        private static final Logger logger =
+            Logger.getLogger(WMSQPSState.class);
+
+        public WMSQPSState(WMSDBArtifact artifact) {
+            super(artifact);
+        }
+
+        @Override
+        protected String getFacetType() {
+            return FLOODMAP_QPS;
+        }
+
+        @Override
+        protected String getUrl() {
+            return FLYSUtils.getUserWMSUrl(artifact.identifier());
+        }
+
+        @Override
+        protected String getSrid() {
+            River river = RiverFactory.getRiver(getRiverId());
+            return FLYSUtils.getRiverSrid(river.getName());
+        }
+
+        @Override
+        protected Envelope getExtent(boolean reproject) {
+            River river = RiverFactory.getRiver(getRiverId());
+            List<CrossSectionTrack> qps;
+
+            String kind = getIdPart(2);
+
+            if (kind != null && kind.equals("1")) {
+                qps = CrossSectionTrack.getCrossSectionTrack(river.getName(),
+                        Integer.parseInt(kind));
+            } else if (kind != null) {
+                qps = CrossSectionTrack.getCrossSectionTrack(river.getName(),
+                        getName(), Integer.parseInt(kind));
+            } else {
+                qps = CrossSectionTrack.getCrossSectionTrack(river.getName(),
+                        getName());
+            }
+
+            Envelope max = null;
+
+            for (CrossSectionTrack qp: qps) {
+                Envelope env = qp.getGeom().getEnvelopeInternal();
+
+                if (max == null) {
+                    max = env;
+                    continue;
+                }
+
+                max.expandToInclude(env);
+            }
+
+            return max != null && reproject
+                ? GeometryUtils.transform(max, getSrid())
+                : max;
+        }
+
+        @Override
+        protected String getFilter() {
+            String kind = getIdPart(2);
+            if (kind != null && kind.equals("1")) {
+                // There can be several layers named qps that differ in kind
+                return "river_id=" + String.valueOf(getRiverId()) +
+                    " AND kind_id=" + kind;
+            } else if (kind != null) {
+                return "river_id=" + String.valueOf(getRiverId()) +
+                    " AND kind_id=" + kind +
+                    " AND name='" + getName() + "'";
+            }
+            return "river_id=" + String.valueOf(getRiverId()) +
+                " AND name='" + getName() + "'";
+        }
+
+        @Override
+        protected String getDataString() {
+            String srid = getSrid();
+
+            if (FLYSUtils.isUsingOracle()) {
+                return "geom FROM cross_section_tracks USING SRID " + srid;
+            }
+            else {
+                return "geom FROM cross_section_tracks " +
+                       "USING UNIQUE id USING SRID " + srid;
+            }
+        }
+
+        @Override
+        protected String getGeometryType() {
+            return "LINE";
+        }
+    } // end of WMSQPSState
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/WQKmsInterpolArtifact.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,295 @@
+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.Facet;
+import org.dive4elements.artifactdatabase.state.FacetActivity;
+import org.dive4elements.artifactdatabase.state.DefaultOutput;
+import org.dive4elements.artifactdatabase.state.State;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.ArtifactFactory;
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.WQKms;
+import org.dive4elements.river.artifacts.model.WQFacet;
+import org.dive4elements.river.artifacts.model.WKmsFactory;
+import org.dive4elements.river.artifacts.model.WQKmsFactory;
+import org.dive4elements.river.artifacts.model.WstValueTable;
+import org.dive4elements.river.artifacts.model.WstValueTableFactory;
+
+import org.dive4elements.river.artifacts.states.StaticState;
+import org.dive4elements.river.artifacts.resources.Resources;
+
+
+/**
+ * Artifact to access additional "waterlevel/discharge"-type of data, like
+ * fixation measurements, but doing so with costy interpolation.
+ *
+ * This artifact neglects (Static)FLYSArtifacts capabilities of interaction
+ * with the StateEngine by overriding the getState*-methods.
+ */
+public class WQKmsInterpolArtifact
+extends      StaticFLYSArtifact
+implements   FacetTypes
+{
+    /** The logger for this class. */
+    private static Logger logger =
+        Logger.getLogger(WQKmsInterpolArtifact.class);
+
+    /** State name. */
+    public static final String STATIC_STATE_NAME =
+        "state.additional_wqkms.interpol.static";
+
+    /** Artifact name. */
+    private static final String NAME = "staticwqkmsinterpol";
+
+    static {
+        // TODO: Move to configuration.
+        FacetActivity.Registry.getInstance()
+            .register(NAME, FacetActivity.INACTIVE);
+    }
+
+    /** One and only state to be in. */
+    protected transient State state = null;
+
+
+    /**
+     * Trivial Constructor.
+     */
+    public WQKmsInterpolArtifact() {
+        logger.debug("WQKmsInterpolArtifact.WQKmsInterpolArtifact");
+    }
+
+
+    /** Return fixed artifact (types) name. */
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+
+    /**
+     * Gets called from factory, to set things up.
+     */
+    @Override
+    public void setup(
+        String          identifier,
+        ArtifactFactory factory,
+        Object          context,
+        CallMeta        callMeta,
+        Document        data)
+    {
+        logger.debug("WQKmsInterpolArtifact.setup");
+
+        state = new StaticState(STATIC_STATE_NAME);
+
+        List<Facet> fs = new ArrayList<Facet>();
+        String code = getDatacageIDValue(data);
+
+        // TODO Go for JSON, one day.
+        //ex.: flood_protection-wstv-114-12
+        if (code != null) {
+            String [] parts = code.split("-");
+
+            logger.debug("WQKmsInterpolArtifact.setup: code " + code);
+
+            if (parts.length >= 4) {
+                int wst = Integer.parseInt(parts[3]);
+                int col = -1;
+                String colpos = parts[2];
+                // Are we interested in a single column or in all columns?
+                if (colpos.equals("A")) {
+                    ; // Take all.
+                }
+                else {
+                    col = Integer.parseInt(colpos);
+                    addStringData("col_pos", parts[2]);
+                }
+                addStringData("wst_id",  parts[3]);
+                String wkmsName = (col >= 0)
+                                ? WKmsFactory.getWKmsName(col, wst)
+                                : WKmsFactory.getWKmsName(wst);
+                String name;
+                if (parts[0].startsWith("height")){
+                    name = STATIC_WQ_ANNOTATIONS;
+                }
+                else if (parts[0].startsWith("flood")) {
+                    name = STATIC_WKMS_INTERPOL;
+                }
+                else {
+                    name = STATIC_WQ;
+                }
+
+                Facet wQFacet = new WQFacet(name,
+                    Resources.getMsg(
+                        callMeta,
+                        wkmsName,
+                        wkmsName));
+                fs.add(wQFacet);
+                addFacets(state.getID(), fs);
+            }
+        }
+        else {
+            logger.warn("WQKmsInterpolArtifact: no code");
+        }
+
+        spawnState();
+        super.setup(identifier, factory, context, callMeta, data);
+    }
+
+
+    /**
+     * Initialize the static state with output.
+     * @return static state
+     */
+    protected State spawnState() {
+        state = new StaticState(STATIC_STATE_NAME);
+        List<Facet> fs = getFacets(STATIC_STATE_NAME);
+        DefaultOutput output = new DefaultOutput(
+            "general",
+            "general",
+            "image/png",
+            fs,
+            "chart");
+
+        state.getOutputs().add(output);
+
+        return state;
+    }
+
+
+    /**
+     * Called via setup.
+     *
+     * @param artifact The master-artifact.
+     */
+    @Override
+    protected void initialize(
+        Artifact artifact,
+        Object context,
+        CallMeta meta)
+    {
+        logger.debug("WQKmsInterpolArtifact.initialize");
+        FLYSArtifact winfo = (FLYSArtifact) artifact;
+        importData(winfo, "river");
+        importData(winfo, "ld_locations");
+    }
+
+
+    /**
+     * 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 WQ at a given km.
+     * @param currentKm the requested km. If NULL, ld_location data
+     *        will be used.
+     */
+    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"));
+        }
+
+        Double tmp = (currentKm != null)
+                     ? currentKm
+                     : getDataAsDouble("ld_locations");
+
+        double [][] vs = interpolator.interpolateWQColumnwise(
+            tmp != null ? tmp : 0);
+
+        for (int x = 0; x < vs[1].length; x++) {
+            logger.debug("getWQAtKm: Q/W " + vs[0][x] + " / " + vs[1][x]);
+        }
+
+        return vs;
+    }
+
+
+    /**
+     * Get a DataItem casted to int (0 if fails).
+     */
+    public int getDataAsInt(String dataName) {
+        String val = getDataAsString(dataName);
+        try {
+            return Integer.parseInt(val);
+        }
+        catch (NumberFormatException e) {
+            logger.warn("Could not get data " + dataName + " as int", e);
+            return 0;
+        }
+    }
+
+
+    /**
+     * 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();
+    }
+
+
+    /**
+     * Get WQKms from factory.
+     * @param idx param is not needed (TODO)
+     * @return WQKms according to parameterization (can be null);
+     */
+    public WQKms getWQKms(int idx) {
+        logger.debug("WQKmsInterpolArtifact.getWQKms");
+        logger.warn("Stub, getWQKms not yet implemented.");
+
+        return WQKmsFactory.getWQKms(
+            Integer.parseInt(getDataAsString("col_pos")),
+            Integer.parseInt(getDataAsString("wst_id")));
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/WaterLineArtifact.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,23 @@
+package org.dive4elements.river.artifacts;
+
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.geom.Lines;
+import org.dive4elements.river.model.FastCrossSectionLine;
+
+
+/**
+ * Interface, Artifact can create WaterLines (Water against Cross-Profile).
+ */
+public interface WaterLineArtifact {
+
+    /** Get points that define a line of a (water)facet against a cross-
+     * section. */
+    public Lines.LineData getWaterLines(
+        int                  facetIdx,
+        FastCrossSectionLine      csl,
+        double                      d,
+        double                      w,
+        CallContext           context);
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/WaterlevelArtifact.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,122 @@
+package org.dive4elements.river.artifacts;
+
+import org.dive4elements.artifactdatabase.state.DefaultFacet;
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifactdatabase.state.FacetActivity;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.ArtifactFactory;
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.river.artifacts.states.DefaultState;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+
+
+/**
+ * Clone of an WINFOArtifact to expose exactly one waterlevel only.
+ * All Facets of the "longitudinal_section" output will be added to the
+ * "w_differences" output and filterFacets adjusted accordingly.
+ *
+ * @TODO Straighten inheritance-line (waterlevel-WINFO or vice versa).
+ */
+public class WaterlevelArtifact extends WINFOArtifact {
+
+    /** The logger for this class. */
+    private static Logger logger = Logger.getLogger(WaterlevelArtifact.class);
+
+    /** The name of the artifact. */
+    public static final String ARTIFACT_NAME = "waterlevel";
+
+    static {
+        // TODO: Move to configuration.
+        FacetActivity.Registry.getInstance()
+            .register(ARTIFACT_NAME, FacetActivity.INACTIVE);
+    }
+
+    /**
+     * The default constructor.
+     */
+    public WaterlevelArtifact() {
+    }
+
+
+    /**
+     * Setup and restate longitudinal_section filterfacets to apply to the
+     * w_differences output, too. Also, for w_differences, add respective q-
+     * filter facets.
+     */
+    public void setup(
+        String          identifier,
+        ArtifactFactory factory,
+        Object          context,
+        CallMeta        callMeta,
+        Document        data)
+    {
+        super.setup(identifier, factory, context, callMeta, data);
+        // For w_differences, also allow q-facets.
+        if(filterFacets != null) {
+            List<Facet> list = new ArrayList<Facet>();
+            List<Facet> wlist = filterFacets.get(ChartType.LS);
+            if (wlist == null) {
+                logger.warn("No matching filterfacets found");
+                dumpFilterFacets();
+            } else {
+                for (Facet f: wlist) {
+                    if (!f.getName().equals(LONGITUDINAL_Q)) {
+                        DefaultFacet df = new DefaultFacet(f.getIndex(),
+                            "longitudinal_section.q", "");
+                        list.add(df);
+                    }
+                }
+
+                list.addAll(wlist);
+
+                filterFacets.put("w_differences", list);
+            }
+        }
+    }
+
+
+    /**
+     * Clone important stuff of an WINFOArtifact.
+     * @param artifact the WINFOArtifact to clone stuff from.
+     */
+    protected void initialize(
+        Artifact artifact,
+        Object context,
+        CallMeta meta)
+    {
+        WINFOArtifact winfo = (WINFOArtifact) artifact;
+        setData(winfo.cloneData());
+        logger.debug("Cloned data of winfo artifact.");
+        // Statically add Facets.
+        List<Facet> fs = new ArrayList<Facet>();
+        DefaultState state = (DefaultState) getCurrentState(context);
+        state.computeInit(this, hash(), context, meta, fs);
+        if (!fs.isEmpty()) {
+            logger.debug("Facets to add in WaterlevelArtifact.initialize .");
+            addFacets(getCurrentStateId(), fs);
+        }
+        else {
+            logger.debug("No facets to add in WaterlevelArtifact.initialize ("
+                + state.getID() + ").");
+        }
+    }
+
+
+    /**
+     * Returns the name of the concrete artifact.
+     *
+     * @return the name of the concrete artifact.
+     */
+    public String getName() {
+        return ARTIFACT_NAME;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/access/Access.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,197 @@
+package org.dive4elements.river.artifacts.access;
+
+import org.dive4elements.artifactdatabase.data.StateData;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+import org.dive4elements.river.artifacts.model.DateRange;
+
+import org.dive4elements.river.utils.FLYSUtils;
+
+import gnu.trove.TDoubleArrayList;
+import gnu.trove.TLongArrayList;
+
+import java.util.ArrayList;
+import java.util.Date;
+
+import org.apache.log4j.Logger;
+
+public class Access
+{
+    private static Logger log = Logger.getLogger(Access.class);
+
+    protected FLYSArtifact artifact;
+
+    public Access() {
+    }
+
+    public Access(FLYSArtifact artifact) {
+        this.artifact = artifact;
+    }
+
+    public FLYSArtifact getArtifact() {
+        return artifact;
+    }
+
+    public void setArtifact(FLYSArtifact artifact) {
+        this.artifact = artifact;
+    }
+
+
+    /** Get a data entry as string. */
+    protected String getString(String key) {
+        StateData sd = artifact.getData(key);
+        if (sd == null) {
+            log.warn("missing '" + key + "' value");
+            return null;
+        }
+        return (String)sd.getValue();
+    }
+
+    /** Get a data entry as double, returns null if string not double valueoffable. */
+    protected Double getDouble(String key) {
+        StateData sd = artifact.getData(key);
+        if (sd == null) {
+            log.warn("missing '" + key + "' value");
+            return null;
+        }
+        try {
+            return Double.valueOf((String)sd.getValue());
+        }
+        catch (NumberFormatException nfe) {
+            log.warn(key + " '" + sd.getValue() + "' is not numeric.");
+        }
+        return null;
+    }
+
+    protected Long getLong(String key) {
+        StateData sd = artifact.getData(key);
+        if (sd == null) {
+            log.warn("missing '" + key + "' value");
+            return null;
+        }
+        try {
+            return Long.valueOf((String)sd.getValue());
+        }
+        catch (NumberFormatException nfe) {
+            log.warn(key + " '" + sd.getValue() + "' is not a long integer.");
+        }
+        return null;
+    }
+
+    protected Integer getInteger(String key) {
+        StateData sd = artifact.getData(key);
+        if (sd == null) {
+            log.warn("missing '" + key + "' value");
+            return null;
+        }
+        try {
+            return Integer.valueOf((String)sd.getValue());
+        }
+        catch (NumberFormatException nfe) {
+            log.warn(key + " '" + sd.getValue() + "' is not a integer.");
+        }
+        return null;
+    }
+
+    protected int [] getIntArray(String key) {
+        StateData sd = artifact.getData(key);
+        if (sd == null) {
+            log.warn("missing '" + key +"' value");
+            return null;
+        }
+        return FLYSUtils.intArrayFromString((String)sd.getValue());
+    }
+
+    protected DateRange [] getDateRange(String key) {
+
+        StateData sd = artifact.getData(key);
+
+        if (sd == null) {
+            log.warn("missing '" + key + "'");
+            return null;
+        }
+
+        String data = (String)sd.getValue();
+        String[] pairs = data.split("\\s*;\\s*");
+
+        ArrayList<DateRange> aPs = new ArrayList<DateRange>(pairs.length);
+
+        for (int i = 0; i < pairs.length; i++) {
+            String[] fromTo = pairs[i].split("\\s*,\\s*");
+            if (fromTo.length >= 2) {
+                try {
+                    Date from = new Date(Long.parseLong(fromTo[0]));
+                    Date to   = new Date(Long.parseLong(fromTo[1]));
+                    DateRange aP = new DateRange(from, to);
+                    if (!aPs.contains(aP)) {
+                        aPs.add(aP);
+                    }
+                }
+                catch (NumberFormatException nfe) {
+                    log.warn(key + " contains no long values.", nfe);
+                }
+            }
+        }
+
+        DateRange [] result = aPs.toArray(new DateRange[aPs.size()]);
+
+        if (log.isDebugEnabled()) {
+            for (int i = 0; i < result.length; ++i) {
+                DateRange ap = result[i];
+                log.debug("period " +
+                    ap.getFrom() + " - " + ap.getTo());
+            }
+        }
+
+        return result;
+    }
+
+    protected Boolean getBoolean(String key) {
+        StateData sd = artifact.getData(key);
+        if (sd == null) {
+            log.warn("missing '" + key + "' value");
+            return null;
+        }
+        return Boolean.valueOf((String)sd.getValue());
+    }
+
+    protected double [] getDoubleArray(String key) {
+        StateData sd = artifact.getData(key);
+        if (sd == null) {
+            log.warn("missing '" + key + "'");
+            return null;
+        }
+        String [] parts = ((String)sd.getValue()).split("[\\s;]+");
+        TDoubleArrayList list = new TDoubleArrayList(parts.length);
+        for (String part: parts) {
+            try {
+                list.add(Double.parseDouble(part));
+            }
+            catch (NumberFormatException nfe) {
+                log.warn("'" + part + "' is not numeric.");
+            }
+        }
+        return list.toNativeArray();
+    }
+
+    protected long [] getLongArray(String key) {
+        StateData sd = artifact.getData(key);
+        if (sd == null) {
+            log.warn("missing '" + key + "'");
+            return null;
+        }
+        String [] parts = ((String)sd.getValue()).split("[\\s;]+");
+        TLongArrayList list = new TLongArrayList(parts.length);
+        for (String part: parts) {
+            try {
+                list.add(Long.parseLong(part));
+            }
+            catch (NumberFormatException nfe) {
+                log.warn("'" + part + "' is not numeric.");
+            }
+        }
+        return list.toNativeArray();
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/access/BedDifferencesAccess.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,53 @@
+package org.dive4elements.river.artifacts.access;
+
+import java.util.Arrays;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.utils.FLYSUtils;
+import org.dive4elements.river.utils.StringUtil;
+
+
+public class BedDifferencesAccess
+extends RiverAccess
+{
+    private static Logger logger = Logger.getLogger(BedDifferencesAccess.class);
+    private String yearEpoch;
+    private String[] diffs;
+
+    private CallContext context;
+
+    public BedDifferencesAccess(FLYSArtifact artifact, CallContext context) {
+        super(artifact);
+        this.context = context;
+    }
+
+    public String getYearEpoch() {
+        yearEpoch = getString("ye_select");
+        return yearEpoch;
+    }
+
+    public FLYSArtifact[][] getDifferenceArtifacts() {
+        diffs = getString("diffids").split("#");
+        logger.debug("diffs: " + Arrays.toString(diffs));
+        FLYSArtifact[][] artifacts = new FLYSArtifact[diffs.length/2][2];
+        for (int i = 0; i < diffs.length; i += 2) {
+            String diff1 = StringUtil.unbracket(diffs[0 + 2*i]);
+            String diff2 = StringUtil.unbracket(diffs[1 + 2*i]);
+            String[] diff1parts = diff1.split(";");
+            String[] diff2parts = diff2.split(";");
+            logger.debug("creating 2 artifacts." + diff1parts[0] + "; " + diff2parts[0]);
+            artifacts[i][0] = FLYSUtils.getArtifact(diff1parts[0], context);
+            artifacts[i][1] = FLYSUtils.getArtifact(diff2parts[0], context);
+        }
+        return artifacts;
+    }
+
+    public static int getHeightId(FLYSArtifact artifact) {
+        Access a = new Access(artifact);
+        return a.getInteger("height_id");
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/access/BedHeightAccess.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,152 @@
+package org.dive4elements.river.artifacts.access;
+
+import java.util.List;
+
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.model.minfo.BedHeight;
+import org.dive4elements.river.artifacts.model.minfo.BedHeightFactory;
+import org.dive4elements.river.artifacts.states.SoundingsSelect;
+import org.dive4elements.river.model.BedHeightSingle;
+import org.dive4elements.river.model.BedHeightSingleValue;
+
+import gnu.trove.TIntArrayList;
+
+import org.apache.log4j.Logger;
+
+
+public class BedHeightAccess
+extends      RangeAccess
+{
+
+    private static final Logger logger = Logger.getLogger(BedHeightAccess.class);
+
+    private int[] singleIDs;
+    private int[] epochIDs;
+
+    private String time;
+
+    public BedHeightAccess(FLYSArtifact artifact, CallContext context) {
+        super(artifact, context);
+    }
+
+
+    public Double getLowerKM() {
+        // TODO update callers
+        return getFrom();
+    }
+
+
+    public Double getUpperKM() {
+        // TODO update callers
+        return getTo();
+    }
+
+
+    public int[] getBedHeightSingleIDs() {
+        if (singleIDs == null) {
+            String data = getString("soundings");
+
+            if (data == null) {
+                logger.warn("No 'soundings' parameter specified!");
+                return null;
+            }
+            else {
+                logger.debug("getBedHeightSingleIDs(): data=" + data);
+            }
+
+            String[] parts = data.split(";");
+
+            TIntArrayList ids = new TIntArrayList();
+
+            for (String part: parts) {
+                if (part.indexOf(SoundingsSelect.PREFIX_SINGLE) >= 0) {
+                    String tmp = part.replace(SoundingsSelect.PREFIX_SINGLE, "");
+
+                    try {
+                        int i = Integer.parseInt(tmp);
+                        if (!ids.contains(i)) {
+                            ids.add(i);
+                        }
+                    }
+                    catch (NumberFormatException nfe) {
+                        logger.warn("Cannot parse int from string: '" + tmp + "'");
+                    }
+                }
+            }
+
+            singleIDs = ids.toNativeArray();
+        }
+
+        return singleIDs;
+    }
+
+
+    public String getYearEpoch() {
+        if (time == null) {
+            time =  getString("ye_select");
+        }
+        return time;
+    }
+
+
+    public int[] getBedHeightEpochIDs() {
+        if (epochIDs == null) {
+            String data = getString("soundings");
+
+            if (data == null) {
+                logger.warn("No 'soundings' parameter specified!");
+                return null;
+            }
+
+            String[] parts = data.split(";");
+
+            TIntArrayList ids = new TIntArrayList();
+
+            for (String part: parts) {
+                if (part.indexOf(SoundingsSelect.PREFIX_EPOCH) >= 0) {
+                    String tmp = part.replace(SoundingsSelect.PREFIX_EPOCH, "");
+
+                    try {
+                        ids.add(Integer.parseInt(tmp));
+                    }
+                    catch (NumberFormatException nfe) {
+                        logger.warn("Cannot parse int from string: '" + tmp + "'");
+                    }
+                }
+            }
+
+            epochIDs = ids.toNativeArray();
+        }
+
+        return epochIDs;
+    }
+
+    /**
+     * Return the {@link BedHeight} at the height_id and time of the artifact
+     * @return {@link BedHeight}
+     */
+    public BedHeight getHeight() {
+        logger.debug("getHeight");
+        return BedHeightFactory.getHeight(
+            artifact.getDataAsString("type"),
+            Integer.parseInt(artifact.getDataAsString("height_id")),
+            Integer.parseInt(artifact.getDataAsString("time")));
+    }
+
+    /**
+     * Return a {@link List} of {@link BedHeightSingleValue}s
+     * at the range of the artifact
+     * @return List of {@link BedHeightSingleValue}s
+     */
+    public List<BedHeightSingleValue> getSingleValues() {
+        logger.debug("getSingleValues");
+        BedHeightSingle single = BedHeightSingle.getBedHeightSingleById(
+                artifact.getDataAsInteger("height_id"));
+        return BedHeightSingleValue.getBedHeightSingleValues(single,
+                getFrom(),
+                getTo());
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/access/BedQualityAccess.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,86 @@
+package org.dive4elements.river.artifacts.access;
+
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.model.DateRange;
+
+
+/** Access data of artifact used in BedQuality calculations. */
+public class BedQualityAccess
+extends      RangeAccess {
+
+    private static final Logger logger = Logger
+        .getLogger(BedQualityAccess.class);
+
+    private List<String> bedDiameter;
+    private List<String> bedloadDiameter;
+    private List<DateRange> ranges;
+
+
+    public BedQualityAccess(FLYSArtifact artifact, CallContext context) {
+        super(artifact, context);
+    }
+
+    public List<DateRange> getDateRanges() {
+        if (ranges == null) {
+            ranges = extractRanges(getString("periods"));
+        }
+        return ranges;
+    }
+
+    public List<String> getBedDiameter() {
+        String value = getString("bed_diameter");
+        if (bedDiameter == null && value != null) {
+            bedDiameter = extractDiameter(value);
+        }
+        return bedDiameter;
+    }
+
+    public List<String> getBedloadDiameter() {
+        String value = getString("load_diameter");
+        if (bedloadDiameter == null && value != null) {
+            bedloadDiameter = extractDiameter(value);
+        }
+        return bedloadDiameter;
+    }
+
+    private List<DateRange> extractRanges(String dateString) {
+        List<DateRange> list = new LinkedList<DateRange>();
+        String[] dates = dateString.split(";");
+        for (String s : dates) {
+            String[] pair = s.split(",");
+            try {
+                long l1      = Long.parseLong(pair[0]);
+                long l2      = Long.parseLong(pair[1]);
+                Date first   = new Date(l1);
+                Date second  = new Date(l2);
+                DateRange dr = new DateRange(first, second);
+                list.add(dr);
+            }
+            catch (NumberFormatException nfe) {
+                continue;
+            }
+        }
+        return list;
+    }
+
+    private List<String> extractDiameter(String value) {
+        List<String> result = new LinkedList<String>();
+        String[] diameter = value.split(";");
+        for (String v : diameter) {
+            logger.debug("diameter: " + v);
+            String[] parts = v.split("\\.");
+            result.add(parts[parts.length - 1]);
+            logger.debug(parts[parts.length-1]);
+        }
+        return result;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/access/Calculation4Access.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,91 @@
+package org.dive4elements.river.artifacts.access;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+import org.dive4elements.river.artifacts.model.Segment;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.river.utils.DoubleUtil;
+
+public class Calculation4Access
+extends      RangeAccess
+{
+    private static Logger log = Logger.getLogger(Calculation4Access.class);
+
+    protected List<Segment> segments;
+
+    protected double [] fromToStep;
+
+    protected Boolean isQ;
+
+    protected Boolean isRange;
+
+
+    public Calculation4Access(FLYSArtifact artifact) {
+        super(artifact, null);
+    }
+
+    public List<Segment> getSegments() {
+        if (segments == null) {
+            String input = getString("wq_values");
+            if (input == null || (input = input.trim()).length() == 0) {
+                log.warn("no wq_values given");
+                segments = Collections.<Segment>emptyList();
+            }
+            else {
+                segments = Segment.parseSegments(input);
+            }
+        }
+        return segments;
+    }
+
+    public boolean isQ() {
+        if (isQ == null) {
+            Boolean value = getBoolean("wq_isq");
+            isQ = value != null && value;
+        }
+        return isQ;
+    }
+
+    public boolean isRange() {
+        if (isRange == null) {
+            String mode = getString("ld_mode");
+            isRange = mode == null || mode.equals("distance");
+        }
+        return isRange;
+    }
+
+    public double [] getFromToStep() {
+        if (fromToStep == null) {
+            // XXX: Is this really needed in this calculation?
+            if (!isRange()) {
+                return null;
+            }
+
+            // XXX: FLYSArtifact sucks!
+            // TODO further use RangeAccess functionality.
+            double [] fromTo = getKmRange();
+
+            if (fromTo == null) {
+                return null;
+            }
+
+            Double dStep = getDouble("ld_step");
+            if (dStep == null) {
+                return null;
+            }
+
+            fromToStep =  new double [] {
+                fromTo[0],
+                fromTo[1],
+                DoubleUtil.round(dStep / 1000d)
+            };
+        }
+        return fromToStep;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/access/ExtremeAccess.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,103 @@
+package org.dive4elements.river.artifacts.access;
+
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+import org.dive4elements.river.artifacts.model.RangeWithValues;
+
+import org.dive4elements.river.utils.DoubleUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+/** Access data used for extreme value analysis. */
+public class ExtremeAccess
+extends      RangeAccess
+{
+    /** Our private logger. */
+    private static Logger logger = Logger.getLogger(ExtremeAccess.class);
+
+    protected Long start;
+    protected Long end;
+
+    protected Double percent;
+
+    protected String function;
+
+    protected List<RangeWithValues> ranges;
+
+
+    public ExtremeAccess(FLYSArtifact artifact, CallContext context) {
+        super(artifact, context);
+    }
+
+
+    /** Returns the percent given. */
+    public Double getPercent() {
+
+        if (percent == null) {
+            percent = getDouble("percent");
+        }
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("percent: '" + percent + "'");
+        }
+
+        return percent;
+    }
+
+    public String getFunction() {
+        if (function == null) {
+            function = getString("function");
+        }
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("function: '" + function + "'");
+        }
+
+        return function;
+    }
+
+
+    /** Find first RangeWithValues for which the given location
+     * is within the range and return its values.
+     * @return values of first suitable rangewithvalues or null.
+     */
+    public double[] getValuesForRange(double location) {
+        logger.debug("ExtemeAcces.getValuesForRange");
+        for (RangeWithValues rangeValues: getRanges()) {
+            if (rangeValues.inside(location)) {
+                return rangeValues.getValues();
+            }
+        }
+        return null;
+    }
+
+
+    public List<RangeWithValues> getRanges() {
+
+        if (ranges == null) {
+            String rangesS = getString("ranges");
+            if (rangesS == null) {
+                return null;
+            }
+            ranges = new ArrayList<RangeWithValues>();
+            DoubleUtil.parseSegments(rangesS, new DoubleUtil.SegmentCallback() {
+                @Override
+                public void newSegment(double from, double to, double [] values) {
+                    ranges.add(new RangeWithValues(from, to, values));
+                }
+            });
+        }
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("ranges: " + ranges);
+        }
+
+        return ranges;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/access/FixAccess.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,114 @@
+package org.dive4elements.river.artifacts.access;
+
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+import java.util.Arrays;
+
+import org.apache.log4j.Logger;
+
+/** Access for Fixation related data. */
+public class FixAccess
+extends      RangeAccess
+{
+    private static Logger log = Logger.getLogger(FixAccess.class);
+
+    protected Long start;
+    protected Long end;
+
+    protected Integer qSectorStart;
+    protected Integer qSectorEnd;
+
+    protected int [] events;
+
+    protected Boolean preprocessing;
+
+    protected String  function;
+
+    public FixAccess(FLYSArtifact artifact, CallContext context) {
+        super(artifact, context);
+    }
+
+    public Long getStart() {
+
+        if (start == null) {
+            start = getLong("start");
+        }
+
+        if (log.isDebugEnabled()) {
+            log.debug("start: '" + start + "'");
+        }
+
+        return start;
+    }
+
+    public Long getEnd() {
+
+        if (end == null) {
+            end = getLong("end");
+        }
+
+        if (log.isDebugEnabled()) {
+            log.debug("end: '" + end + "'");
+        }
+
+        return end;
+    }
+
+    public Integer getQSectorStart() {
+
+        if (qSectorStart == null) {
+            qSectorStart = getInteger("q1");
+        }
+
+        if (log.isDebugEnabled()) {
+            log.debug("q1: '" + qSectorStart + "'");
+        }
+
+        return qSectorStart;
+    }
+
+    public Integer getQSectorEnd() {
+
+        if (qSectorEnd == null) {
+            qSectorEnd = getInteger("q2");
+        }
+
+        if (log.isDebugEnabled()) {
+            log.debug("q2: '" + qSectorEnd + "'");
+        }
+
+        return qSectorEnd;
+    }
+
+    public int [] getEvents() {
+        if (events == null) {
+            events = getIntArray("events");
+        }
+        if (log.isDebugEnabled() && events != null) {
+            log.debug("events: " + Arrays.toString(events));
+        }
+        return events;
+    }
+
+    public Boolean getPreprocessing() {
+        if (preprocessing == null) {
+            preprocessing = getBoolean("preprocessing");
+        }
+        if (log.isDebugEnabled()) {
+            log.debug("preprocessing: " + preprocessing);
+        }
+        return preprocessing;
+    }
+
+    public String getFunction() {
+        if (function == null) {
+            function = getString("function");
+        }
+        if (log.isDebugEnabled()) {
+            log.debug("function: " + function);
+        }
+        return function;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/access/FixAnalysisAccess.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,103 @@
+package org.dive4elements.river.artifacts.access;
+
+import org.dive4elements.artifactdatabase.data.StateData;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+import org.dive4elements.river.artifacts.model.DateRange;
+
+import java.util.Arrays;
+import java.util.Date;
+
+import org.apache.log4j.Logger;
+
+public class FixAnalysisAccess
+extends      FixAccess
+{
+    private static Logger log = Logger.getLogger(FixAnalysisAccess.class);
+
+    protected DateRange    referencePeriod;
+    protected DateRange [] analysisPeriods;
+
+    protected double [] qs;
+
+    public FixAnalysisAccess(FLYSArtifact artifact, CallContext context) {
+        super(artifact, context);
+    }
+
+    public DateRange getReferencePeriod() {
+        if (referencePeriod == null) {
+            StateData refStart = artifact.getData("ref_start");
+            StateData refEnd   = artifact.getData("ref_end");
+
+            if (refStart == null || refEnd == null) {
+                log.warn("missing 'ref_start' or 'ref_start' value");
+                return null;
+            }
+
+            try {
+                long rs = Long.parseLong((String)refStart.getValue());
+                long re = Long.parseLong((String)refEnd  .getValue());
+
+                if (rs > re) { long t = rs; rs = re; re = t; }
+
+                Date from = new Date(rs);
+                Date to   = new Date(re);
+                referencePeriod = new DateRange(from, to);
+            }
+            catch (NumberFormatException nfe) {
+                log.warn("ref_start or ref_end is not an integer.");
+            }
+        }
+
+        return referencePeriod;
+    }
+
+    public DateRange [] getAnalysisPeriods() {
+        if (analysisPeriods == null) {
+            analysisPeriods = getDateRange("ana_data");
+        }
+
+        return analysisPeriods;
+    }
+
+    /**
+     * @return DateRange object ranging from eldest to youngest date
+     * of analysis and reference periods.
+     */
+    public DateRange getDateRange() {
+        DateRange refP = getReferencePeriod();
+
+        if (refP == null) {
+            return null;
+        }
+
+        Date from = refP.getFrom();
+        Date to   = refP.getTo();
+
+        DateRange[] rs = getAnalysisPeriods();
+        for (DateRange r: rs) {
+            if (r.getFrom().before(from)) {
+                from = r.getFrom();
+            }
+            if (r.getTo().after(to)) {
+                to = r.getTo();
+            }
+        }
+
+        return new DateRange(from, to);
+    }
+
+    public double [] getQs() {
+        if (qs == null) {
+            qs = getDoubleArray("qs");
+        }
+
+        if (log.isDebugEnabled() && qs != null) {
+            log.debug("qs: " + Arrays.toString(qs));
+        }
+        return qs;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/access/FixRealizingAccess.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,53 @@
+package org.dive4elements.river.artifacts.access;
+
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+import org.dive4elements.river.artifacts.model.Segment;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+
+/** Fix-Realizing (Volmer/Ausgelagerte Wasserspiegellage) access. */
+public class FixRealizingAccess
+extends      FixAccess
+{
+    private static Logger log = Logger.getLogger(FixRealizingAccess.class);
+
+    protected Boolean isQ;
+
+    protected List<Segment> segments;
+
+    public FixRealizingAccess(FLYSArtifact artifact, CallContext context) {
+        super(artifact, context);
+    }
+
+    public Boolean isQ() {
+        if (isQ == null) {
+            isQ = getBoolean("wq_isq");
+        }
+
+        if (log.isDebugEnabled()) {
+            log.debug("isQ: " + isQ);
+        }
+
+        return isQ;
+    }
+
+    public List<Segment> getSegments() {
+        if (segments == null) {
+            String segmentsS = getString("wq_values");
+            if (segmentsS != null) {
+                segments = Segment.parseSegments(segmentsS);
+            }
+        }
+        if (log.isDebugEnabled()) {
+            log.debug("segments: " + segments);
+        }
+
+        return segments;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/access/FlowVelocityAccess.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,50 @@
+package org.dive4elements.river.artifacts.access;
+
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+
+/** Access to data that deals with flow velocity stuff. */
+public class FlowVelocityAccess
+extends      RangeAccess
+{
+
+    private int[] mainChannels;
+    private int[] totalChannels;
+
+    public FlowVelocityAccess(FLYSArtifact artifact, CallContext context) {
+        super(artifact, context);
+    }
+
+
+    public int[] getMainChannels() {
+        if (mainChannels == null) {
+            mainChannels = getIntArray("main_channel");
+        }
+
+        return mainChannels;
+    }
+
+
+    public int[] getTotalChannels() {
+        if (totalChannels == null) {
+            totalChannels = getIntArray("total_channel");
+        }
+
+        return totalChannels;
+    }
+
+
+    public Double getLowerKM() {
+        // TODO update callers to getFrom
+        return getFrom();
+    }
+
+
+    public Double getUpperKM() {
+        // TODO update callers to getTo
+        return getTo();
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/access/GaugeAccess.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,27 @@
+package org.dive4elements.river.artifacts.access;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.utils.FLYSUtils;
+
+/** For the moment, light-weight wrapper around FLYSUtils. */
+// TODO employ 'Caching' like other Accesses, remove usage of FLYSUtils.
+public class GaugeAccess
+extends RangeAccess
+{
+    private static Logger logger = Logger.getLogger(GaugeAccess.class);
+
+    public GaugeAccess(FLYSArtifact artifact, CallContext context) {
+        super(artifact, context);
+    }
+
+
+    public String getGaugeName() {
+        return FLYSUtils.getGaugename(this.getArtifact());
+    }
+
+    // TODO there is also getGauges in FLYSUtils...
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/access/HistoricalDischargeAccess.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,109 @@
+package org.dive4elements.river.artifacts.access;
+
+import java.util.Date;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.model.Timerange;
+
+
+public class HistoricalDischargeAccess extends RiverAccess {
+
+    public static enum EvaluationMode {
+        W, Q
+    }
+
+    public static final String DATA_EVALUATION_TIME = "year_range";
+    public static final String DATA_EVALUATION_MODE = "historical_mode";
+    public static final String DATA_INPUT_VALUES = "historical_values";
+
+    private Timerange evaluationTimerange;
+    private EvaluationMode evaluationMode;
+
+    private double[] qs;
+    private double[] ws;
+
+    public HistoricalDischargeAccess(FLYSArtifact artifact) {
+        super(artifact);
+    }
+
+    /**
+     * This method returns the evaluation mode. The evaluation mode W is set, if
+     * the <b>DATA_EVALUATION_MODE</b> is 0. Otherwise, the evaluation mode Q is
+     * set.
+     *
+     * @return EvaluationMode.W if the parameter <i>historical_mode</i> is set
+     *         to 0, otherwise EvaluationMode.Q.
+     */
+    public EvaluationMode getEvaluationMode() {
+        if (evaluationMode == null) {
+            int mode = getInteger(DATA_EVALUATION_MODE);
+            evaluationMode = mode == 0 ? EvaluationMode.W : EvaluationMode.Q;
+        }
+
+        return evaluationMode;
+    }
+
+    /**
+     * This method returns the time range specified by <i>year_range</i>
+     * parameter. This parameter has to be a string that consists of two long
+     * values (time millis since 1970) separated by a ';'.
+     *
+     * @return the evaluation time range specified by <i>year_range</i>.
+     */
+    public Timerange getEvaluationTimerange() {
+        if (evaluationTimerange == null) {
+            long[] startend = getLongArray(DATA_EVALUATION_TIME);
+
+            if (startend != null && startend.length > 1) {
+                Date start = new Date(startend[0]);
+                Date end = new Date(startend[1]);
+
+                evaluationTimerange = new Timerange(start, end);
+            }
+        }
+
+        return evaluationTimerange;
+    }
+
+    /**
+     * This method returns the input Q values if the evaluation mode Q is set.
+     * Otherwise, this method will return a double array of length 0. The values
+     * returned by this method are extracted from string parameter
+     * <i>historical_values</i>.
+     *
+     * @return the input Q values or a double array of length 0.
+     */
+    public double[] getQs() {
+        if (qs == null) {
+            if (getEvaluationMode() == EvaluationMode.Q) {
+                qs = getDoubleArray(DATA_INPUT_VALUES);
+            }
+            else {
+                qs = new double[0];
+            }
+        }
+
+        return qs;
+    }
+
+    /**
+     * This method returns the input W values if the evaluation mode W is set.
+     * Otherwise, this method will return a double array of length 0. The values
+     * returned by this method are extracted from string parameter
+     * <i>historical_values</i>.
+     *
+     * @return the input W values or a double array of length 0.
+     */
+    public double[] getWs() {
+        if (ws == null) {
+            if (getEvaluationMode() == EvaluationMode.W) {
+                ws = getDoubleArray(DATA_INPUT_VALUES);
+            }
+            else {
+                ws = new double[0];
+            }
+        }
+
+        return ws;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/access/MapAccess.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,27 @@
+package org.dive4elements.river.artifacts.access;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+
+public class MapAccess
+extends RangeAccess
+{
+
+    public MapAccess(FLYSArtifact artifact, CallContext context) {
+        super(artifact, context);
+    }
+
+    public List<String> getHWS() {
+        String param = getString("uesk.hws");
+        if (param != null) {
+            String[] split = param.split(";");
+            return new ArrayList<String>(Arrays.asList(split));
+        }
+        return new ArrayList<String>();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/access/RangeAccess.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,193 @@
+package org.dive4elements.river.artifacts.access;
+
+import org.apache.log4j.Logger;
+
+import gnu.trove.TDoubleArrayList;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.WINFOArtifact;
+
+import org.dive4elements.river.utils.FLYSUtils;
+
+
+/** For the moment, light-weight wrapper around FLYSUtils. */
+// TODO employ 'Caching' like other Accesses, remove usage of FLYSUtils.
+public class RangeAccess
+extends RiverAccess
+{
+    private static Logger logger = Logger.getLogger(RangeAccess.class);
+
+    private CallContext context;
+
+    public static enum KM_MODE { RANGE, LOCATIONS, NONE };
+
+    double[] kmRange;
+
+    Double from;
+
+    Double to;
+
+    Double step;
+
+    private KM_MODE mode;
+
+
+    public RangeAccess(FLYSArtifact artifact, CallContext context) {
+        super(artifact);
+        this.context = context;
+    }
+
+
+    /** Evaluate the ld_mode data of artifact. */
+    public KM_MODE getKmRangeMode() {
+        if (mode != null) {
+            return mode;
+        }
+        String modeData = getString("ld_mode");
+
+        if (modeData == null || modeData.length() == 0) {
+            mode = KM_MODE.NONE;
+        }
+        else if (modeData.equals("distance"))  {
+            mode = KM_MODE.RANGE;
+        }
+        else if (modeData.equals("locations")) {
+            mode = KM_MODE.LOCATIONS;
+        }
+        else {
+            mode = KM_MODE.NONE;
+        }
+
+        return mode;
+    }
+
+    /**
+     * Return sorted array of locations at which stuff was calculated
+     * (from ld_locations data), null if not parameterized this way.
+     */
+    public double[] getLocations() {
+        String locationStr = getString("ld_locations");
+
+        if (locationStr == null || locationStr.length() == 0) {
+            if (getArtifact() instanceof WINFOArtifact) {
+                WINFOArtifact winfo = (WINFOArtifact) getArtifact();
+                if (winfo.getReferenceStartKm() != null && winfo.getReferenceEndKms() != null) {
+                    return new double[]
+                        {
+                            winfo.getReferenceStartKm().doubleValue(),
+                            winfo.getReferenceEndKms()[0]
+                        };
+                }
+                else if (winfo.getReferenceStartKm() != null) {
+                    return new double[]
+                        {
+                            winfo.getReferenceStartKm().doubleValue(),
+                            winfo.getReferenceStartKm().doubleValue()
+                        };
+                }
+            }
+            return null;
+        }
+
+        String[] tmp               = locationStr.split(" ");
+        TDoubleArrayList locations = new TDoubleArrayList();
+
+        for (String l: tmp) {
+            try {
+                locations.add(Double.parseDouble(l));
+            }
+            catch (NumberFormatException nfe) {
+                logger.debug(nfe.getLocalizedMessage(), nfe);
+            }
+        }
+
+        locations.sort();
+
+        return locations.toNativeArray();
+    }
+
+
+    /** Return ld_from data (in km). */
+    public double getFrom() {
+        if (from == null) {
+            from = getDouble("ld_from");
+        }
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("from: '" + from + "'");
+        }
+
+        return from.doubleValue();
+    }
+
+
+    /** Return ld_to data (in km). */
+    public double getTo() {
+        if (to == null) {
+            to = getDouble("ld_to");
+        }
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("to: '" + to + "'");
+        }
+
+        return to.doubleValue();
+    }
+
+
+    /** Step width for calculation. */
+    public Double getStep() {
+
+        if (step == null) {
+            step = getDouble("ld_step");
+        }
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("step: '" + step + "'");
+        }
+
+        return step;
+    }
+
+
+    /**
+     * Get min and max kilometer, independent of parametization
+     * (ld_from/to vs ld_locations).
+     */
+    public double[] getKmRange() {
+        // TODO store kmRange in field.
+        switch (getKmRangeMode()) {
+            case RANGE: {
+                return getKmFromTo();
+            }
+
+            case LOCATIONS: {
+                double[] locs = getLocations();
+                // if no locations, nPE.
+                if (locs == null) {
+                    logger.warn("no locations to get km range from.");
+                    return new double[] { Double.NaN, Double.NaN };
+                }
+                return new double[] { locs[0], locs[locs.length-1] };
+            }
+
+            case NONE: {
+                double[] locs = getLocations();
+                if (locs != null) {
+                    return new double[] { locs[0], locs[locs.length-1] };
+                }
+                else {
+                    return getKmFromTo();
+                }
+            }
+        }
+
+        return new double[] { Double.NaN, Double.NaN };
+    }
+
+
+    public double[] getKmFromTo() {
+         return FLYSUtils.getKmFromTo(this.getArtifact());
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/access/RiverAccess.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,37 @@
+package org.dive4elements.river.artifacts.access;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+import org.apache.log4j.Logger;
+
+/** Access to river data of an artifact. */
+public class RiverAccess
+extends      Access
+{
+    /** Private logger. */
+    private static Logger log = Logger.getLogger(RiverAccess.class);
+
+    /** River name. */
+    protected String river;
+
+
+    public RiverAccess() {
+    }
+
+    public RiverAccess(FLYSArtifact artifact) {
+        super(artifact);
+    }
+
+
+    /** Get River name. */
+    public String getRiver() {
+        if (river == null) {
+            river = getString("river");
+        }
+        if (log.isDebugEnabled()) {
+            log.debug("river: '" + river + "'");
+        }
+        return river;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/access/SQRelationAccess.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,76 @@
+package org.dive4elements.river.artifacts.access;
+
+import java.util.Date;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.model.DateRange;
+
+public class SQRelationAccess
+extends      RiverAccess
+{
+    private static Logger log = Logger.getLogger(SQRelationAccess.class);
+
+    protected Double    location;
+
+    protected DateRange period;
+
+    protected Double    outliers;
+
+    private String      method;
+
+    public SQRelationAccess() {
+    }
+
+    public SQRelationAccess(FLYSArtifact artifact) {
+        super(artifact);
+    }
+
+    public Double getLocation() {
+        if (location == null) {
+            // XXX: The parameter name suggests plural!?
+            location = getDouble("ld_locations");
+        }
+
+        if (log.isDebugEnabled()) {
+            log.debug("location: " + location);
+        }
+
+        return location;
+    }
+
+    public DateRange getPeriod() {
+        if (period == null) {
+            Long start = getLong("start");
+            Long end   = getLong("end");
+
+            if (start != null && end != null) {
+                period = new DateRange(new Date(start), new Date(end));
+            }
+        }
+
+        return period;
+    }
+
+    public Double getOutliers() {
+        if (outliers == null) {
+            outliers = getDouble("outliers");
+        }
+        if (log.isDebugEnabled()) {
+            log.debug("outliers: " + outliers);
+        }
+        return outliers;
+    }
+
+    public String getOutlierMethod() {
+        if (method == null) {
+            method = getString("outlier-method");
+        }
+        if (log.isDebugEnabled()) {
+            log.debug("outlier-method: " + method);
+        }
+        return method;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/access/SedimentLoadAccess.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,98 @@
+package org.dive4elements.river.artifacts.access;
+
+import gnu.trove.TIntArrayList;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+
+public class SedimentLoadAccess
+extends      RangeAccess
+{
+    private static final Logger logger = Logger.getLogger(BedHeightAccess.class);
+
+    private int[] singleIDs;
+    private int[] epochIDs;
+
+    private String time;
+    private String unit;
+
+    public SedimentLoadAccess(FLYSArtifact artifact, CallContext context) {
+        super(artifact, context);
+    }
+
+    public Double getLowerKM() {
+        // TODO update callers
+        return getFrom();
+    }
+
+    public Double getUpperKM() {
+        // TODO update callers
+        return getTo();
+    }
+
+    public String getYearEpoch() {
+        if (time == null) {
+            time =  getString("ye_select");
+        }
+        return time;
+    }
+
+    public int[] getPeriod() {
+        if (getYearEpoch().equals("year") ) {
+            Integer start = getInteger("start");
+            Integer end = getInteger("end");
+            if (start == null || end == null) {
+                logger.warn("No 'start' or 'end' parameter specified!");
+                return null;
+            }
+
+            return new int[]{start.intValue(), end.intValue()};
+        }
+        return null;
+    }
+
+    public int[][] getEpochs() {
+        if (getYearEpoch().equals("epoch") ||
+            getYearEpoch().equals("off_epoch")
+        ) {
+            String data = getString("epochs");
+
+            if (data == null) {
+                logger.warn("No 'epochs' parameter specified!");
+                return null;
+            }
+
+            String[] parts = data.split(";");
+
+            int[][] list = new int[parts.length][];
+
+            for (int i = 0; i < parts.length; i++) {
+                String[] values = parts[i].split(",");
+                TIntArrayList ints = new TIntArrayList();
+                try {
+                    ints.add(Integer.parseInt(values[0]));
+                    ints.add(Integer.parseInt(values[1]));
+                    list[i] = ints.toNativeArray();
+                }
+                catch (NumberFormatException nfe) {
+                    logger.warn("Cannot parse int from string: '" + values + "'");
+                }
+            }
+            return list;
+        }
+
+        return null;
+    }
+
+    public String getUnit () {
+        if (unit == null) {
+            unit = getString("unit");
+        }
+        return unit;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/access/StaticSQRelationAccess.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,31 @@
+package org.dive4elements.river.artifacts.access;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+
+public class StaticSQRelationAccess
+extends RiverAccess
+{
+    /** The logger that is used in this state. */
+    private static final Logger logger =
+        Logger.getLogger(StaticSQRelationAccess.class);
+
+    private String measurementStation;
+
+    public StaticSQRelationAccess(FLYSArtifact artifact) {
+        super(artifact);
+    }
+
+    /** Get measurement station */
+    public String getMeasurementStation() {
+        if (measurementStation == null) {
+            measurementStation = getString("station");
+        }
+        if (logger.isDebugEnabled()) {
+            logger.debug("measurement station: '" + measurementStation + "'");
+        }
+        return measurementStation;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/cache/CacheFactory.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,79 @@
+package org.dive4elements.river.artifacts.cache;
+
+import org.dive4elements.artifacts.common.utils.Config;
+
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.CacheException;
+import net.sf.ehcache.CacheManager;
+
+import org.apache.log4j.Logger;
+
+public final class CacheFactory
+{
+    private static Logger log = Logger.getLogger(CacheFactory.class);
+
+    public static final String CACHE_CONFIG_FILE_PROPERTY =
+        "flys.artifacts.cache.config.file";
+
+    public static final String XPATH_CACHE_CONFIG_FILE =
+        "/artifact-database/cache/config-file/text()";
+
+    private CacheFactory() {
+    }
+
+    private static boolean      initialized;
+
+    private static CacheManager cacheManager;
+
+    public static final Cache getCache() {
+        return getCache(Cache.DEFAULT_CACHE_NAME);
+    }
+
+    public static final String getConfigFile() {
+        String configFile = System.getProperty(CACHE_CONFIG_FILE_PROPERTY);
+
+        if (configFile != null) {
+            return configFile;
+        }
+
+        configFile = Config.getStringXPath(XPATH_CACHE_CONFIG_FILE);
+
+        if (configFile != null) {
+            configFile = Config.replaceConfigDir(configFile);
+        }
+
+        return configFile;
+    }
+
+    public static final synchronized Cache getCache(String cacheName) {
+        if (!initialized) {
+            initialized = true; // try only once
+            String configFile = getConfigFile();
+            if (configFile != null) {
+                try {
+                    cacheManager = CacheManager.create(configFile);
+                    //System.setProperty(
+                    //  "net.sf.ehcache.enableShutdownHook", "true");
+                    Runtime.getRuntime().addShutdownHook(new Thread() {
+                        public void run() {
+                            log.info("shutting down caches");
+                            for (String name: cacheManager.getCacheNames()) {
+                                log.info("\tflushing '" + name + "'");
+                                cacheManager.getCache(name).flush();
+                            }
+                            cacheManager.shutdown();
+                        }
+                    });
+                }
+                catch (CacheException ce) {
+                    log.error("cannot configure cache", ce);
+                }
+            }
+        }
+
+        return cacheManager != null
+            ? cacheManager.getCache(cacheName)
+            : 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/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/charts/CrossSectionApp.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,694 @@
+package org.dive4elements.river.artifacts.charts;
+
+import org.dive4elements.river.backend.SessionFactoryProvider;
+
+import org.dive4elements.river.artifacts.geom.Lines;
+
+import org.dive4elements.river.model.CrossSection;
+import org.dive4elements.river.model.CrossSectionLine;
+import org.dive4elements.river.model.CrossSectionPoint;
+
+import org.dive4elements.river.utils.Pair;
+
+import org.dive4elements.river.jfree.StableXYDifferenceRenderer;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import java.math.BigDecimal;
+import java.math.MathContext;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import java.awt.Color;
+import java.awt.Paint;
+import java.awt.TexturePaint;
+
+import java.awt.image.BufferedImage;
+
+import javax.swing.DefaultComboBoxModel;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.JTextField;
+
+import javax.swing.event.TableModelEvent;
+import javax.swing.event.TableModelListener;
+
+import javax.swing.table.AbstractTableModel;
+
+import org.hibernate.Query;
+import org.hibernate.Session;
+
+import org.jfree.chart.ChartFactory;
+import org.jfree.chart.ChartPanel;
+import org.jfree.chart.ChartUtilities;
+import org.jfree.chart.JFreeChart;
+
+import org.jfree.chart.axis.NumberAxis;
+
+import org.jfree.chart.plot.PlotOrientation;
+import org.jfree.chart.plot.XYPlot;
+
+import org.jfree.data.xy.DefaultXYDataset;
+import org.jfree.data.xy.XYSeries;
+import org.jfree.data.xy.XYDataset;
+import org.jfree.data.xy.XYSeriesCollection;
+import org.jfree.chart.renderer.xy.XYItemRenderer;
+
+import org.jfree.ui.ApplicationFrame;
+import org.jfree.ui.RefineryUtilities;
+
+
+/**
+ * Standalone tech-demo.
+ */
+public class CrossSectionApp
+extends      ApplicationFrame
+{
+    public static final String RIVER = System.getProperty("river", "Saar");
+
+    public static final String WATER_LEVEL = System.getProperty("waterlevel");
+
+    public static final String KM = System.getProperty("km");
+
+    public static final double EPSILON = 1e-4;
+
+    protected Session session;
+
+    protected JComboBox crossSectionLinesCB;
+    protected JTextField waterlevelTF;
+
+    protected ChartPanel chartPanel;
+
+    protected Double lastWaterLevel;
+
+    protected List<CrossSection> crossSections;
+    protected boolean [] drawCrossSection;
+    protected boolean [] drawWaterLevel;
+    protected boolean [] drawGround;
+    protected boolean [] drawFill;
+
+    protected Map<Double, List<Pair<CrossSection, CrossSectionLine>>> km2lines;
+
+    protected static final Paint TRANSPARENT = createTransparentPaint();
+
+    public class CrossSectionTableModel extends AbstractTableModel {
+
+        @Override
+        public String getColumnName(int col) {
+            switch (col) {
+                case 0: return "Peilungsname";
+                case 1: return "Peilung";
+                case 2: return "Wasserstand";
+                case 3: return "Boden";
+                case 4: return "Wasser";
+            }
+            return "";
+        }
+
+        @Override
+        public int getColumnCount() {
+            return 5;
+        }
+
+        @Override
+        public int getRowCount() {
+            return crossSections != null ? crossSections.size() : 0;
+        }
+
+        @Override
+        public Object getValueAt(int row, int col) {
+            if (crossSections == null) return null;
+            switch (col) {
+                case 0: return crossSections.get(row).getDescription();
+                case 1: return drawCrossSection[row];
+                case 2: return drawWaterLevel[row];
+                case 3: return drawGround[row];
+                case 4: return drawFill[row];
+            }
+            return null;
+        }
+
+        @Override
+        public void setValueAt(Object value, int row, int col) {
+            switch (col) {
+                case 1:
+                    if (change(drawCrossSection, row, (Boolean)value)) {
+                        fireTableCellUpdated(row, col);
+                    }
+                    break;
+                case 2:
+                    if (change(drawWaterLevel, row, (Boolean)value)) {
+                        fireTableCellUpdated(row, col);
+                    }
+                    break;
+                case 3:
+                    if (change(drawGround, row, (Boolean)value)) {
+                        fireTableCellUpdated(row, col);
+                    }
+                    break;
+                case 4:
+                    if (change(drawFill, row, (Boolean)value)) {
+                        fireTableCellUpdated(row, col);
+                    }
+                    break;
+            }
+        }
+
+        @Override
+        public Class<?> getColumnClass(int columnIndex) {
+            switch (columnIndex) {
+                case 0: return String.class;
+                case 1:
+                case 2:
+                case 3:
+                case 4: return Boolean.class;
+            }
+            return null;
+        }
+
+        @Override
+        public boolean isCellEditable(
+            int rowIndex,
+            int columnIndex
+        ) {
+            return columnIndex >= 1 && columnIndex <= 4;
+        }
+    } // class CrossSectionTableModel
+
+    private static boolean change(
+        boolean [] values,
+        int        index,
+        boolean    value
+    ) {
+        if (values[index] != value) {
+            values[index] = value;
+            return true;
+        }
+        return false;
+    }
+
+    public static class CrossSectionLineItem {
+
+        Double km;
+        List<Pair<CrossSection, CrossSectionLine>> lines;
+
+        public CrossSectionLineItem(
+            Double km,
+            List<Pair<CrossSection, CrossSectionLine>> lines
+        ) {
+            this.km    = km;
+            this.lines = lines;
+        }
+
+        public String toString() {
+            return String.valueOf(km);
+        }
+    } // CrossSectionLineItem
+
+    public CrossSectionApp(String title) {
+        super(title);
+
+        session = SessionFactoryProvider
+            .createSessionFactory()
+            .openSession();
+
+        JPanel content = createContent();
+        content.setPreferredSize(new Dimension(800, 480));
+        setContentPane(content);
+    }
+
+
+    /** Query and return CrossSections for named river. */
+    public List<CrossSection> crossSections(String river) {
+        Query query = session.createQuery(
+            "from CrossSection where river.name = :river");
+        query.setParameter("river", river);
+        return query.list();
+    }
+
+    protected Map<Double, List<Pair<CrossSection, CrossSectionLine>>>
+        loadAllLines(List<CrossSection> crossSections) {
+        Map<Double, List<Pair<CrossSection, CrossSectionLine>>> km2lines =
+            new TreeMap<Double, List<Pair<CrossSection, CrossSectionLine>>>();
+        for (CrossSection cs: crossSections) {
+            List<CrossSectionLine> lines = cs.getLines();
+            for (CrossSectionLine csl: lines) {
+                Double km = Math.round(csl.getKm().doubleValue() * 1000d)/1000d;
+                List<Pair<CrossSection, CrossSectionLine>> ls
+                    = km2lines.get(km);
+                if (ls == null) {
+                    ls = new ArrayList<Pair<CrossSection, CrossSectionLine>>(2);
+                    km2lines.put(km, ls);
+                }
+                ls.add(new Pair<CrossSection, CrossSectionLine>(cs, csl));
+            }
+        }
+        return km2lines;
+    }
+
+    public JPanel createContent() {
+        JPanel panel = new JPanel(new BorderLayout());
+
+        JPanel nav = new JPanel(new FlowLayout());
+
+        crossSections = crossSections(RIVER);
+        km2lines = loadAllLines(crossSections);
+
+        int CS = crossSections.size();
+        Arrays.fill(drawCrossSection = new boolean[CS], true);
+        drawWaterLevel = new boolean[CS];
+        drawGround     = new boolean[CS];
+        drawFill       = new boolean[CS];
+
+        Object [] clis = createCrossSectionLineItems(km2lines);
+
+        DefaultComboBoxModel dcbm = new DefaultComboBoxModel(clis);
+
+        crossSectionLinesCB = new JComboBox(dcbm);
+
+        if (KM != null) {
+            try {
+                double km = Double.parseDouble(KM);
+
+                CrossSectionLineItem found = null;
+
+                for (Object o: clis) {
+                    CrossSectionLineItem csli = (CrossSectionLineItem)o;
+                    if (Math.abs(csli.km - km) < EPSILON) {
+                        found = csli;
+                        break;
+                    }
+                }
+
+                if (found != null) {
+                    crossSectionLinesCB.setSelectedItem(found);
+                }
+            }
+            catch (NumberFormatException nfe) {
+                System.err.println("km is not a number: "
+                    + nfe.getMessage());
+            }
+        }
+
+        nav.add(crossSectionLinesCB);
+
+        crossSectionLinesCB.addItemListener(new ItemListener() {
+            @Override
+            public void itemStateChanged(ItemEvent ie) {
+                if (ie.getStateChange() == ItemEvent.SELECTED) {
+                    updateChart();
+                }
+            }
+        });
+
+        waterlevelTF = new JTextField(5);
+
+        if (WATER_LEVEL != null) {
+            try {
+                waterlevelTF.setText(
+                    (lastWaterLevel = Double.valueOf(WATER_LEVEL)).toString());
+            }
+            catch (NumberFormatException nfe) {
+                System.err.println("Water level not a number: " +
+                    nfe.getMessage());
+            }
+        }
+
+        waterlevelTF.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent ae) {
+                waterLevelChanged();
+            }
+        });
+
+        nav.add(waterlevelTF);
+
+        JButton dump = new JButton("dump");
+
+        dump.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent ae) {
+                dumpData();
+            }
+        });
+
+        nav.add(dump);
+
+
+        chartPanel = createChartPanel();
+
+        panel.add(chartPanel, BorderLayout.CENTER);
+
+
+        CrossSectionTableModel cstm = new CrossSectionTableModel();
+
+        cstm.addTableModelListener(new TableModelListener() {
+            @Override
+            public void tableChanged(TableModelEvent e) {
+                updateChart();
+            }
+        });
+
+        JTable crossTable = new JTable(cstm);
+
+        JPanel west = new JPanel(new BorderLayout());
+        JScrollPane scrollPane = new JScrollPane(crossTable);
+        west.add(scrollPane);
+
+        west.add(nav, BorderLayout.SOUTH);
+
+        panel.add(west, BorderLayout.WEST);
+
+        return panel;
+    }
+
+    protected void waterLevelChanged() {
+        String value = waterlevelTF.getText();
+        try {
+            lastWaterLevel = Double.valueOf(value);
+        }
+        catch (NumberFormatException nfe) {
+            waterlevelTF.setText(
+                lastWaterLevel != null ? lastWaterLevel.toString() : "");
+            return;
+        }
+        updateChart();
+    }
+
+    protected void updateChart() {
+
+        JFreeChart chart = createChart();
+
+        chartPanel.setChart(chart);
+    }
+
+    protected ChartPanel createChartPanel() {
+
+        JFreeChart chart = createChart();
+
+        return new ChartPanel(chart);
+    }
+
+    protected void dumpData() {
+
+        CrossSectionLineItem csli =
+            (CrossSectionLineItem)crossSectionLinesCB.getSelectedItem();
+
+        if (csli == null) {
+            return;
+        }
+
+        double km = Math.round(csli.km.doubleValue() * 1000d)/1000d;
+
+        String kmS = String.valueOf(km).replace(".", "-");
+
+        int i = 1;
+        File file = new File("cross-section-" + kmS + ".txt");
+        while (file.exists()) {
+            file = new File("cross-section-" + kmS + "[" + (i++) + "].txt");
+        }
+
+        System.err.println("dump points to file '" + file + "'");
+
+        PrintWriter out = null;
+
+        MathContext mc = new MathContext(3);
+
+        try {
+            out =
+                new PrintWriter(
+                new FileWriter(file));
+
+            for (Pair<CrossSection, CrossSectionLine> pair: csli.lines) {
+                out.println("# " + pair.getA().getDescription());
+                for (CrossSectionPoint point: pair.getB().getPoints()) {
+                    out.println(
+                        new BigDecimal(point.getX()).round(mc) + " " +
+                        new BigDecimal(point.getY()).round(mc));
+                }
+            }
+
+            out.flush();
+        }
+        catch (IOException ioe) {
+            ioe.printStackTrace();
+        }
+        finally {
+            if (out != null) {
+                out.close();
+            }
+        }
+    }
+
+    public void generateWaterLevels(
+        List<Point2D>                         points,
+        List<Pair<XYDataset, XYItemRenderer>> datasets
+    ) {
+        if (points == null || points.isEmpty() || lastWaterLevel == null) {
+            return;
+        }
+
+        double [][] data = Lines.createWaterLines(points, lastWaterLevel).points;
+        XYSeries series =
+            new XYSeries(String.valueOf(lastWaterLevel), false);
+
+        double [] x = data[0];
+        double [] y = data[1];
+        for (int i = 0; i < x.length; ++i) {
+            series.add(x[i], y[i], false);
+        }
+
+        datasets.add(new Pair<XYDataset, XYItemRenderer>(
+            new XYSeriesCollection(series), null));
+    }
+
+    public void generateFill(
+        List<Point2D>                         points,
+        String                                legend,
+        List<Pair<XYDataset, XYItemRenderer>> datasets
+    ) {
+        if (points == null || points.isEmpty() || lastWaterLevel == null) {
+            return;
+        }
+
+        double [][] data   = Lines.createWaterLines(points, lastWaterLevel).points;
+        double [][] values = CrossSectionLine.fetchCrossSectionProfile(points);
+
+        DefaultXYDataset dataset = new DefaultXYDataset();
+
+        dataset.addSeries(legend + "-Linie", values);
+        dataset.addSeries(legend + "-Fl\u00e4che", data);
+
+        datasets.add(new Pair<XYDataset, XYItemRenderer>(
+            dataset,
+            new StableXYDifferenceRenderer(
+                TRANSPARENT, Color.blue, false)));
+    }
+
+    public void generateProfile(
+        List<Point2D>                         points,
+        String                                legend,
+        List<Pair<XYDataset, XYItemRenderer>> datasets
+    ) {
+        if (points == null || points.isEmpty()) {
+            return;
+        }
+
+        double [][] values = CrossSectionLine.fetchCrossSectionProfile(points);
+
+        XYSeries series = new XYSeries(legend, false);
+
+        double [] x = values[0];
+        double [] y = values[1];
+        for (int i = 0; i < x.length; ++i) {
+            series.add(x[i], y[i], false);
+        }
+
+        datasets.add(new Pair<XYDataset, XYItemRenderer>(
+            new XYSeriesCollection(series), null));;
+    }
+
+
+    /**
+     * @param legend the legend entry.
+     */
+    public void generateGround(
+        List<Point2D>                         points,
+        String                                legend,
+        List<Pair<XYDataset, XYItemRenderer>> datasets
+    ) {
+        if (points == null || points.isEmpty()) {
+            return;
+        }
+
+        double [][] values = CrossSectionLine.fetchCrossSectionProfile(points);
+
+        DefaultXYDataset dataset = new DefaultXYDataset();
+
+        dataset.addSeries(legend, values);
+
+        StableXYDifferenceRenderer renderer =
+            new StableXYDifferenceRenderer();
+
+        datasets.add(new Pair<XYDataset, XYItemRenderer>(
+            dataset, renderer));
+    }
+
+    public List<Pair<XYDataset, XYItemRenderer>> generateDatasets() {
+
+        List<Pair<XYDataset, XYItemRenderer>> datasets =
+            new ArrayList<Pair<XYDataset, XYItemRenderer>>();
+
+        CrossSectionLineItem csli =
+            (CrossSectionLineItem)crossSectionLinesCB.getSelectedItem();
+
+        for (int i = 0; i < drawCrossSection.length; ++i) {
+            List<Point2D> points = null;
+            CrossSection cs = crossSections.get(i);
+
+            if (drawGround[i]) {
+                for (Pair<CrossSection, CrossSectionLine> csl: csli.lines) {
+                    if (csl.getA() == cs) {
+                        if (points == null) {
+                            points = csl.getB().fetchCrossSectionLinesPoints();
+                        }
+                        generateGround(
+                            points,
+                            cs.getDescription() + "/Boden",
+                            datasets);
+                        break;
+                    }
+                }
+            }
+
+            if (drawFill[i]) {
+                for (Pair<CrossSection, CrossSectionLine> csl: csli.lines) {
+                    if (csl.getA() == cs) {
+                        if (points == null) {
+                            points = csl.getB().fetchCrossSectionLinesPoints();
+                        }
+
+                        generateFill(
+                            points, cs.getDescription(), datasets);
+                        break;
+                    }
+                }
+            }
+
+            if (drawCrossSection[i]) {
+                for (Pair<CrossSection, CrossSectionLine> csl: csli.lines) {
+                    if (csl.getA() == cs) {
+                        if (points == null) {
+                            points = csl.getB().fetchCrossSectionLinesPoints();
+                        }
+
+                        generateProfile(
+                            points, cs.getDescription(), datasets);
+                        break;
+                    }
+                }
+            }
+
+            if (drawWaterLevel[i]) {
+                for (Pair<CrossSection, CrossSectionLine> csl: csli.lines) {
+                    if (csl.getA() == cs) {
+                        if (points == null) {
+                            points = csl.getB().fetchCrossSectionLinesPoints();
+                        }
+                        generateWaterLevels(points, datasets);
+                        break;
+                    }
+                }
+            }
+
+        }
+
+        return datasets;
+    }
+
+    protected Object [] createCrossSectionLineItems(
+        Map<Double, List<Pair<CrossSection, CrossSectionLine>>> km2lines
+    ) {
+        Object [] result = new Object[km2lines.size()];
+        int i = 0;
+        for (Map.Entry<Double, List<Pair<CrossSection, CrossSectionLine>>> entry:
+            km2lines.entrySet()) {
+            result[i++] = new CrossSectionLineItem(
+                entry.getKey(),
+                entry.getValue());
+        }
+        return result;
+    }
+
+
+    public JFreeChart createChart() {
+        JFreeChart chart = ChartFactory.createXYLineChart(
+            null,
+            "Abstand [m]",
+            "H\u00f6he [m]",
+            null,
+            PlotOrientation.VERTICAL,
+            true,
+            true,
+            false);
+
+        List<Pair<XYDataset, XYItemRenderer>> datasets =
+            generateDatasets();
+
+        XYPlot plot = chart.getXYPlot();
+
+        for (int i = 0, N = datasets.size(); i < N; ++i) {
+            Pair<XYDataset, XYItemRenderer> p = datasets.get(i);
+            plot.setDataset(i, p.getA());
+            plot.mapDatasetToRangeAxis(i, 0);
+            XYItemRenderer renderer = p.getB();
+            if (renderer != null) {
+                plot.setRenderer(i, renderer);
+            }
+        }
+
+        NumberAxis yAxis = (NumberAxis)plot.getRangeAxis();
+        yAxis.setAutoRangeIncludesZero(false);
+
+        ChartUtilities.applyCurrentTheme(chart);
+        return chart;
+    }
+
+    protected static Paint createTransparentPaint() {
+        BufferedImage texture = new BufferedImage(
+            1, 1, BufferedImage.TYPE_4BYTE_ABGR);
+
+        return new TexturePaint(
+            texture, new Rectangle2D.Double(0d, 0d, 0d, 0d));
+    }
+
+    public static void main(String [] args) {
+        CrossSectionApp csa = new CrossSectionApp("Querprofile");
+        csa.pack();
+        RefineryUtilities.centerFrameOnScreen(csa);
+        csa.setVisible(true);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/charts/TimeseriesStepChart.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,219 @@
+package org.dive4elements.river.artifacts.charts;
+
+import java.awt.Dimension;
+import java.util.Date;
+import java.util.GregorianCalendar;
+
+import org.jfree.chart.ChartFactory;
+import org.jfree.chart.ChartPanel;
+import org.jfree.chart.JFreeChart;
+import org.jfree.ui.ApplicationFrame;
+
+import org.jfree.data.time.SimpleTimePeriod;
+import org.jfree.data.time.Second;
+import org.jfree.data.time.TimeSeries;
+import org.jfree.data.time.TimeSeriesCollection;
+import org.jfree.data.time.TimePeriodValues;
+import org.jfree.data.time.TimePeriodValuesCollection;
+
+import org.apache.log4j.Logger;
+import org.apache.log4j.PropertyConfigurator;
+
+
+public class TimeseriesStepChart extends ApplicationFrame {
+
+    private static final Logger logger =
+        Logger.getLogger(TimeseriesStepChart.class);
+
+
+    public static final String LOG4J_PROPERTIES = "FLYS_CLIENT_LOG4J_PROPERIES";
+
+    public static final int USER_CHART_TYPE =
+        Integer.getInteger("chart.type", -1);
+
+    public static final int CHART_TYPE_TIMEPERIOD = 1;
+    public static final int CHART_TYPE_TIMESERIES = 2;
+
+
+    public static void main(String[] args) {
+        configureLogging();
+
+        logger.info("Start TimeseriesStepChart demo.");
+
+        if (USER_CHART_TYPE <= 0) {
+            logger.error("Unknown chart type: " + USER_CHART_TYPE);
+        }
+
+        logger.debug("Create Chart from type: " + USER_CHART_TYPE);
+
+        TimeseriesStepChart tsc = new TimeseriesStepChart();
+        tsc.build(USER_CHART_TYPE);
+
+        logger.info("End of TimeseriesStepChart demo.");
+    }
+
+
+    /** Setup the log4j logging (read LOG4J_PROPERIES envvar). */
+    public static void configureLogging() {
+        String log4jProperties = System.getenv(LOG4J_PROPERTIES);
+
+        if (log4jProperties != null && log4jProperties.length() > 0) {
+            PropertyConfigurator.configure(log4jProperties);
+            logger.info("Log4J logging initialized.");
+        }
+        else {
+            System.out.println("Error while setting up Log4J configuration.");
+        }
+
+        System.out.println("LoggingConfigurator.init finished");
+    }
+
+
+    public TimeseriesStepChart() {
+        super("TimeseriesStepChart");
+    }
+
+
+    public void build(int type) {
+        ChartPanel chartPanel = createChartPanel(type);
+        chartPanel.setPreferredSize(new Dimension(500, 300));
+
+        setContentPane(chartPanel);
+
+        pack();
+        setVisible(true);
+    }
+
+
+    protected ChartPanel createChartPanel(int type) {
+        JFreeChart chart = createJFreeChart(type);
+        ChartPanel panel = new ChartPanel(chart);
+        return panel;
+    }
+
+
+    protected JFreeChart createJFreeChart(int type) {
+        if (type == CHART_TYPE_TIMEPERIOD) {
+            return createTimePeriodValuesChart();
+        }
+        else if (type == CHART_TYPE_TIMESERIES) {
+            return createTimeSeriesChart();
+        }
+
+        logger.error("Unknown chart type: " + type);
+
+        return null;
+    }
+
+
+    public static Date createDate(int year, int month, int day) {
+        GregorianCalendar cal = new GregorianCalendar(year, month, day);
+        return cal.getTime();
+    }
+
+
+
+    //
+    // XXX FOLLOWING METHODS ARE USED FOR "TimePeriodValues" CHART TYPE
+    //
+    protected JFreeChart createTimePeriodValuesChart() {
+        return ChartFactory.createTimeSeriesChart(
+            "TimePeriodValues Chart",
+            "Time Axis",
+            "Y Axis",
+            createTimePeriodValuesDataset(),
+            true,
+            false,
+            false);
+    }
+
+
+    protected TimePeriodValuesCollection createTimePeriodValuesDataset() {
+        TimePeriodValuesCollection dataset = new TimePeriodValuesCollection();
+        TimePeriodValues series = createTimePeriodValues();
+
+        dataset.addSeries(series);
+
+        return dataset;
+    }
+
+
+    protected TimePeriodValues createTimePeriodValues() {
+        TimePeriodValues series1 = new TimePeriodValues("Series 1");
+
+        Date start1 = createDate(2000, 0, 1);
+        Date end1   = createDate(2000, 11, 31);
+
+        logger.debug("START DATE 1 = " + start1);
+        logger.debug("END   DATE 1 = " + end1);
+
+        SimpleTimePeriod period1 = new SimpleTimePeriod(start1, end1);
+
+        Date start2 = createDate(2001, 0, 1);
+        Date end2   = createDate(2001, 11, 31);
+        SimpleTimePeriod period2 = new SimpleTimePeriod(start2, end2);
+
+        Date start3 = createDate(2002, 0, 1);
+        Date end3   = createDate(2002, 11, 31);
+        SimpleTimePeriod period3 = new SimpleTimePeriod(start3, end3);
+
+        series1.add(period1, 100);
+        series1.add(period2, 200);
+        series1.add(period3, 150);
+
+        return series1;
+    }
+
+
+    //
+    // XXX FOLLOWING METHODS ARE USED FOR "TimePeriodValues" CHART TYPE
+    //
+    protected JFreeChart createTimeSeriesChart() {
+        return ChartFactory.createTimeSeriesChart(
+            "TimeSeriesCollection Chart",
+            "Time Axis",
+            "Y Axis",
+            createTimeSeriesCollectionDataset(),
+            true,
+            false,
+            false);
+    }
+
+
+    protected TimeSeriesCollection createTimeSeriesCollectionDataset() {
+        TimeSeriesCollection dataset = new TimeSeriesCollection();
+        TimeSeries[]         series  = createTimeSeries();
+
+        for (TimeSeries tmp: series) {
+            dataset.addSeries(tmp);
+        }
+
+        return dataset;
+    }
+
+
+    protected TimeSeries[] createTimeSeries() {
+        TimeSeries series1 = new TimeSeries("Series 1");
+        series1.add(new Second(0, 0, 0, 1, 1, 2000), 100);
+        series1.add(new Second(59, 59, 23, 31, 12, 2000), 100);
+
+        series1.add(new Second(0, 0, 0, 1, 1, 2001), 200);
+        series1.add(new Second(59, 59, 23, 31, 12, 2001), 200);
+
+        series1.add(new Second(0, 0, 0, 1, 1, 2002), 150);
+        series1.add(new Second(59, 59, 23, 31, 12, 2002), 150);
+
+        TimeSeries series2 = new TimeSeries("Series 2");
+        series2.add(new Second(0, 0, 0, 1, 1, 2000), 10);
+        series2.add(new Second(59, 59, 23, 31, 12, 2000), 10);
+
+        series2.add(new Second(0, 0, 0, 1, 1, 2001), 20);
+        series2.add(new Second(59, 59, 23, 31, 12, 2001), 20);
+
+        series2.add(new Second(0, 0, 0, 1, 1, 2002), 15);
+        series2.add(new Second(59, 59, 23, 31, 12, 2002), 15);
+
+        return new TimeSeries[] { series1, series2 };
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/context/FLYSContext.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,116 @@
+package org.dive4elements.river.artifacts.context;
+
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+
+import org.dive4elements.artifactdatabase.DefaultArtifactContext;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.exports.OutGenerator;
+
+
+/**
+ * This class is used to store application wide information in a global context.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class FLYSContext extends DefaultArtifactContext {
+
+    /** The logger used in this class. */
+    private static Logger logger = Logger.getLogger(FLYSContext.class);
+
+    /** The key that is used to store the StateEngine in the context. */
+    public static final String ARTIFACT_KEY =
+        "artifact";
+
+    /** The key that is used to store the TransitionEngine in the context. */
+    public static final String TRANSITION_ENGINE_KEY =
+        "artifact.transition.engine";
+
+    /** The key that is used to store the StateEngine in the context. */
+    public static final String STATE_ENGINE_KEY =
+        "artifact.state.engine";
+
+    /** The key that is used to store the Map of OutGenerator classes in the
+     * context. */
+    public static final String OUTGENERATORS_KEY =
+        "flys.export.outgenerators";
+
+    /** The key that is used to store the map of themes in the context. */
+    public static final String THEMES =
+        "flys.themes.map";
+
+    /** The key that is used to store a map of theme mappings in the context. */
+    public static final String THEME_MAPPING =
+        "flys.themes.mapping.map";
+
+    /** The key that is used to store a map of WMS urls for each river. */
+    public static final String RIVER_WMS =
+        "flys.floodmap.river.wms";
+
+    /** The key that is used to store an instance of Scheduler in the context.*/
+    public static final String SCHEDULER =
+        "flys.wsplgen.scheduler";
+
+    /** Key to store the configured modules in the context. */
+    public static final String MODULES = "flys.modules";
+
+
+    /**
+     * The default constructor.
+     */
+    public FLYSContext() {
+        super();
+    }
+
+
+    /**
+     * A constructor with a config document.
+     */
+    public FLYSContext(Document config) {
+        super(config);
+    }
+
+    /**
+     * Returns the OutGenerator for a specified <i>type</i>.
+     *
+     * @param name The name of the output type.
+     * @param type Defines the type of the desired OutGenerator.
+     *
+     * @return Instance of an OutGenerator for specified <i>type</i>.
+     */
+    public static OutGenerator getOutGenerator(
+        CallContext context,
+        String      name,
+        String      type)
+    {
+
+        FLYSContext flysContext = context instanceof FLYSContext
+            ? (FLYSContext) context
+            : (FLYSContext) context.globalContext();
+
+        Map<String, Class> generators = (Map<String, Class>)
+            flysContext.get(FLYSContext.OUTGENERATORS_KEY);
+
+        if (generators == null) {
+            return null;
+        }
+
+        Class clazz = generators.get(name);
+
+        try {
+            return clazz != null ? (OutGenerator) clazz.newInstance() : null;
+        }
+        catch (InstantiationException ie) {
+            logger.error(ie, ie);
+        }
+        catch (IllegalAccessException iae) {
+            logger.error(iae, iae);
+        }
+
+        return 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/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/context/FLYSContextFactory.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,521 @@
+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.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.xpath.XPathConstants;
+
+import org.apache.log4j.Logger;
+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.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class FLYSContextFactory implements ArtifactContextFactory {
+
+    /** The logger that is used in this class. */
+    private static Logger logger = Logger.getLogger(FLYSContextFactory.class);
+
+    /** The XPath to the artifacts configured in the configuration. */
+    public static final String XPATH_ARTIFACTS =
+        "/artifact-database/artifacts/artifact";
+
+    /** The XPath to the name of the artifact. */
+    public static final String XPATH_ARTIFACT_NAME = "/artifact/@name";
+
+    /** The XPath to the xlink ref in an artifact configuration. */
+    public static final String XPATH_XLINK = "xlink:href";
+
+    /** The XPath to the transitions configured in the artifact config. */
+    public static final String XPATH_TRANSITIONS =
+        "/artifact/states/transition";
+
+    /** The XPath to the states configured in the artifact config. */
+    public static final String XPATH_STATES =
+        "/artifact/states/state";
+
+    public static final String XPATH_OUTPUT_GENERATORS =
+        "/artifact-database/output-generators/output-generator";
+
+    public static final String XPATH_THEME_CONFIG =
+        "/artifact-database/flys/themes/configuration/text()";
+
+    public static final String XPATH_THEMES =
+        "theme";
+
+    public static final String XPATH_THEME_GROUPS =
+        "/themes/themegroup";
+
+    public static final String XPATH_THEME_MAPPINGS =
+        "/themes/mappings/mapping";
+
+    public static final String XPATH_RIVER_WMS =
+        "/artifact-database/floodmap/river";
+
+    public static final String XPATH_MODULES = "/artifact-database/modules/module";
+
+    private static final String XPATH_ZOOM_SCALES = "/artifact-database/options/zoom-scales/zoom-scale";
+
+    private static final String XPATH_DGM_PATH = "/artifact-database/options/dgm-path/text()";
+
+
+    /**
+     * Creates a new FLYSArtifactContext object and initialize all
+     * components required by the application.
+     *
+     * @param config The artifact server configuration.
+     * @return a FLYSArtifactContext.
+     */
+    @Override
+    public GlobalContext createArtifactContext(Document config) {
+        FLYSContext context = new FLYSContext(config);
+
+        configureTransitions(config, context);
+        configureStates(config, context);
+        configureOutGenerators(config, context);
+        configureThemes(config, context);
+        configureThemesMappings(config, context);
+        configureFloodmapWMS(config, context);
+        configureModules(config, context);
+        configureZoomScales(config, context);
+        configureDGMPath(config, context);
+
+        return context;
+    }
+
+
+    private void configureDGMPath(Document config, FLYSContext context) {
+        String dgmPath = (String) XMLUtils.xpath(
+            config,
+            XPATH_DGM_PATH,
+            XPathConstants.STRING);
+
+        context.put("dgm-path", dgmPath);
+    }
+
+
+    protected void configureZoomScales(Document config, FLYSContext context) {
+        NodeList list = (NodeList)XMLUtils.xpath(
+            config,
+            XPATH_ZOOM_SCALES,
+            XPathConstants.NODESET);
+        ZoomScale scale = new ZoomScale();
+        for (int i = 0; i < list.getLength(); i++) {
+            Element element = (Element)list.item(i);
+            String river = "default";
+            double range = 0d;
+            double radius = 10d;
+            if (element.hasAttribute("river")) {
+                river = element.getAttribute("river");
+            }
+            if (!element.hasAttribute("range")) {
+                continue;
+            }
+            else {
+                String r = element.getAttribute("range");
+                try {
+                    range = Double.parseDouble(r);
+                }
+                catch (NumberFormatException nfe) {
+                    continue;
+                }
+            }
+            if (!element.hasAttribute("radius")) {
+                continue;
+            }
+            else {
+                String r = element.getAttribute("radius");
+                try {
+                    radius = Double.parseDouble(r);
+                }
+                catch (NumberFormatException nfe) {
+                    continue;
+                }
+            }
+            scale.addRange(river, range, radius);
+       }
+       context.put("zoomscale", scale);
+    }
+
+
+    /**
+     * This method initializes the transition configuration.
+     *
+     * @param config the config document.
+     * @param context the FLYSContext.
+     */
+    protected void configureTransitions(Document config, FLYSContext context) {
+        TransitionEngine engine = new TransitionEngine();
+
+        Document[] artifacts = getArtifactConfigurations(config);
+        logger.info("Found " + artifacts.length + " artifacts in the config.");
+
+        for (Document doc: artifacts) {
+
+            String artName = (String) XMLUtils.xpath(
+                doc, XPATH_ARTIFACT_NAME, XPathConstants.STRING);
+
+            NodeList list = (NodeList) XMLUtils.xpath(
+                doc, XPATH_TRANSITIONS, XPathConstants.NODESET);
+
+            if (list == null) {
+                logger.warn("The artifact " + artName +
+                    " has no transitions configured.");
+                continue;
+            }
+
+            int trans = list.getLength();
+
+            logger.info(
+                "Artifact '" + artName + "' has " + trans + " transitions.");
+
+            for (int i = 0; i < trans; i++) {
+                Transition t = TransitionFactory.createTransition(list.item(i));
+                String     s = t.getFrom();
+                engine.addTransition(s, t);
+            }
+        }
+
+        context.put(FLYSContext.TRANSITION_ENGINE_KEY, engine);
+    }
+
+
+    /**
+     * This method returns all artifact documents defined in
+     * <code>config</code>. <br>NOTE: The artifact configurations need to be
+     * stored in own files referenced by an xlink.
+     *
+     * @param config The global configuration.
+     *
+     * @return an array of Artifact configurations.
+     */
+    protected Document[] getArtifactConfigurations(Document config) {
+        NodeList artifacts = (NodeList) XMLUtils.xpath(
+            config, XPATH_ARTIFACTS, XPathConstants.NODESET);
+
+        int count = artifacts.getLength();
+
+        Document[] artifactDocs = new Document[count];
+
+        for (int i = 0; i < count; i++) {
+            Element tmp = (Element) artifacts.item(i);
+
+            String xlink = tmp.getAttribute(XPATH_XLINK);
+            xlink        = Config.replaceConfigDir(xlink);
+
+            File artifactFile = new File(xlink);
+            artifactDocs[i]   = XMLUtils.parseDocument(artifactFile);
+        }
+
+        return artifactDocs;
+    }
+
+
+    /**
+     * This method initializes the transition configuration.
+     *
+     * @param config the config document.
+     * @param context the FLYSContext.
+     */
+    protected void configureStates(Document config, FLYSContext context) {
+        StateEngine engine = new StateEngine();
+
+        Document[] artifacts = getArtifactConfigurations(config);
+        logger.info("Found " + artifacts.length + " artifacts in the config.");
+
+        for (Document doc: artifacts) {
+            List<State> states = new ArrayList<State>();
+
+            String artName = (String) XMLUtils.xpath(
+                doc, XPATH_ARTIFACT_NAME, XPathConstants.STRING);
+
+            NodeList stateList = (NodeList) XMLUtils.xpath(
+                doc, XPATH_STATES, XPathConstants.NODESET);
+
+            if (stateList == null) {
+                logger.warn("The artifact " + artName +
+                    " has no states configured.");
+                continue;
+            }
+
+            int count = stateList.getLength();
+
+            logger.info(
+                "Artifact '" + artName + "' has " + count + " states.");
+
+            for (int i = 0; i < count; i++) {
+                states.add(StateFactory.createState(
+                    stateList.item(i)));
+            }
+
+            engine.addStates(artName, states);
+        }
+
+        context.put(FLYSContext.STATE_ENGINE_KEY, engine);
+    }
+
+
+    /**
+     * This method intializes the provided output generators.
+     *
+     * @param config the config document.
+     * @param context the FLYSContext.
+     */
+    protected void configureOutGenerators(Document config, FLYSContext context){
+        Map<String, Class<?>> generators = new HashMap<String, Class<?>>();
+
+        NodeList outGenerators = (NodeList) XMLUtils.xpath(
+            config,
+            XPATH_OUTPUT_GENERATORS,
+            XPathConstants.NODESET);
+
+        int num = outGenerators == null ? 0 : outGenerators.getLength();
+
+        if (num == 0) {
+            logger.warn("No output generators configured in this application.");
+            return;
+        }
+
+        logger.info("Found " + num + " configured output generators.");
+
+        int idx = 0;
+
+        for (int i = 0; i < num; i++) {
+            Node item = outGenerators.item(i);
+
+            String name = (String) XMLUtils.xpath(
+                item, "@name", XPathConstants.STRING);
+
+            String clazz = (String) XMLUtils.xpath(
+                item, "text()", XPathConstants.STRING);
+
+            if (name == null || clazz == null) {
+                continue;
+            }
+
+            try {
+                generators.put(name, Class.forName(clazz));
+
+                idx++;
+            }
+            catch (ClassNotFoundException cnfe) {
+                logger.warn(cnfe, cnfe);
+            }
+        }
+
+        logger.info("Successfully loaded " + idx + " output generators.");
+        context.put(FLYSContext.OUTGENERATORS_KEY, generators);
+    }
+
+
+    /**
+     * This methods reads the configured themes and puts them into the
+     * FLYSContext.
+     *
+     * @param config The global configuration.
+     * @param context The FLYSContext.
+     */
+    protected void configureThemes(Document config, FLYSContext context) {
+        logger.debug("FLYSContextFactory.configureThemes");
+
+        Document cfg = getThemeConfig(config);
+
+        NodeList themeGroups = (NodeList) XMLUtils.xpath(
+            cfg, XPATH_THEME_GROUPS, XPathConstants.NODESET);
+
+        int groupNum = themeGroups != null ? themeGroups.getLength() : 0;
+
+        if (groupNum == 0) {
+            logger.warn("There are no theme groups configured!");
+        }
+
+        logger.info("Found " + groupNum + " theme groups in configuration");
+
+        List<ThemeGroup> groups = new ArrayList<ThemeGroup>();
+
+        for (int g = 0; g < groupNum; g++) {
+            Element themeGroup = (Element) themeGroups.item(g);
+            NodeList themes = (NodeList) XMLUtils.xpath(
+                themeGroup, XPATH_THEMES, XPathConstants.NODESET);
+
+            int num = themes != null ? themes.getLength() : 0;
+
+            if (num == 0) {
+                logger.warn("There are no themes configured!");
+                return;
+            }
+
+            logger.info("Theme group has " + num + " themes.");
+
+            Map<String, Theme> theThemes = new HashMap<String, Theme>();
+
+            for (int i = 0; i < num; i++) {
+                Node theme = themes.item(i);
+
+                Theme theTheme = ThemeFactory.createTheme(cfg, theme);
+
+                if (theme != null) {
+                    theThemes.put(theTheme.getName(), theTheme);
+                }
+            }
+            String gName = themeGroup.getAttribute("name");
+            groups.add(new ThemeGroup(gName, theThemes));
+
+            logger.info(
+                "Initialized " + theThemes.size() + "/" + num + " themes " +
+                "of theme-group '" + gName + "'");
+        }
+        context.put(FLYSContext.THEMES, groups);
+    }
+
+    /**
+     * This method is used to retrieve the theme configuration document.
+     *
+     * @param config The global configuration.
+     *
+     * @return the theme configuration.
+     */
+    protected Document getThemeConfig(Document config) {
+        String themeConfig = (String) XMLUtils.xpath(
+            config,
+            XPATH_THEME_CONFIG,
+            XPathConstants.STRING);
+
+        themeConfig = Config.replaceConfigDir(themeConfig);
+
+        logger.debug("Parse theme cfg: " + themeConfig);
+
+        return XMLUtils.parseDocument(new File(themeConfig));
+    }
+
+
+    protected void configureThemesMappings(Document cfg, FLYSContext context) {
+        logger.debug("FLYSContextFactory.configureThemesMappings");
+
+        Document config = getThemeConfig(cfg);
+
+        NodeList mappings = (NodeList) XMLUtils.xpath(
+            config, XPATH_THEME_MAPPINGS, XPathConstants.NODESET);
+
+        int num = mappings != null ? mappings.getLength() : 0;
+
+        if (num == 0) {
+            logger.warn("No theme <--> facet mappins found!");
+            return;
+        }
+
+        Map<String, List<ThemeMapping>> mapping =
+            new HashMap<String, List<ThemeMapping>>();
+
+        for (int i = 0; i < num; i++) {
+            Element node = (Element)mappings.item(i);
+
+            String from              = node.getAttribute("from");
+            String to                = node.getAttribute("to");
+            String pattern           = node.getAttribute("pattern");
+            String masterAttrPattern = node.getAttribute("masterAttr");
+            String outputPattern     = node.getAttribute("output");
+
+            if (from.length() > 0 && to.length() > 0) {
+                List<ThemeMapping> tm = mapping.get(from);
+
+                if (tm == null) {
+                    tm = new ArrayList<ThemeMapping>();
+                    mapping.put(from, tm);
+                }
+
+                tm.add(new ThemeMapping(
+                    from, to, pattern, masterAttrPattern, outputPattern));
+            }
+        }
+
+        logger.debug("Found " + mapping.size() + " theme mappings.");
+
+        context.put(FLYSContext.THEME_MAPPING, mapping);
+    }
+
+
+    /**
+     * Reads configured floodmap river WMSs from floodmap.xml and
+     * loads them into the given FLYSContext.
+     * @param cfg
+     * @param context
+     */
+    protected void configureFloodmapWMS(Document cfg, FLYSContext context) {
+        Map<String, String> riverWMS = new HashMap<String, String>();
+
+        NodeList rivers = (NodeList) XMLUtils.xpath(
+            cfg, XPATH_RIVER_WMS, XPathConstants.NODESET);
+
+        int num = rivers != null ? rivers.getLength() : 0;
+
+        for (int i = 0; i < num; i++) {
+            Element e = (Element) rivers.item(i);
+
+            String river = e.getAttribute("name");
+            String url   = XMLUtils.xpathString(e, "river-wms/@url", null);
+
+            if (river != null && url != null) {
+                riverWMS.put(river, url);
+            }
+        }
+
+        logger.debug("Found " + riverWMS.size() + " river WMS.");
+
+        context.put(FLYSContext.RIVER_WMS, riverWMS);
+    }
+
+
+    /**
+     * This method initializes the modules configuration.
+     *
+     * @param config the config document.
+     * @param context the FLYSContext.
+     */
+    protected void configureModules(Document cfg, FLYSContext context) {
+        NodeList modulenodes = (NodeList) XMLUtils.xpath(
+            cfg, XPATH_MODULES, XPathConstants.NODESET);
+
+        int num = modulenodes != null ? modulenodes.getLength() : 0;
+        ArrayList<Module> modules = new ArrayList<Module>(num);
+
+        for (int i = 0; i < num; i++) {
+            Element e = (Element) modulenodes.item(i);
+            String modulename = e.getAttribute("name");
+            String attrselected = e.getAttribute("selected");
+            boolean selected = attrselected == null ? false :
+                attrselected.equalsIgnoreCase("true");
+            logger.debug("Loaded module " + modulename);
+            modules.add(new Module(modulename, selected));
+        }
+        context.put(FLYSContext.MODULES, modules);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/context/SessionCallContextListener.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,68 @@
+package org.dive4elements.river.artifacts.context;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+import org.hibernate.Session;
+
+import org.dive4elements.river.backend.SessionHolder;
+
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.CallContext.Listener;
+
+
+/**
+ * This CallContextListener is used to initialize a ThreadLocal variable in
+ * each CallContext (for each request) that holds Sessions.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class SessionCallContextListener implements Listener {
+
+    public static final String SESSION_KEY = "context.session";
+
+    /** The logger that is used in this class.*/
+    private static Logger logger =
+        Logger.getLogger(SessionCallContextListener.class);
+
+
+    public SessionCallContextListener() {
+    }
+
+
+    public void setup(Document config, Node listenerConfig) {
+        // nothing to do here
+    }
+
+
+    /**
+     * Initializes a ThreadLocal variable that is used to hold sessions.
+     *
+     * @param context The CallContext.
+     */
+    public void init(CallContext context) {
+        logger.debug("SessionCallContextListener.init");
+
+        Session session = SessionHolder.acquire();
+
+        context.putContextValue(SESSION_KEY, session);
+    }
+
+
+    /**
+     * Closes open sessions of the ThreadLocal variable opened in init().
+     *
+     * @param context The CallContext.
+     */
+    public void close(CallContext context) {
+        logger.debug("SessionCallContextListener.close");
+
+        Session session = (Session)context.getContextValue(SESSION_KEY);
+        session.close();
+
+        SessionHolder.release();
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/datacage/DBConfig.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,108 @@
+package org.dive4elements.river.artifacts.datacage;
+
+import org.dive4elements.artifacts.common.utils.Config;
+
+import org.dive4elements.artifactdatabase.db.SQL;
+import org.dive4elements.artifactdatabase.db.DBConnection;
+
+import org.apache.log4j.Logger;
+
+public class DBConfig
+{
+    private static Logger logger = Logger.getLogger(DBConfig.class);
+
+     /**
+     * XPath to access the database driver within the global configuration.
+     */
+    public static final String DB_DRIVER =
+        "/artifact-database/datacage/driver/text()";
+    /**
+     * XPath to access the database URL within the global configuration.
+     */
+    public static final String DB_URL =
+        "/artifact-database/datacage/url/text()";
+    /**
+     * XPath to access the database use within the global configuration.
+     */
+    public static final String DB_USER =
+        "/artifact-database/datacage/user/text()";
+    /**
+     * XPath to access the database password within the global configuration.
+     */
+    public static final String DB_PASSWORD =
+        "/artifact-database/datacage/password/text()";
+
+    /**
+     * The default database driver: H2
+     */
+    public static final String DEFAULT_DRIVER =
+        "org.h2.Driver";
+
+    /**
+     * The default database user: ""
+     */
+    public static final String DEFAULT_USER = "";
+
+    /**
+     * The default database password: ""
+     */
+    public static final String DEFAULT_PASSWORD = "";
+
+
+    public static final String DEFAULT_URL =
+        "jdbc:h2:mem:datacage;INIT=RUNSCRIPT FROM '${artifacts.config.dir}/datacage.sql'";
+
+    public static final String RESOURCE_PATH = "/datacage-sql";
+
+    private static DBConfig instance;
+
+    protected DBConnection dbConnection;
+    protected SQL          sql;
+
+    public DBConfig() {
+    }
+
+    public DBConfig(DBConnection dbConnection, SQL sql) {
+        this.dbConnection = dbConnection;
+        this.sql          = sql;
+    }
+
+    public static synchronized DBConfig getInstance() {
+        if (instance == null) {
+            instance = createInstance();
+        }
+        return instance;
+    }
+
+    protected static DBConfig createInstance() {
+        String driver = Config.getStringXPath(
+            DB_DRIVER, DEFAULT_DRIVER);
+
+        String url = Config.getStringXPath(
+            DB_URL, DEFAULT_URL);
+
+        url = Config.replaceConfigDir(url);
+
+        String user = Config.getStringXPath(
+            DB_USER, DEFAULT_USER);
+
+        String password = Config.getStringXPath(
+            DB_PASSWORD, DEFAULT_PASSWORD);
+
+        DBConnection dbConnection = new DBConnection(
+            driver, url, user, password);
+
+        SQL sql = new SQL(DBConfig.class, RESOURCE_PATH, driver);
+
+        return new DBConfig(dbConnection, sql);
+    }
+
+    public DBConnection getDBConnection() {
+        return dbConnection;
+    }
+
+    public SQL getSQL() {
+        return sql;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/datacage/Datacage.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,1073 @@
+package org.dive4elements.river.artifacts.datacage;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Date;
+
+import java.sql.SQLException;
+import java.sql.PreparedStatement;
+import java.sql.Types;
+import java.sql.Timestamp;
+
+import org.dive4elements.artifacts.GlobalContext;
+import org.dive4elements.artifacts.ArtifactCollection;
+import org.dive4elements.artifacts.User;
+
+import org.dive4elements.artifactdatabase.db.SQL;
+import org.dive4elements.artifactdatabase.db.SQLExecutor;
+
+import org.dive4elements.artifactdatabase.LifetimeListener;
+import org.dive4elements.artifactdatabase.Backend;
+
+import org.dive4elements.artifactdatabase.data.StateData;
+
+import org.dive4elements.artifactdatabase.state.Output;
+import org.dive4elements.artifactdatabase.state.Facet;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.ArtifactDatabase;
+import org.dive4elements.artifacts.ArtifactDatabaseException;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+import org.dive4elements.artifacts.common.utils.LRUCache;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+
+public class Datacage
+implements   LifetimeListener
+{
+    private static Logger log = Logger.getLogger(Datacage.class);
+
+    public static final String DATACAGE_KEY =
+        "global.datacage.instance";
+
+    public static final String ARTEFACT_DATABASE_KEY =
+        "global.artifact.database";
+
+    private String SQL_DELETE_ALL_USERS      = "delete.all.users";
+    private String SQL_DELETE_ALL_ARTIFACTS  = "delete.all.artifacts";
+    private String SQL_USER_ID_NEXTVAL       = "user.id.nextval";
+    private String SQL_USER_BY_GID           = "user.by.gid";
+    private String SQL_INSERT_USER           = "insert.user";
+    private String SQL_COLLECTION_BY_GID     = "collection.by.gid";
+    private String SQL_COLLECTION_ID_NEXTVAL = "collection.id.nextval";
+    private String SQL_INSERT_COLLECTION     = "insert.collection";
+    private String SQL_ARTIFACT_BY_GID       = "artifact.by.gid";
+    private String SQL_COLLECTION_ITEM_ID_NEXTVAL =
+        "collection.item.id.nextval";
+    private String SQL_INSERT_COLLECTION_ITEM = "insert.collection.item";
+    private String SQL_ARTIFACT_ID_NEXTVAL    = "artifact.id.nextval";
+    private String SQL_INSERT_ARTIFACT        = "insert.artifact";
+    private String SQL_ARTIFACT_DATA_ID_NEXTVAL = "artifact.data.id.nextval";
+    private String SQL_INSERT_ARTIFACT_DATA   = "insert.artifact.data";
+    private String SQL_OUT_ID_NEXTVALUE       = "out.id.nextval";
+    private String SQL_INSERT_OUT             = "insert.out";
+    private String SQL_FACET_ID_NEXTVAL       = "facet.id.nextval";
+    private String SQL_INSERT_FACET           = "insert.facet";
+    private String SQL_UPDATE_COLLECTION_NAME = "update.collection.name";
+    private String SQL_DELETE_ARTIFACT_FROM_COLLECTION =
+        "delete.artifact.from.collection";
+    private String SQL_DELETE_COLLECTION_BY_GID =
+        "delete.collection.by.gid";
+    private String SQL_DELETE_USER_BY_GID = "delete.user.by.gid";
+    private String SQL_DELETE_ARTIFACT_DATA_BY_ARTIFACT_ID =
+        "delete.artifact.data.by.artifact.id";
+    private String SQL_DELETE_OUTS_BY_ARTIFACT_ID =
+        "delete.outs.by.artifact.id";
+    private String SQL_DELETE_FACETS_BY_ARTIFACT_ID =
+        "delete.facets.by.artifact.id";
+    private String SQL_DELETE_ARTIFACT_BY_GID =
+        "delete.artifact.by.gid";
+
+    protected SQLExecutor sqlExecutor;
+
+    public class InitialScan
+    implements   ArtifactDatabase.ArtifactLoadedCallback
+    {
+        protected LRUCache<String, Integer> users;
+        protected LRUCache<String, Integer> collections;
+        protected LRUCache<String, Integer> artifacts;
+
+        protected GlobalContext context;
+
+        public InitialScan() {
+            users       = new LRUCache<String, Integer>();
+            collections = new LRUCache<String, Integer>();
+            artifacts   = new LRUCache<String, Integer>();
+        }
+
+        public InitialScan(GlobalContext context) {
+            this();
+            this.context = context;
+        }
+
+        @Override
+        public void artifactLoaded(
+            String   userId,
+            String   collectionId,
+            String   collectionName,
+            Date     collectionCreated,
+            String   artifactId,
+            Date     artifactCreated,
+            Artifact artifact
+        ) {
+            if (!(artifact instanceof FLYSArtifact)) {
+                log.warn("ignoring none FLYS artifacts");
+                return;
+            }
+
+            FLYSArtifact flysArtifact = (FLYSArtifact)artifact;
+
+            Integer uId = getUserId(userId);
+            Integer cId = getCollectionId(
+                collectionId, uId, collectionName, collectionCreated);
+
+            storeArtifact(artifactId, cId, flysArtifact, artifactCreated);
+        }
+
+        protected Integer getId(
+            LRUCache<String, Integer> cache,
+            final String              idString,
+            final String              selectById
+        ) {
+            Integer id = cache.get(idString);
+            if (id != null) {
+                return id;
+            }
+
+            final Integer [] res = new Integer[1];
+
+            SQLExecutor.Instance exec = sqlExecutor.new Instance() {
+                @Override
+                public boolean doIt() throws SQLException {
+                    prepareStatement(selectById);
+                    stmnt.setString(1, idString);
+                    result = stmnt.executeQuery();
+                    if (!result.next()) {
+                        return false;
+                    }
+                    res[0] = result.getInt(1);
+                    return true;
+                }
+            };
+
+            if (exec.runRead()) {
+                cache.put(idString, res[0]);
+                return res[0];
+            }
+
+            return null;
+        }
+
+        protected void storeArtifact(
+            final String       artifactId,
+            Integer            collectionId,
+            final FLYSArtifact artifact,
+            final Date         artifactCreated
+        ) {
+            Integer aId = getId(artifacts, artifactId, SQL_ARTIFACT_BY_GID);
+
+            if (aId != null) {
+                // We've already stored it. Just create the collection item.
+                storeCollectionItem(collectionId, aId);
+                return;
+            }
+            // We need to write it to database
+
+            final Integer [] res = new Integer[1];
+
+            SQLExecutor.Instance exec = sqlExecutor.new Instance() {
+                @Override
+                public boolean doIt() throws SQLException {
+                    prepareStatement(SQL_ARTIFACT_ID_NEXTVAL);
+                    result = stmnt.executeQuery();
+                    if (!result.next()) {
+                        return false;
+                    }
+                    res[0] = result.getInt(1);
+                    reset();
+                    prepareStatement(SQL_INSERT_ARTIFACT);
+                    stmnt.setInt   (1, res[0]);
+                    stmnt.setString(2, artifactId);
+                    stmnt.setString(3, artifact.getCurrentStateId());
+                    Timestamp timestamp = new Timestamp(artifactCreated != null
+                        ? artifactCreated.getTime()
+                        : System.currentTimeMillis());
+                    stmnt.setTimestamp(4, timestamp);
+                    stmnt.execute();
+                    conn.commit();
+                    return true;
+                }
+            };
+
+            if (!exec.runWrite()) {
+                log.error("storing of artifact failed.");
+                return;
+            }
+
+            artifacts.put(artifactId, aId = res[0]);
+
+            storeCollectionItem(collectionId, aId);
+
+            storeData(aId, artifact);
+
+            storeOuts(aId, artifact, context);
+        }
+
+
+        protected void storeCollectionItem(
+            final Integer collectionId,
+            final Integer artifactId
+        ) {
+            SQLExecutor.Instance exec = sqlExecutor.new Instance() {
+                @Override
+                public boolean doIt() throws SQLException {
+                    prepareStatement(SQL_COLLECTION_ITEM_ID_NEXTVAL);
+                    result = stmnt.executeQuery();
+                    if (!result.next()) {
+                        return false;
+                    }
+                    int ciId = result.getInt(1);
+                    reset();
+                    prepareStatement(SQL_INSERT_COLLECTION_ITEM);
+                    stmnt.setInt(1, ciId);
+                    stmnt.setInt(2, collectionId);
+                    stmnt.setInt(3, artifactId);
+                    stmnt.execute();
+                    conn.commit();
+                    return true;
+                }
+            };
+
+            if (!exec.runWrite()) {
+                log.error("storing of collection item failed.");
+            }
+        }
+
+        protected Integer getCollectionId(
+            final String  collectionId,
+            final Integer ownerId,
+            final String  collectionName,
+            final Date    collectionCreated
+        ) {
+            Integer c = getId(collections, collectionId, SQL_COLLECTION_BY_GID);
+
+            if (c != null) {
+                return c;
+            }
+
+            final Integer [] res = new Integer[1];
+
+            SQLExecutor.Instance exec = sqlExecutor.new Instance() {
+                @Override
+                public boolean doIt() throws SQLException {
+                    prepareStatement(SQL_COLLECTION_ID_NEXTVAL);
+                    result = stmnt.executeQuery();
+                    if (!result.next()) {
+                        return false;
+                    }
+                    res[0] = result.getInt(1);
+                    reset();
+                    prepareStatement(SQL_INSERT_COLLECTION);
+                    stmnt.setInt   (1, res[0]);
+                    stmnt.setString(2, collectionId);
+                    stmnt.setInt   (3, ownerId);
+                    setString(stmnt, 4, collectionName);
+                    Timestamp timestamp = new Timestamp(collectionCreated != null
+                        ? collectionCreated.getTime()
+                        : System.currentTimeMillis());
+                    stmnt.setTimestamp(5, timestamp);
+                    stmnt.execute();
+                    conn.commit();
+                    return true;
+                }
+            };
+
+            if (exec.runWrite()) {
+                collections.put(collectionId, res[0]);
+                return res[0];
+            }
+
+            return null;
+        }
+
+        protected Integer getUserId(final String userId) {
+
+            Integer u = getId(users, userId, SQL_USER_BY_GID);
+
+            if (u != null) {
+                return u;
+            }
+
+            final Integer [] res = new Integer[1];
+
+            SQLExecutor.Instance exec = sqlExecutor.new Instance() {
+                @Override
+                public boolean doIt() throws SQLException {
+                    prepareStatement(SQL_USER_ID_NEXTVAL);
+                    result = stmnt.executeQuery();
+                    if (!result.next()) {
+                        return false;
+                    }
+                    res[0] = result.getInt(1);
+                    reset();
+                    prepareStatement(SQL_INSERT_USER);
+                    stmnt.setInt   (1, res[0]);
+                    stmnt.setString(2, userId);
+                    stmnt.execute();
+                    conn.commit();
+                    return true;
+                }
+            };
+
+            if (exec.runWrite()) {
+                users.put(userId, res[0]);
+                return res[0];
+            }
+
+            return null;
+        }
+
+        public boolean scan(ArtifactDatabase adb) {
+            log.debug("scan");
+            try {
+                adb.loadAllArtifacts(this);
+            }
+            catch (ArtifactDatabaseException ade) {
+                log.error(ade);
+                return false;
+            }
+            return true;
+        }
+    } // class InitialScan
+
+
+    public Datacage() {
+    }
+
+    @Override
+    public void setup(Document document) {
+        log.debug("setup");
+        DBConfig config = DBConfig.getInstance();
+        setupSQL(config.getSQL());
+        sqlExecutor = new SQLExecutor(config.getDBConnection());
+    }
+
+    protected void setupSQL(SQL sql) {
+        SQL_DELETE_ALL_USERS      = sql.get(SQL_DELETE_ALL_USERS);
+        SQL_DELETE_ALL_ARTIFACTS  = sql.get(SQL_DELETE_ALL_ARTIFACTS);
+        SQL_USER_ID_NEXTVAL       = sql.get(SQL_USER_ID_NEXTVAL);
+        SQL_USER_BY_GID           = sql.get(SQL_USER_BY_GID);
+        SQL_INSERT_USER           = sql.get(SQL_INSERT_USER);
+        SQL_COLLECTION_BY_GID     = sql.get(SQL_COLLECTION_BY_GID);
+        SQL_COLLECTION_ID_NEXTVAL = sql.get(SQL_COLLECTION_ID_NEXTVAL);
+        SQL_INSERT_COLLECTION     = sql.get(SQL_INSERT_COLLECTION);
+        SQL_ARTIFACT_BY_GID       = sql.get(SQL_ARTIFACT_BY_GID);
+        SQL_COLLECTION_ITEM_ID_NEXTVAL =
+            sql.get(SQL_COLLECTION_ITEM_ID_NEXTVAL);
+        SQL_INSERT_COLLECTION_ITEM =
+            sql.get(SQL_INSERT_COLLECTION_ITEM);
+        SQL_ARTIFACT_ID_NEXTVAL = sql.get(SQL_ARTIFACT_ID_NEXTVAL);
+        SQL_INSERT_ARTIFACT     = sql.get(SQL_INSERT_ARTIFACT);
+        SQL_ARTIFACT_DATA_ID_NEXTVAL = sql.get(SQL_ARTIFACT_DATA_ID_NEXTVAL);
+        SQL_INSERT_ARTIFACT_DATA = sql.get(SQL_INSERT_ARTIFACT_DATA);
+        SQL_OUT_ID_NEXTVALUE     = sql.get(SQL_OUT_ID_NEXTVALUE);
+        SQL_INSERT_OUT           = sql.get(SQL_INSERT_OUT);
+        SQL_FACET_ID_NEXTVAL     = sql.get(SQL_FACET_ID_NEXTVAL);
+        SQL_INSERT_FACET         = sql.get(SQL_INSERT_FACET);
+        SQL_UPDATE_COLLECTION_NAME = sql.get(SQL_UPDATE_COLLECTION_NAME);
+        SQL_DELETE_ARTIFACT_FROM_COLLECTION =
+            sql.get(SQL_DELETE_ARTIFACT_FROM_COLLECTION);
+        SQL_DELETE_COLLECTION_BY_GID = sql.get(SQL_DELETE_COLLECTION_BY_GID);
+        SQL_DELETE_USER_BY_GID       = sql.get(SQL_DELETE_USER_BY_GID);
+        SQL_DELETE_ARTIFACT_DATA_BY_ARTIFACT_ID =
+            sql.get(SQL_DELETE_ARTIFACT_DATA_BY_ARTIFACT_ID);
+        SQL_DELETE_OUTS_BY_ARTIFACT_ID =
+            sql.get(SQL_DELETE_OUTS_BY_ARTIFACT_ID);
+        SQL_DELETE_FACETS_BY_ARTIFACT_ID =
+            sql.get(SQL_DELETE_FACETS_BY_ARTIFACT_ID);
+        SQL_DELETE_ARTIFACT_BY_GID =
+            sql.get(SQL_DELETE_ARTIFACT_BY_GID);
+    }
+
+    protected static final int numFacets(List<Output> outs) {
+        int sum = 0;
+        for (Output out: outs) {
+            sum += out.getFacets().size();
+        }
+        return sum;
+    }
+
+    protected static final void setString(
+        PreparedStatement stmnt,
+        int               index,
+        Object            value
+    )
+    throws SQLException
+    {
+        if (value == null) {
+            stmnt.setNull(index, Types.VARCHAR);
+        }
+        else {
+            stmnt.setString(index, value.toString());
+        }
+    }
+
+    @Override
+    public void systemUp(GlobalContext context) {
+        log.debug("systemUp entered");
+        initialScan(context);
+        context.put(DATACAGE_KEY, this);
+        log.debug("systemUp leaved");
+    }
+
+    protected void initialScan(GlobalContext context) {
+        log.debug("initialScan");
+
+        Object adbObject = context.get(ARTEFACT_DATABASE_KEY);
+
+        if (!(adbObject instanceof ArtifactDatabase)) {
+            log.error("missing artefact database. Cannot scan");
+            return;
+        }
+
+        ArtifactDatabase adb = (ArtifactDatabase)adbObject;
+
+        if (!cleanDatabase()) {
+            log.error("cleaning database failed");
+            return;
+        }
+
+        InitialScan is = new InitialScan(context);
+
+        if (!is.scan(adb)) {
+            log.error("initial scan failed");
+            return;
+        }
+
+    }
+
+    protected boolean cleanDatabase() {
+        log.debug("cleanDatabase");
+
+        boolean success = sqlExecutor.new Instance() {
+            @Override
+            public boolean doIt() throws SQLException {
+                prepareStatement(SQL_DELETE_ALL_USERS);
+                stmnt.execute();
+                prepareStatement(SQL_DELETE_ALL_ARTIFACTS);
+                stmnt.execute();
+                conn.commit();
+                return true;
+            }
+        }.runWrite();
+
+        log.debug("after runWrite(): " + success);
+
+        return success;
+    }
+
+
+    @Override
+    public void systemDown(GlobalContext context) {
+        log.debug("systemDown");
+    }
+
+    public void setup(GlobalContext globalContext) {
+        log.debug("setup");
+    }
+
+    public void createdArtifact(
+        Artifact      artifact,
+        Backend       backend,
+        GlobalContext context
+    ) {
+        log.debug("createdArtifact");
+
+        if (artifact == null) {
+            log.warn("artifact to create is null");
+            return;
+        }
+
+        if (!(artifact instanceof FLYSArtifact)) {
+            log.warn("need FLYSArtifact here (have " + artifact.getClass() + ")");
+            return;
+        }
+
+        final FLYSArtifact flys = (FLYSArtifact)artifact;
+
+        final int [] res = new int[1];
+
+        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
+            @Override
+            public boolean doIt() throws SQLException {
+                prepareStatement(SQL_ARTIFACT_ID_NEXTVAL);
+                result = stmnt.executeQuery();
+                if (!result.next()) {
+                    log.error("id generation for artifact failed");
+                    return false;
+                }
+                res[0] = result.getInt(1);
+                reset();
+                prepareStatement(SQL_INSERT_ARTIFACT);
+                stmnt.setInt      (1, res[0]);
+                stmnt.setString   (2, flys.identifier());
+                stmnt.setString   (3, flys.getCurrentStateId());
+                stmnt.setTimestamp(4,
+                    new Timestamp(System.currentTimeMillis()));
+                stmnt.execute();
+                conn.commit();
+                return true;
+            }
+        };
+
+        if (!exec.runWrite()) {
+            log.error("storing of artifact failed.");
+            return;
+        }
+
+        storeData(res[0], flys);
+        storeOuts(res[0], flys, context);
+    }
+
+    public void storedArtifact(
+        Artifact      artifact,
+        Backend       backend,
+        GlobalContext context
+    ) {
+        log.debug("storedArtifact");
+        if (!(artifact instanceof FLYSArtifact)) {
+            log.warn("need FLYSArtifact here but have a " + artifact.getClass());
+            return;
+        }
+
+        final FLYSArtifact flys = (FLYSArtifact)artifact;
+
+        final Integer [] res = new Integer[1];
+
+        // check first if artifact already exists
+        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
+            @Override
+            public boolean doIt() throws SQLException {
+                prepareStatement(SQL_ARTIFACT_BY_GID);
+                stmnt.setString(1, flys.identifier());
+                result = stmnt.executeQuery();
+                if (!result.next()) {
+                    // new artifact
+                    return true;
+                }
+                res[0] = result.getInt(1);
+                return true;
+            }
+        };
+
+        if (!exec.runRead()) {
+            log.error("querying artifact failed");
+            return;
+        }
+
+        if (res[0] == null) { // new artifact
+            createdArtifact(artifact, backend, context);
+            return;
+        }
+
+        // artifact already exists -> delete old data
+        exec = sqlExecutor.new Instance() {
+            @Override
+            public boolean doIt() throws SQLException {
+                prepareStatement(SQL_DELETE_ARTIFACT_DATA_BY_ARTIFACT_ID);
+                stmnt.setInt(1, res[0]);
+                stmnt.execute();
+                prepareStatement(SQL_DELETE_FACETS_BY_ARTIFACT_ID);
+                stmnt.setInt(1, res[0]);
+                stmnt.execute();
+                prepareStatement(SQL_DELETE_OUTS_BY_ARTIFACT_ID);
+                stmnt.setInt(1, res[0]);
+                stmnt.execute();
+                conn.commit();
+                return true;
+            }
+        };
+
+        if (!exec.runWrite()) {
+            log.error("deleting old artifact data failed");
+            return;
+        }
+
+        // write new data
+        storeData(res[0], flys);
+        storeOuts(res[0], flys, context);
+    }
+
+    public void createdUser(
+        final User    user,
+        Backend       backend,
+        GlobalContext context
+    ) {
+        log.debug("createdUser");
+        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
+            @Override
+            public boolean doIt() throws SQLException {
+                prepareStatement(SQL_USER_ID_NEXTVAL);
+                result = stmnt.executeQuery();
+                if (!result.next()) {
+                    log.error("id generation for user failed");
+                    return false;
+                }
+                int uId = result.getInt(1);
+                reset();
+                prepareStatement(SQL_INSERT_USER);
+                stmnt.setInt(1, uId);
+                stmnt.setString(2, user.identifier());
+                stmnt.execute();
+                conn.commit();
+                return true;
+            }
+        };
+
+        if (!exec.runWrite()) {
+            log.error("create user failed");
+        }
+    }
+
+    public void deletedUser(
+        final String  identifier,
+        Backend       backend,
+        GlobalContext context
+    ) {
+        log.debug("deletedUser");
+        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
+            @Override
+            public boolean doIt() throws SQLException {
+                prepareStatement(SQL_DELETE_USER_BY_GID);
+                stmnt.setString(1, identifier);
+                stmnt.execute();
+                conn.commit();
+                return true;
+            }
+        };
+
+        if (!exec.runWrite()) {
+            log.error("delete user failed");
+        }
+    }
+
+    public void createdCollection(
+        final ArtifactCollection collection,
+        Backend                  backend,
+        GlobalContext            context
+    ) {
+        log.debug("createdCollection");
+        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
+            @Override
+            public boolean doIt() throws SQLException {
+                String userId = collection.getUser().identifier();
+                prepareStatement(SQL_USER_BY_GID);
+                stmnt.setString(1, userId);
+                result = stmnt.executeQuery();
+                int uId;
+                if (result.next()) {
+                    uId = result.getInt(1);
+                    reset();
+                }
+                else {
+                    // need to create user first
+                    reset();
+                    prepareStatement(SQL_USER_ID_NEXTVAL);
+                    result = stmnt.executeQuery();
+                    if (!result.next()) {
+                        log.error("id generation for user failed");
+                        return false;
+                    }
+                    uId = result.getInt(1);
+                    reset();
+                    prepareStatement(SQL_INSERT_USER);
+                    stmnt.setInt(1, uId);
+                    stmnt.setString(2, userId);
+                    stmnt.execute();
+                    conn.commit();
+                    reset();
+                }
+
+                prepareStatement(SQL_COLLECTION_ID_NEXTVAL);
+                result = stmnt.executeQuery();
+                if (!result.next()) {
+                    log.error("id generation for collection failed");
+                    return false;
+                }
+                int cId = result.getInt(1);
+                reset();
+
+                String identifier = collection.identifier();
+                String name       = collection.getName();
+
+                prepareStatement(SQL_INSERT_COLLECTION);
+                stmnt.setInt(1, cId);
+                stmnt.setString(2, identifier);
+                stmnt.setInt(3, uId);
+                setString(stmnt, 4, name);
+                stmnt.setTimestamp(5,
+                    new Timestamp(System.currentTimeMillis()));
+                stmnt.execute();
+
+                conn.commit();
+                return true;
+            }
+        };
+
+        if (!exec.runWrite()) {
+            log.error("create collection failed");
+        }
+    }
+
+    public void deletedCollection(
+        final String  identifier,
+        Backend       backend,
+        GlobalContext context
+    ) {
+        log.debug("deletedCollection");
+        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
+            @Override
+            public boolean doIt() throws SQLException {
+                prepareStatement(SQL_DELETE_COLLECTION_BY_GID);
+                stmnt.setString(1, identifier);
+                stmnt.execute();
+                conn.commit();
+                return true;
+            }
+        };
+
+        if (!exec.runWrite()) {
+            log.error("delete collection failed");
+        }
+    }
+
+    public void changedCollectionAttribute(
+        String        identifier,
+        Document      document,
+        Backend       backend,
+        GlobalContext context
+    ) {
+        log.debug("changedCollectionAttribute");
+    }
+
+    public void changedCollectionItemAttribute(
+        String        collectionId,
+        String        artifactId,
+        Document      document,
+        Backend       backend,
+        GlobalContext context
+    ) {
+        log.debug("changedCollectionItemAttribute");
+    }
+
+    public void addedArtifactToCollection(
+        final String  artifactId,
+        final String  collectionId,
+        Backend       backend,
+        GlobalContext context
+    ) {
+        log.debug("addedArtifactToCollection");
+        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
+            @Override
+            public boolean doIt() throws SQLException {
+                prepareStatement(SQL_ARTIFACT_BY_GID);
+                stmnt.setString(1, artifactId);
+                result = stmnt.executeQuery();
+                if (!result.next()) {
+                    return false;
+                }
+                int aId = result.getInt(1);
+                reset();
+
+                prepareStatement(SQL_COLLECTION_BY_GID);
+                stmnt.setString(1, collectionId);
+                result = stmnt.executeQuery();
+                if (!result.next()) {
+                    return false;
+                }
+                int cId = result.getInt(1);
+                reset();
+
+                prepareStatement(SQL_COLLECTION_ITEM_ID_NEXTVAL);
+                result = stmnt.executeQuery();
+                if (!result.next()) {
+                    return false;
+                }
+                int ciId = result.getInt(1);
+                reset();
+
+                prepareStatement(SQL_INSERT_COLLECTION_ITEM);
+                stmnt.setInt(1, ciId);
+                stmnt.setInt(2, cId);
+                stmnt.setInt(3, aId);
+                stmnt.execute();
+
+                conn.commit();
+                return true;
+            }
+        };
+        if (!exec.runWrite()) {
+            log.error("added artifact to collection failed");
+        }
+    }
+
+    public void removedArtifactFromCollection(
+        final String  artifactId,
+        final String  collectionId,
+        Backend       backend,
+        GlobalContext context
+    ) {
+        log.debug("removedArtifactFromCollection");
+        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
+            @Override
+            public boolean doIt() throws SQLException {
+                prepareStatement(SQL_ARTIFACT_BY_GID);
+                stmnt.setString(1, artifactId);
+                result = stmnt.executeQuery();
+                if (!result.next()) {
+                    return false;
+                }
+                int aId = result.getInt(1);
+                reset();
+                prepareStatement(SQL_COLLECTION_BY_GID);
+                stmnt.setString(1, collectionId);
+                result = stmnt.executeQuery();
+                if (!result.next()) {
+                    return false;
+                }
+                int cId = result.getInt(1);
+                reset();
+                prepareStatement(SQL_DELETE_ARTIFACT_FROM_COLLECTION);
+                stmnt.setInt(1, cId);
+                stmnt.setInt(2, aId);
+                stmnt.execute();
+                conn.commit();
+                return true;
+            }
+        };
+        if (!exec.runWrite()) {
+            log.error("removing artifact from collection failed");
+        }
+    }
+
+    public void setCollectionName(
+        final String  collectionId,
+        final String  name,
+        GlobalContext context
+    ) {
+        log.debug("setCollectionName");
+        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
+            @Override
+            public boolean doIt() throws SQLException {
+                prepareStatement(SQL_UPDATE_COLLECTION_NAME);
+                stmnt.setString(1, name);
+                stmnt.setString(2, collectionId);
+                stmnt.execute();
+                conn.commit();
+                return true;
+            }
+        };
+        if (!exec.runWrite()) {
+            log.error("changing name failed");
+        }
+    }
+
+    protected void storeData(
+        final int     artifactId,
+        FLYSArtifact  artifact
+    ) {
+        final Collection<StateData> data = artifact.getAllData();
+
+        if (data.isEmpty()) {
+            return;
+        }
+
+        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
+            @Override
+            public boolean doIt() throws SQLException {
+                int [] ids = new int[data.size()];
+                prepareStatement(SQL_ARTIFACT_DATA_ID_NEXTVAL);
+
+                for (int i = 0; i < ids.length; ++i) {
+                    result = stmnt.executeQuery();
+                    if (!result.next()) {
+                        log.error("generating id for artifact data failed");
+                        return false;
+                    }
+                    ids[i] = result.getInt(1);
+                    result.close(); result = null;
+                }
+                reset();
+                prepareStatement(SQL_INSERT_ARTIFACT_DATA);
+
+                int i = 0;
+                for (StateData sd: data) {
+                    int id = ids[i++];
+                    stmnt.setInt(1, id);
+                    stmnt.setInt(2, artifactId);
+                    // XXX: Where come the nulls from?
+                    String type = sd.getType();
+                    if (type == null) type = "String";
+                    stmnt.setString(3, type);
+                    stmnt.setString(4, sd.getName());
+                    setString(stmnt, 5, sd.getValue());
+                    stmnt.execute();
+                }
+
+                conn.commit();
+                return true;
+            }
+        };
+
+        if (!exec.runWrite()) {
+            log.error("storing artifact data failed");
+        }
+    }
+
+    protected void storeOuts(
+        final int          artifactId,
+        final FLYSArtifact artifact,
+        GlobalContext      context
+    ) {
+        final List<Output> outs = artifact.getOutputs(context);
+
+        if (outs.isEmpty()) {
+            return;
+        }
+
+        final int [] outIds = new int[outs.size()];
+
+        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
+            @Override
+            public boolean doIt() throws SQLException {
+                prepareStatement(SQL_OUT_ID_NEXTVALUE);
+                for (int i = 0; i < outIds.length; ++i) {
+                    result = stmnt.executeQuery();
+                    if (!result.next()) {
+                        log.error("generation of out ids failed");
+                        return false;
+                    }
+                    outIds[i] = result.getInt(1);
+                    result.close(); result = null;
+                }
+                reset();
+                prepareStatement(SQL_INSERT_OUT);
+                for (int i = 0; i < outIds.length; ++i) {
+                    Output out = outs.get(i);
+                    stmnt.setInt(1, outIds[i]);
+                    stmnt.setInt(2, artifactId);
+                    stmnt.setString(3, out.getName());
+                    setString(stmnt, 4, out.getDescription());
+                    setString(stmnt, 5, out.getType());
+                    stmnt.execute();
+                }
+                conn.commit();
+                return true;
+            }
+        };
+
+        if (!exec.runWrite()) {
+            log.error("storing artifact outs failed");
+            return;
+        }
+
+        final int FACETS = numFacets(outs);
+
+        if (FACETS == 0) {
+            return;
+        }
+
+        exec = sqlExecutor.new Instance() {
+            @Override
+            public boolean doIt() throws SQLException {
+                int [] facetIds = new int[FACETS];
+                prepareStatement(SQL_FACET_ID_NEXTVAL);
+                for (int i = 0; i < facetIds.length; ++i) {
+                    result = stmnt.executeQuery();
+                    if (!result.next()) {
+                        log.error("generation of facet ids failed");
+                        return false;
+                    }
+                    facetIds[i] = result.getInt(1);
+                    result.close(); result = null;
+                }
+                reset();
+                prepareStatement(SQL_INSERT_FACET);
+                int index = 0;
+                for (int i = 0, N = outs.size(); i < N; ++i) {
+                    Output out = outs.get(i);
+                    int outId = outIds[i];
+                    for (Facet facet: out.getFacets()) {
+                        stmnt.setInt(1, facetIds[index]);
+                        stmnt.setInt(2, outId);
+                        stmnt.setString(3, facet.getName());
+                        stmnt.setInt(4, facet.getIndex());
+                        stmnt.setString(5, "XXX"); // TODO: handle states
+                        setString(stmnt, 6, facet.getDescription());
+                        stmnt.execute();
+                        ++index;
+                    }
+                }
+                conn.commit();
+                return true;
+            }
+        };
+
+        if (!exec.runWrite()) {
+            log.error("storing facets failed");
+        }
+    }
+
+    public void killedCollections(
+        final List<String> identifiers,
+        GlobalContext      context
+    ) {
+        log.debug("killedCollections");
+
+        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
+            @Override
+            public boolean doIt() throws SQLException {
+                prepareStatement(SQL_DELETE_COLLECTION_BY_GID);
+                for (String identifier: identifiers) {
+                    stmnt.setString(1, identifier);
+                    stmnt.execute();
+                }
+                conn.commit();
+                return true;
+            }
+        };
+
+        if (!exec.runWrite()) {
+            log.error("killing collections failed");
+        }
+    }
+
+    public void killedArtifacts(
+        final List<String> identifiers,
+        GlobalContext      context
+    ) {
+        log.debug("killedArtifacts");
+
+        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
+            @Override
+            public boolean doIt() throws SQLException {
+                prepareStatement(SQL_DELETE_ARTIFACT_BY_GID);
+                for (String identifier: identifiers) {
+                    stmnt.setString(1, identifier);
+                    stmnt.execute();
+                }
+                conn.commit();
+                return true;
+            }
+        };
+
+        if (!exec.runWrite()) {
+            log.error("killing artifacts failed");
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/datacage/DatacageBackendListener.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,191 @@
+package org.dive4elements.river.artifacts.datacage;
+
+import java.util.List;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.ArtifactCollection;
+import org.dive4elements.artifacts.GlobalContext;
+import org.dive4elements.artifacts.User;
+
+import org.dive4elements.artifactdatabase.BackendListener;
+import org.dive4elements.artifactdatabase.Backend;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+
+/** Triggers Datacage to update db. */
+public class DatacageBackendListener
+implements   BackendListener
+{
+    private static Logger log =
+        Logger.getLogger(DatacageBackendListener.class);
+
+    protected GlobalContext context;
+
+    public DatacageBackendListener() {
+        log.debug("new DatacageBackendListener");
+    }
+
+    protected Datacage getDatacage() {
+        Object listener = context.get(Datacage.DATACAGE_KEY);
+        return listener instanceof Datacage
+            ? (Datacage)listener
+            : null;
+    }
+
+    @Override
+    public void setup(GlobalContext context) {
+        log.debug("setup");
+        this.context = context;
+        Datacage l = getDatacage();
+        if (l != null) {
+            l.setup(context);
+        }
+    }
+
+    @Override
+    public void createdArtifact(Artifact artifact, Backend backend) {
+        log.debug("createdArtifact");
+        Datacage l = getDatacage();
+        if (l != null) {
+            l.createdArtifact(artifact, backend, context);
+        }
+    }
+
+    @Override
+    public void storedArtifact(Artifact artifact, Backend backend) {
+        log.debug("storedArtifact");
+        Datacage l = getDatacage();
+        if (l != null) {
+            l.storedArtifact(artifact, backend, context);
+        }
+    }
+
+    @Override
+    public void createdUser(User user, Backend backend) {
+        log.debug("createdUser");
+        Datacage l = getDatacage();
+        if (l != null) {
+            l.createdUser(user, backend, context);
+        }
+    }
+
+    @Override
+    public void deletedUser(String identifier, Backend backend) {
+        log.debug("deletedUser");
+        Datacage l = getDatacage();
+        if (l != null) {
+            l.deletedUser(identifier, backend, context);
+        }
+    }
+
+    @Override
+    public void createdCollection(
+        ArtifactCollection collection,
+        Backend            backend
+    ) {
+        log.debug("createdCollection");
+        Datacage l = getDatacage();
+        if (l != null) {
+            l.createdCollection(collection, backend, context);
+        }
+    }
+
+    @Override
+    public void deletedCollection(String identifier, Backend backend) {
+        log.debug("deletedCollection");
+        Datacage l = getDatacage();
+        if (l != null) {
+            l.deletedCollection(identifier, backend, context);
+        }
+    }
+
+    @Override
+    public void changedCollectionAttribute(
+        String   identifier,
+        Document document,
+        Backend  backend
+    ) {
+        log.debug("changedCollectionAttribute");
+        Datacage l = getDatacage();
+        if (l != null) {
+            l.changedCollectionAttribute(
+                identifier, document, backend, context);
+        }
+    }
+
+    @Override
+    public void changedCollectionItemAttribute(
+        String   collectionId,
+        String   artifactId,
+        Document document,
+        Backend  backend
+    ) {
+        log.debug("changedCollectionItemAttribute");
+        Datacage l = getDatacage();
+        if (l != null) {
+            l.changedCollectionItemAttribute(
+                collectionId, artifactId, document, backend, context);
+        }
+    }
+
+    @Override
+    public void addedArtifactToCollection(
+        String  artifactId,
+        String  collectionId,
+        Backend backend
+    ) {
+        log.debug("addedArtifactToCollection");
+        Datacage l = getDatacage();
+        if (l != null) {
+            l.addedArtifactToCollection(
+                artifactId, collectionId, backend, context);
+        }
+    }
+
+    @Override
+    public void removedArtifactFromCollection(
+        String  artifactId,
+        String  collectionId,
+        Backend backend
+    ) {
+        log.debug("removedArtifactFromCollection");
+        Datacage l = getDatacage();
+        if (l != null) {
+            l.removedArtifactFromCollection(
+                artifactId, collectionId, backend, context);
+        }
+    }
+
+    @Override
+    public void setCollectionName(
+        String collectionId,
+        String name
+    ) {
+        log.debug("setCollectionName");
+        Datacage l = getDatacage();
+        if (l != null) {
+            l.setCollectionName(collectionId, name, context);
+        }
+    }
+
+    @Override
+    public void killedCollections(List<String> identifiers, Backend backend) {
+        log.debug("killedCollections");
+        Datacage l = getDatacage();
+        if (l != null) {
+            l.killedCollections(identifiers, context);
+        }
+    }
+
+    @Override
+    public void killedArtifacts(List<String> identifiers, Backend backend) {
+        log.debug("killedArtifacts");
+        Datacage l = getDatacage();
+        if (l != null) {
+            l.killedArtifacts(identifiers, context);
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/datacage/Recommendations.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,344 @@
+package org.dive4elements.river.artifacts.datacage;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+import java.util.ArrayList;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.File;
+
+import java.io.FileInputStream;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+import org.hibernate.Session;
+import org.hibernate.SessionFactory;
+
+import org.hibernate.jdbc.Work;
+
+import org.dive4elements.artifacts.common.utils.Config;
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+import org.dive4elements.artifacts.common.utils.StringUtils;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+import org.dive4elements.river.backend.SessionFactoryProvider;
+import org.dive4elements.river.backend.SessionHolder;
+
+import org.dive4elements.artifactdatabase.data.StateData;
+
+import org.dive4elements.river.artifacts.datacage.templating.Builder;
+import org.dive4elements.river.artifacts.datacage.templating.BuilderPool;
+
+
+/**
+ * Also accessible as Singleton with getInstance().
+ */
+public class Recommendations
+{
+    public static final String CONNECTION_USER   = "user";
+    public static final String CONNECTION_SYSTEM = "system";
+    public static final String CONNECTION_SEDDB  = "seddb";
+
+    public static final String DEFAULT_CONNECTION_NAME = CONNECTION_SYSTEM;
+
+    private static Logger log = Logger.getLogger(Recommendations.class);
+
+    private static final boolean DEVELOPMENT_MODE =
+        Boolean.getBoolean("flys.datacage.recommendations.development");
+
+    public static final String XPATH_TEMPLATE =
+        "/artifact-database/metadata/template/text()";
+
+    public static final String DEFAULT_TEMPLATE_PATH =
+        "${artifacts.config.dir}/meta-data.xml";
+
+    private static Recommendations INSTANCE;
+
+    public static class BuilderPoolProvider
+    {
+        protected BuilderPool builderPool;
+
+        public BuilderPoolProvider() {
+        }
+
+        public BuilderPoolProvider(BuilderPool builderPool) {
+            this.builderPool = builderPool;
+        }
+
+        public BuilderPool getBuilderPool() {
+            return builderPool;
+        }
+    } // class BuilderProvider
+
+    public static class FileBuilderPoolProvider
+    extends             BuilderPoolProvider
+    {
+        protected File file;
+        protected long lastModified;
+
+        public FileBuilderPoolProvider() {
+        }
+
+        public FileBuilderPoolProvider(File file) {
+            this.file    = file;
+            lastModified = Long.MIN_VALUE;
+        }
+
+        @Override
+        public synchronized BuilderPool getBuilderPool() {
+            long modified = file.lastModified();
+            if (modified > lastModified) {
+                lastModified = modified;
+                try {
+                    Document template = loadTemplate(file);
+                    builderPool = new BuilderPool(template);
+                }
+                catch (IOException ioe) {
+                    log.error(ioe);
+                }
+            }
+            return builderPool;
+        }
+
+        public BuilderPoolProvider toStaticProvider() {
+            return new BuilderPoolProvider(builderPool);
+        }
+    } // class BuilderProvider
+
+    protected BuilderPoolProvider builderPoolProvider;
+
+    public Recommendations() {
+    }
+
+    public Recommendations(BuilderPoolProvider builderPoolProvider) {
+        this.builderPoolProvider = builderPoolProvider;
+    }
+
+    public BuilderPool getBuilderPool() {
+        return builderPoolProvider.getBuilderPool();
+    }
+
+    protected static void artifactToParameters(
+        FLYSArtifact        artifact,
+        Map<String, Object> parameters
+    ) {
+        parameters.put("CURRENT-STATE-ID", artifact.getCurrentStateId());
+        parameters.put("ARTIFACT-ID",      artifact.identifier());
+
+        for (StateData sd: artifact.getAllData()) {
+            Object value = sd.getValue();
+            if (value == null) {
+                continue;
+            }
+            String key = sd.getName().replace('.', '-').toUpperCase();
+            parameters.put(key, value);
+        }
+    }
+
+    /**
+     * Put Key/Values from \param src to \param dst, but uppercase
+     * both Keys and Values.
+     */
+    public static void convertKeysToUpperCase(
+        Map<String, Object> src,
+        Map<String, Object> dst
+    ) {
+        for (Map.Entry<String, Object> entry: src.entrySet()) {
+            dst.put(entry.getKey().toUpperCase(), entry.getValue());
+        }
+    }
+
+
+    /**
+     * Append recommendations to \param result.
+     * @param extraParameters parameters (typicall example: 'recommended')
+     */
+    public void  recommend(
+        FLYSArtifact        artifact,
+        String              userId,
+        String []           outs,
+        Map<String, Object> extraParameters,
+        Node                result
+    ) {
+        Map<String, Object> parameters = new HashMap<String, Object>();
+
+        if (extraParameters != null) {
+            convertKeysToUpperCase(extraParameters, parameters);
+        }
+
+        if (userId != null) {
+            parameters.put("USER-ID", userId);
+        }
+
+        if (artifact != null) {
+            artifactToParameters(artifact, parameters);
+        }
+
+        parameters.put("ARTIFACT-OUTS", StringUtils.toUpperCase(outs));
+
+        parameters.put("PARAMETERS", parameters);
+
+        recommend(parameters, userId, result);
+    }
+
+
+    /**
+     * Append recommendations to \param result.
+     */
+    public void recommend(
+        Map<String, Object> parameters,
+        String              userId,
+        Node                result
+    ) {
+        recommend(parameters, userId, result, SessionHolder.HOLDER.get());
+    }
+
+    public void recommend(
+        final Map<String, Object> parameters,
+        final String              userId,
+        final Node                result,
+        Session                   systemSession
+    ) {
+        systemSession.doWork(new Work() {
+            @Override
+            public void execute(final Connection systemConnection)
+            throws SQLException
+            {
+                SessionFactory sedDBFactory =
+                    SessionFactoryProvider.getSedDBSessionFactory();
+
+                Session sedDBSession = sedDBFactory.openSession();
+                try {
+                    sedDBSession.doWork(new Work() {
+                        @Override
+                        public void execute(Connection sedDBConnection)
+                        throws SQLException
+                        {
+                            recommend(
+                                parameters, userId, result,
+                                systemConnection,
+                                sedDBConnection);
+                        }
+                    });
+                }
+                finally {
+                    sedDBSession.close();
+                }
+            }
+        });
+    }
+
+    public void recommend(
+        Map<String, Object> parameters,
+        String              userId,
+        Node                result,
+        Connection          systemConnection,
+        Connection          seddbConnection
+    ) throws SQLException
+    {
+        List<Builder.NamedConnection> connections =
+            new ArrayList<Builder.NamedConnection>(3);
+
+        Connection userConnection = userId != null
+            ? DBConfig
+                .getInstance()
+                .getDBConnection()
+                .getDataSource()
+                .getConnection()
+            : null;
+
+        try {
+            connections.add(new Builder.NamedConnection(
+                CONNECTION_SYSTEM, systemConnection, true));
+
+            if (seddbConnection != null) {
+                connections.add(new Builder.NamedConnection(
+                    CONNECTION_SEDDB, seddbConnection, true));
+            }
+
+            if (userConnection != null) {
+                connections.add(new Builder.NamedConnection(
+                    CONNECTION_USER, userConnection, false));
+            }
+
+            getBuilderPool().build(connections, result, parameters);
+        }
+        finally {
+            if (userConnection != null) {
+                userConnection.close();
+            }
+        }
+    }
+
+    /** Get singleton instance. */
+    public static synchronized Recommendations getInstance() {
+        if (INSTANCE == null) {
+            INSTANCE = createRecommendations();
+        }
+        return INSTANCE;
+    }
+
+
+    protected static Document loadTemplate(File file) throws IOException {
+        InputStream in = new FileInputStream(file);
+
+        try {
+            Document template = XMLUtils.parseDocument(in);
+            if (template == null) {
+                throw new IOException("cannot load template");
+            }
+            return template;
+        }
+        finally {
+            in.close();
+        }
+    }
+
+    public static Recommendations createRecommendations(File file) {
+        log.debug("Recommendations.createBuilder");
+
+        if (!file.isFile() || !file.canRead()) {
+            log.error("Cannot open template file '" + file + "'");
+            return null;
+        }
+
+        FileBuilderPoolProvider fbp = new FileBuilderPoolProvider(file);
+
+        if (fbp.getBuilderPool() == null) {
+            log.error("failed loading builder");
+            return null;
+        }
+
+        BuilderPoolProvider bp = DEVELOPMENT_MODE
+            ? fbp
+            : fbp.toStaticProvider();
+
+        return new Recommendations(bp);
+    }
+
+    protected static Recommendations createRecommendations() {
+        log.debug("Recommendations.createRecommendations");
+
+        String path = Config.getStringXPath(XPATH_TEMPLATE);
+
+        if (path == null) {
+            path = DEFAULT_TEMPLATE_PATH;
+        }
+
+        path = Config.replaceConfigDir(path);
+
+        log.info("Meta data template: " + path);
+
+        return createRecommendations(new File(path));
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/datacage/templating/App.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,109 @@
+package org.dive4elements.river.artifacts.datacage.templating;
+
+import java.util.Map;
+import java.util.HashMap;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+
+import org.dive4elements.river.backend.SessionFactoryProvider;
+
+import org.hibernate.Session;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+
+import org.dive4elements.river.artifacts.datacage.Recommendations;
+
+public class App
+{
+    private static Logger log = Logger.getLogger(App.class);
+
+    public static final String template =
+        System.getProperty("meta.data.template", "meta-data.xml");
+
+    public static final String userId =
+        System.getProperty("user.id");
+
+    public static final String PARAMETERS =
+        System.getProperty("meta.data.parameters", "");
+
+    public static final String OUTPUT =
+        System.getProperty("meta.data.output");
+
+    public static Map<String, Object> getParameters() {
+        HashMap<String, Object> map = new HashMap<String, Object>();
+        String [] parts = PARAMETERS.split("\\s*;\\s*");
+        for (String part: parts) {
+            String [] kv = part.split("\\s*:\\s*");
+            if (kv.length < 2 || (kv[0] = kv[0].trim()).length() == 0) {
+                continue;
+            }
+            String [] values = kv[1].split("\\s*,\\s*");
+            map.put(kv[0], values.length == 1 ? values[0] : values);
+        }
+        return map;
+    }
+
+    public static void main(String [] args) {
+
+        Recommendations rec = Recommendations.createRecommendations(
+            new File(template));
+
+        if (rec == null) {
+            System.err.println("No recommendations created");
+            return;
+        }
+
+        final Document result = XMLUtils.newDocument();
+
+        final Map<String, Object> parameters = getParameters();
+
+        Session session = SessionFactoryProvider
+            .createSessionFactory()
+            .openSession();
+
+        try {
+            rec.recommend(parameters, userId, result, session);
+        }
+        finally {
+            session.close();
+        }
+
+        OutputStream out;
+
+        if (OUTPUT == null) {
+            out = System.out;
+        }
+        else {
+            try {
+                out = new FileOutputStream(OUTPUT);
+            }
+            catch (IOException ioe) {
+                log.error(ioe);
+                return;
+            }
+        }
+
+        try {
+            XMLUtils.toStream(result, out);
+        }
+        finally {
+            if (OUTPUT != null) {
+                try {
+                    out.close();
+                }
+                catch (IOException ioe) {
+                    log.error(ioe);
+                }
+            }
+        }
+        System.exit(0);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/datacage/templating/Builder.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,845 @@
+package org.dive4elements.river.artifacts.datacage.templating;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+
+import org.dive4elements.river.utils.Pair;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.xml.namespace.QName;
+
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpression;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.NamedNodeMap;
+
+
+/** Handles and evaluate meta-data template against dbs. */
+public class Builder
+{
+    private static Logger log = Logger.getLogger(Builder.class);
+
+    public static final Pattern STRIP_LINE_INDENT =
+        Pattern.compile("\\s*\\r?\\n\\s*");
+
+    public static final Pattern BRACKET_XPATH =
+        Pattern.compile("\\{([^}]+)\\}");
+
+    public static final String DC_NAMESPACE_URI =
+        "http://www.intevation.org/2011/Datacage";
+
+    private static final Document EVAL_DOCUMENT =
+        XMLUtils.newDocument();
+
+    private static final XPathFactory XPATH_FACTORY =
+        XPathFactory.newInstance();
+
+    protected Document template;
+
+    protected Map<String, CompiledStatement> compiledStatements;
+
+    protected Map<String, Element> macros;
+
+    /** Connection to either of the databases. */
+    public static class NamedConnection {
+
+        protected String     name;
+        protected Connection connection;
+        protected boolean    cached;
+
+        public NamedConnection() {
+        }
+
+        public NamedConnection(
+            String     name,
+            Connection connection
+        ) {
+            this(name, connection, true);
+        }
+
+        public NamedConnection(
+            String     name,
+            Connection connection,
+            boolean    cached
+        ) {
+            this.name       = name;
+            this.connection = connection;
+            this.cached     = cached;
+        }
+    } // class NamedConnection
+
+    public class BuildHelper
+    {
+        protected Node                                     output;
+        protected Document                                 owner;
+        protected StackFrames                              frames;
+        protected List<NamedConnection>                    connections;
+        protected Map<String, CompiledStatement.Instance>  statements;
+        protected Deque<Pair<NamedConnection, ResultData>> connectionsStack;
+        protected Deque<NodeList>                          macroBodies;
+        protected FunctionResolver                         functionResolver;
+        protected Map<String, XPathExpression>             expressions;
+
+
+        public BuildHelper(
+            Node                  output,
+            List<NamedConnection> connections,
+            Map<String, Object>   parameters
+        ) {
+            if (connections.isEmpty()) {
+                throw new IllegalArgumentException("no connections given.");
+            }
+
+            this.connections = connections;
+            connectionsStack =
+                new ArrayDeque<Pair<NamedConnection, ResultData>>();
+            this.output      = output;
+            frames           = new StackFrames(parameters);
+            owner            = getOwnerDocument(output);
+            macroBodies      = new ArrayDeque<NodeList>();
+            functionResolver = new FunctionResolver(this);
+            expressions      = new HashMap<String, XPathExpression>();
+            statements       =
+                new HashMap<String, CompiledStatement.Instance>();
+        }
+
+        public void build() throws SQLException {
+            try {
+                // XXX: Thread safety is now established by the builder pool.
+                //synchronized (template) {
+                    for (Node current: rootsToList()) {
+                        build(output, current);
+                    }
+                //}
+            }
+            finally {
+                closeStatements();
+            }
+        }
+
+        protected void closeStatements() {
+            for (CompiledStatement.Instance csi: statements.values()) {
+                csi.close();
+            }
+            statements.clear();
+        }
+
+        /**
+         * Return first statement node in NodeList, respecting
+         * macros but not doing evaluation (e.g. of <dc:if>s).
+         */
+        private Node findStatementNode(NodeList nodes) {
+            int S = nodes.getLength();
+
+            // Check direct children and take special care of macros.
+            for (int i = 0; i < S; ++i) {
+                Node node = nodes.item(i);
+                String ns;
+                // Regular statement node.
+                if (node.getNodeType() == Node.ELEMENT_NODE
+                && node.getLocalName().equals("statement")
+                && (ns = node.getNamespaceURI()) != null
+                && ns.equals(DC_NAMESPACE_URI)) {
+                    return node;
+                }
+                // Macro node. Descend.
+                else if (node.getNodeType() == Node.ELEMENT_NODE
+                    && node.getLocalName().equals("call-macro")
+                    && (ns = node.getNamespaceURI()) != null
+                    && ns.equals(DC_NAMESPACE_URI)) {
+
+                    String macroName = ((Element)node).getAttribute("name");
+                    Node inMacroNode =
+                        findStatementNode(getMacroChildren(macroName));
+                    if (inMacroNode != null) {
+                        return inMacroNode;
+                    }
+                }
+
+            }
+
+            return null;
+        }
+
+        /**
+         * Handle a dc:context node.
+         */
+        protected void context(Node parent, Element current)
+        throws SQLException
+        {
+            log.debug("dc:context");
+
+            NodeList subs = current.getChildNodes();
+            Node stmntNode = findStatementNode(subs);
+            int S = subs.getLength();
+
+            if (stmntNode == null) {
+                log.warn("dc:context: cannot find statement");
+                return;
+            }
+
+            String stmntText = stmntNode.getTextContent();
+
+            String con = current.getAttribute("connection");
+
+            String key = con + "-" + stmntText;
+
+            CompiledStatement.Instance csi = statements.get(key);
+
+            if (csi == null) {
+                CompiledStatement cs = compiledStatements.get(stmntText);
+                csi = cs.new Instance();
+                statements.put(key, csi);
+            }
+
+            NamedConnection connection = connectionsStack.isEmpty()
+                ? connections.get(0)
+                : connectionsStack.peek().getA();
+
+            if (con.length() > 0) {
+                for (NamedConnection nc: connections) {
+                    if (con.equals(nc.name)) {
+                        connection = nc;
+                        break;
+                    }
+                }
+            }
+
+            ResultData rd = csi.execute(
+                connection.connection,
+                frames,
+                connection.cached);
+
+            // only descent if there are results
+            if (!rd.isEmpty()) {
+                connectionsStack.push(
+                    new Pair<NamedConnection, ResultData>(connection, rd));
+                try {
+                    for (int i = 0; i < S; ++i) {
+                        build(parent, subs.item(i));
+                    }
+                }
+                finally {
+                    connectionsStack.pop();
+                }
+            }
+        }
+
+        public boolean hasResult() {
+            return !connectionsStack.isEmpty()
+                && !connectionsStack.peek().getB().isEmpty();
+        }
+
+        protected ResultData createFilteredResultData(ResultData rd, String filter) {
+            if (filter == null) return rd;
+
+            List<Object []> rows = rd.getRows();
+            String [] columns = rd.getColumnLabels();
+
+            List<Object []> filtered = new ArrayList<Object[]>(rows.size());
+
+            for (Object [] row: rows) {
+                frames.enter();
+                try {
+                    frames.put(columns, row);
+                    boolean traverse = filter == null;
+
+                    if (!traverse) {
+                        Boolean b = evaluateXPathToBoolean(filter);
+                        traverse = b != null && b;
+                    }
+                    if (traverse) {
+                        filtered.add(row);
+                    }
+                }
+                finally {
+                    frames.leave();
+                }
+            }
+            return new ResultData(rd.getColumnLabels(), filtered);
+        }
+
+        protected void filter(Node parent, Element current)
+        throws SQLException
+        {
+            String expr = current.getAttribute("expr");
+
+            if ((expr = expr.trim()).length() == 0) {
+                expr = null;
+            }
+
+            NodeList subs = current.getChildNodes();
+            int S = subs.getLength();
+            if (S == 0) {
+                log.debug("dc:filter has no children");
+                return;
+            }
+
+            ResultData orig = null;
+            Pair<Builder.NamedConnection, ResultData> pair = null;
+
+            if (expr != null && !connectionsStack.isEmpty()) {
+                pair = connectionsStack.peek();
+                orig = pair.getB();
+                pair.setB(createFilteredResultData(orig, expr));
+            }
+
+            try {
+                for (int i = 0; i < S; ++i) {
+                    build(parent, subs.item(i));
+                }
+            }
+            finally {
+                if (orig != null) {
+                    pair.setB(orig);
+                }
+            }
+        }
+
+        /**
+         * Kind of foreach over results of a statement within a context.
+         */
+        protected void foreach(Node parent, Element current)
+        throws SQLException
+        {
+            log.debug("dc:for-each");
+
+            if (connectionsStack.isEmpty()) {
+                log.debug("dc:for-each without having results");
+                return;
+            }
+
+            NodeList subs = current.getChildNodes();
+            int S = subs.getLength();
+
+            if (S == 0) {
+                log.debug("dc:for-each has no children");
+                return;
+            }
+
+            Pair<Builder.NamedConnection, ResultData> pair =
+                connectionsStack.peek();
+
+            ResultData rd = pair.getB();
+
+            String [] columns = rd.getColumnLabels();
+
+            for (Object [] row: rd.getRows()) {
+                frames.enter();
+                try {
+                    frames.put(columns, row);
+                    for (int i = 0; i < S; ++i) {
+                        build(parent, subs.item(i));
+                    }
+                }
+                finally {
+                    frames.leave();
+                }
+            }
+        }
+
+        /**
+         * Create element.
+         */
+        protected void element(Node parent, Element current)
+        throws SQLException
+        {
+            String attr = expand(current.getAttribute("name"));
+
+            if (log.isDebugEnabled()) {
+                log.debug("dc:element -> '" + attr + "'");
+            }
+
+            if (attr.length() == 0) {
+                log.warn("no name attribute found");
+                return;
+            }
+
+            Element element = owner.createElement(attr);
+
+            NodeList children = current.getChildNodes();
+            for (int i = 0, N = children.getLength(); i < N; ++i) {
+                build(element, children.item(i));
+            }
+
+            parent.appendChild(element);
+        }
+
+        protected void text(Node parent, Element current)
+        throws SQLException
+        {
+            log.debug("dc:text");
+            String value = expand(current.getTextContent());
+            parent.appendChild(owner.createTextNode(value));
+        }
+
+        /**
+         * Add attribute to an element
+         * @see Element
+         */
+        protected void attribute(Node parent, Element current) {
+
+            if (parent.getNodeType() != Node.ELEMENT_NODE) {
+                log.warn("need element here");
+                return;
+            }
+
+            String name  = expand(current.getAttribute("name"));
+            String value = expand(current.getAttribute("value"));
+
+            Element element = (Element)parent;
+
+            element.setAttribute(name, value);
+        }
+
+        /**
+         * Call-Macro node.
+         * Evaluate child-nodes of the given macro element (not its text).
+         */
+        protected void callMacro(Node parent, Element current)
+        throws SQLException
+        {
+            String name = current.getAttribute("name");
+
+            if (name.length() == 0) {
+                log.warn("missing 'name' attribute in 'call-macro'");
+                return;
+            }
+
+            Element macro = macros.get(name);
+
+            if (macro != null) {
+                macroBodies.push(current.getChildNodes());
+                try {
+                    NodeList subs = macro.getChildNodes();
+                    for (int j = 0, M = subs.getLength(); j < M; ++j) {
+                        build(parent, subs.item(j));
+                    }
+                }
+                finally {
+                    macroBodies.pop();
+                }
+            }
+            else {
+                log.warn("no macro '" + name + "' found.");
+            }
+        }
+
+        protected void macroBody(Node parent, Element current)
+        throws SQLException
+        {
+            if (!macroBodies.isEmpty()) {
+                NodeList children = macroBodies.peek();
+                for (int i = 0, N = children.getLength(); i < N; ++i) {
+                    build(parent, children.item(i));
+                }
+            }
+            else {
+                log.warn("no current macro");
+            }
+        }
+
+        /** Get macro node children, not resolving bodies. */
+        protected NodeList getMacroChildren(String name) {
+            NodeList macros = template.getElementsByTagNameNS(
+                DC_NAMESPACE_URI, "macro");
+
+            Element macro = null;
+
+            for (int i = 0, N = macros.getLength(); i < N; ++i) {
+                Element m = (Element) macros.item(i);
+                if (name.equals(m.getAttribute("name"))) {
+                    macro = m;
+                    break;
+                }
+            }
+
+            if (macro != null) {
+                return macro.getChildNodes();
+            }
+            return null;
+        }
+
+        protected void ifClause(Node parent, Element current)
+        throws SQLException
+        {
+            String test = current.getAttribute("test");
+
+            if (test.length() == 0) {
+                log.warn("missing 'test' attribute in 'if'");
+                return;
+            }
+
+            Boolean result = evaluateXPathToBoolean(test);
+
+            if (result != null && result.booleanValue()) {
+                NodeList subs = current.getChildNodes();
+                for (int i = 0, N = subs.getLength(); i < N; ++i) {
+                    build(parent, subs.item(i));
+                }
+            }
+        }
+
+        protected void choose(Node parent, Element current)
+        throws SQLException
+        {
+            Node branch = null;
+
+            NodeList children = current.getChildNodes();
+            for (int i = 0, N = children.getLength(); i < N; ++i) {
+                Node child = children.item(i);
+                String ns = child.getNamespaceURI();
+                if (ns == null
+                || !ns.equals(DC_NAMESPACE_URI)
+                || child.getNodeType() != Node.ELEMENT_NODE
+                ) {
+                    continue;
+                }
+                String name = child.getLocalName();
+                if ("when".equals(name)) {
+                    Element when = (Element)child;
+                    String test = when.getAttribute("test");
+                    if (test.length() == 0) {
+                        log.warn("no 'test' attribute found for when");
+                        continue;
+                    }
+
+                    Boolean result = evaluateXPathToBoolean(test);
+                    if (result != null && result.booleanValue()) {
+                        branch = child;
+                        break;
+                    }
+
+                    continue;
+                }
+                else if ("otherwise".equals(name)) {
+                    branch = child;
+                    // No break here.
+                }
+            }
+
+            if (branch != null) {
+                NodeList subs = branch.getChildNodes();
+                for (int i = 0, N = subs.getLength(); i < N; ++i) {
+                    build(parent, subs.item(i));
+                }
+            }
+        }
+
+        protected XPathExpression getXPathExpression(String expr)
+        throws XPathExpressionException
+        {
+            XPathExpression x = expressions.get(expr);
+            if (x == null) {
+                XPath xpath = XPATH_FACTORY.newXPath();
+                xpath.setXPathVariableResolver(frames);
+                xpath.setXPathFunctionResolver(functionResolver);
+                x = xpath.compile(expr);
+                expressions.put(expr, x);
+            }
+            return x;
+        }
+
+        protected Object evaluateXPath(String expr, QName returnType) {
+
+            if (log.isDebugEnabled()) {
+                log.debug("evaluate: '" + expr + "'");
+            }
+
+            try {
+                XPathExpression x = getXPathExpression(expr);
+                return x.evaluate(EVAL_DOCUMENT, returnType);
+            }
+            catch (XPathExpressionException xpee) {
+                log.error("expression: " + expr, xpee);
+            }
+            return null;
+        }
+
+        protected Boolean evaluateXPathToBoolean(String expr) {
+
+            Object result = evaluateXPath(expr, XPathConstants.BOOLEAN);
+
+            return result instanceof Boolean
+                ? (Boolean)result
+                : null;
+        }
+
+        protected void convert(Element current) {
+
+            String variable = expand(current.getAttribute("var"));
+            String type     = expand(current.getAttribute("type"));
+
+            Object [] result = new Object[1];
+
+            if (frames.getStore(variable, result)) {
+                Object object = TypeConverter.convert(result[0], type);
+                frames.put(variable.toUpperCase(), object);
+            }
+        }
+
+
+        /** Put <dc:variable> content as variable on stackframes. */
+        protected void variable(Element current) {
+
+            String varName = expand(current.getAttribute("name"));
+            String expr    = current.getAttribute("expr");
+            String type    = current.getAttribute("type");
+
+            if (varName.length() == 0 || expr.length() == 0) {
+                log.error("dc:variable 'name' or 'expr' empty.");
+            }
+            else {
+                frames.put(
+                    varName.toUpperCase(),
+                    evaluateXPath(expr, typeToQName(type)));
+            }
+        }
+
+        protected String expand(String s) {
+            Matcher m = CompiledStatement.VAR.matcher(s);
+
+            Object [] result = new Object[1];
+
+            StringBuffer sb = new StringBuffer();
+            while (m.find()) {
+                String key = m.group(1);
+                result[0] = null;
+                if (frames.getStore(key, result)) {
+                    m.appendReplacement(
+                        sb, result[0] != null ? result[0].toString() : "");
+                }
+                else {
+                    m.appendReplacement(sb, "\\${" + key + "}");
+                }
+            }
+            m.appendTail(sb);
+            return sb.toString();
+        }
+
+        protected void evaluateAttributeValue(Attr attr) {
+            String value = attr.getValue();
+            if (value.indexOf('{') >= 0) {
+                StringBuffer sb = new StringBuffer();
+                Matcher m = BRACKET_XPATH.matcher(value);
+                while (m.find()) {
+                    String expr = m.group(1);
+                    Object result = evaluateXPath(expr, XPathConstants.STRING);
+                    if (result instanceof String) {
+                        m.appendReplacement(sb, (String)result);
+                    }
+                    else {
+                        m.appendReplacement(sb, "");
+                    }
+                }
+                m.appendTail(sb);
+                attr.setValue(sb.toString());
+            }
+        }
+
+        protected void build(Node parent, Node current)
+        throws SQLException
+        {
+            String ns = current.getNamespaceURI();
+            if (ns != null && ns.equals(DC_NAMESPACE_URI)) {
+                if (current.getNodeType() != Node.ELEMENT_NODE) {
+                    log.warn("need elements here");
+                }
+                else {
+                    String localName = current.getLocalName();
+                    Element curr = (Element)current;
+                    if ("attribute".equals(localName)) {
+                        attribute(parent, curr);
+                    }
+                    else if ("context".equals(localName)) {
+                        context(parent, curr);
+                    }
+                    else if ("if".equals(localName)) {
+                        ifClause(parent, curr);
+                    }
+                    else if ("choose".equals(localName)) {
+                        choose(parent, curr);
+                    }
+                    else if ("call-macro".equals(localName)) {
+                        callMacro(parent, curr);
+                    }
+                    else if ("macro-body".equals(localName)) {
+                        macroBody(parent, curr);
+                    }
+                    else if ("macro".equals(localName)
+                         ||  "comment".equals(localName)
+                         ||  "statement".equals(localName)) {
+                        // Simply ignore them.
+                    }
+                    else if ("element".equals(localName)) {
+                        element(parent, curr);
+                    }
+                    else if ("for-each".equals(localName)) {
+                        foreach(parent, curr);
+                    }
+                    else if ("filter".equals(localName)) {
+                        filter(parent, curr);
+                    }
+                    else if ("text".equals(localName)) {
+                        text(parent, curr);
+                    }
+                    else if ("variable".equals(localName)) {
+                        variable(curr);
+                    }
+                    else if ("convert".equals(localName)) {
+                        convert(curr);
+                    }
+                    else {
+                        log.warn("unknown '" + localName + "' -> ignore");
+                    }
+                }
+                return;
+            }
+
+            if (current.getNodeType() == Node.TEXT_NODE) {
+                String txt = current.getNodeValue();
+                if (txt != null && txt.trim().length() == 0) {
+                    return;
+                }
+            }
+
+            if (current.getNodeType() == Node.COMMENT_NODE) {
+                // Ignore XML comments
+                return;
+            }
+
+            Node copy = owner.importNode(current, false);
+
+            NodeList children = current.getChildNodes();
+            for (int i = 0, N = children.getLength(); i < N; ++i) {
+                build(copy, children.item(i));
+            }
+            if (copy.getNodeType() == Node.ELEMENT_NODE) {
+                NamedNodeMap nnm = ((Element)copy).getAttributes();
+                for (int i = 0, N = nnm.getLength(); i < N; ++i) {
+                    Node n = nnm.item(i);
+                    if (n.getNodeType() == Node.ATTRIBUTE_NODE) {
+                        evaluateAttributeValue((Attr)n);
+                    }
+                }
+            }
+            parent.appendChild(copy);
+        }
+    } // class BuildHelper
+
+
+    public Builder() {
+        compiledStatements = new HashMap<String, CompiledStatement>();
+        macros             = new HashMap<String, Element>();
+    }
+
+    public Builder(Document template) {
+        this();
+        this.template = template;
+        extractMacros();
+        compileStatements();
+    }
+
+    protected static QName typeToQName(String type) {
+        if ("number" .equals(type)) return XPathConstants.NUMBER;
+        if ("bool"   .equals(type)) return XPathConstants.BOOLEAN;
+        if ("node"   .equals(type)) return XPathConstants.NODE;
+        if ("nodeset".equals(type)) return XPathConstants.NODESET;
+        return XPathConstants.STRING;
+    }
+
+    /** Handle <dc:statement> elements. */
+    protected void compileStatements() {
+
+        NodeList nodes = template.getElementsByTagNameNS(
+            DC_NAMESPACE_URI, "statement");
+
+        for (int i = 0, N = nodes.getLength(); i < N; ++i) {
+            Element stmntElement = (Element)nodes.item(i);
+            String stmnt = trimStatement(stmntElement.getTextContent());
+            if (stmnt == null || stmnt.length() == 0) {
+                throw new IllegalArgumentException("found empty statement");
+            }
+            CompiledStatement cs = new CompiledStatement(stmnt);
+            // For faster lookup store a shortend string into the template.
+            stmnt = "s" + i;
+            stmntElement.setTextContent(stmnt);
+            compiledStatements.put(stmnt, cs);
+        }
+    }
+
+    protected void extractMacros() {
+        NodeList ms = template.getElementsByTagNameNS(
+            DC_NAMESPACE_URI, "macro");
+
+        for (int i = 0, N = ms.getLength(); i < N; ++i) {
+            Element m = (Element)ms.item(i);
+            macros.put(m.getAttribute("name"), m);
+        }
+    }
+
+    protected List<Node> rootsToList() {
+
+        NodeList roots = template.getElementsByTagNameNS(
+            DC_NAMESPACE_URI, "template");
+
+        List<Node> elements = new ArrayList<Node>();
+
+        for (int i = 0, N = roots.getLength(); i < N; ++i) {
+            NodeList rootChildren = roots.item(i).getChildNodes();
+            for (int j = 0, M = rootChildren.getLength(); j < M; ++j) {
+                Node child = rootChildren.item(j);
+                if (child.getNodeType() == Node.ELEMENT_NODE) {
+                    elements.add(child);
+                }
+            }
+        }
+
+        return elements;
+    }
+
+    protected static final String trimStatement(String stmnt) {
+        if (stmnt == null) return null;
+        //XXX: Maybe a bit to radical for multiline strings?
+        return STRIP_LINE_INDENT.matcher(stmnt.trim()).replaceAll(" ");
+    }
+
+    protected static Document getOwnerDocument(Node node) {
+        Document document = node.getOwnerDocument();
+        return document != null ? document : (Document)node;
+    }
+
+    public void build(
+        List<NamedConnection> connections,
+        Node                  output,
+        Map<String, Object>   parameters
+    )
+    throws SQLException
+    {
+        BuildHelper helper = new BuildHelper(output, connections, parameters);
+
+        helper.build();
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/datacage/templating/BuilderPool.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,102 @@
+package org.dive4elements.river.artifacts.datacage.templating;
+
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.List;
+import java.util.Map;
+
+import java.sql.SQLException;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+/** A little round robin pool of builders to mitigate 
+  * the fact the XML DOM documents are not thread safe.
+  */
+public class BuilderPool
+{
+    private static Logger log = Logger.getLogger(BuilderPool.class);
+
+    private static final int DEFAULT_POOL_SIZE = 4;
+
+    private static final int POOL_SIZE = Math.max(
+        Integer.getInteger("flys.datacage.pool.size", DEFAULT_POOL_SIZE), 1);
+
+    private Deque<Builder> pool;
+
+    public BuilderPool(Document document) {
+        this(document, POOL_SIZE);
+    }
+
+    public BuilderPool(Document document, int poolSize) {
+
+        if (log.isDebugEnabled()) {
+            log.debug("Create build pool with " + poolSize + " elements.");
+        }
+
+        pool = new ArrayDeque<Builder>(poolSize);
+        for (int i = 0; i < poolSize; ++i) {
+            pool.add(new Builder(cloneDocument(document)));
+        }
+    }
+
+    private final static Document cloneDocument(Document document) {
+
+        try {
+            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+            DocumentBuilder db = dbf.newDocumentBuilder();
+
+            Node origRoot = document.getDocumentElement();
+
+            Document copy = db.newDocument();
+            Node copyRoot = copy.importNode(origRoot, true);
+            copy.appendChild(copyRoot);
+
+            return copy;
+        }
+        catch (ParserConfigurationException pce) {
+            log.error(pce);
+        }
+
+        log.error("Returning original document. This will lead to threading issues.");
+
+        return document;
+    }
+
+    public void build(
+        List<Builder.NamedConnection> connections,
+        Node                          output,
+        Map<String, Object>           parameters
+    )
+    throws SQLException
+    {
+        Builder builder;
+        synchronized (pool) {
+            try {
+                while ((builder = pool.poll()) == null) {
+                    pool.wait();
+                }
+            }
+            catch (InterruptedException ie) {
+                log.debug("Waiting for builder interrupted. Build canceled.");
+                return;
+            }
+        }
+        try {
+            builder.build(connections, output, parameters);
+        }
+        finally {
+            synchronized (pool) {
+                pool.add(builder);
+                pool.notify();
+            }
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/datacage/templating/CompiledStatement.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,216 @@
+package org.dive4elements.river.artifacts.datacage.templating;
+
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.ArrayList;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.Connection;
+import java.sql.ResultSet;
+
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.Element;
+
+import org.dive4elements.river.artifacts.cache.CacheFactory;
+
+import org.apache.log4j.Logger;
+
+/** SQL Statement, create PreparedStatement. */
+public class CompiledStatement
+{
+    private static Logger log = Logger.getLogger(CompiledStatement.class);
+
+    public static final String DATACAGE_DB_CACHE =
+        "datacage.db";
+
+    public static final Pattern VAR =
+        Pattern.compile("\\$\\{([a-zA-Z0-9_-]+)\\}");
+
+    protected String original;
+    protected String statement;
+
+    protected Map<String, List<Integer>> positions;
+
+    protected int numVars;
+
+    public class Instance {
+
+        protected PreparedStatement preparedStatement;
+
+        public Instance() {
+        }
+
+        /** Executes a Statement. */
+        protected ResultData executeCached(
+            Cache       cache,
+            Connection  connection,
+            StackFrames frames
+        )
+        throws SQLException
+        {
+            log.debug("executeCached");
+            Object [] values = new Object[numVars];
+
+            StringBuilder sb = new StringBuilder(original);
+
+            for (Map.Entry<String, List<Integer>> entry: positions.entrySet()) {
+                String key   = entry.getKey();
+                Object value = frames.get(key);
+                sb.append(';').append(key).append(':').append(value);
+                for (Integer index: entry.getValue()) {
+                    values[index] = value;
+                }
+            }
+
+            // XXX: Maybe too many collisions?
+            // String key = original + Arrays.hashCode(values);
+            String key = sb.toString();
+
+            Element element = cache.get(key);
+
+            if (element != null) {
+                return (ResultData)element.getValue();
+            }
+
+            if (preparedStatement == null) {
+                preparedStatement = connection.prepareStatement(statement);
+            }
+
+            for (int i = 0; i < values.length; ++i) {
+                preparedStatement.setObject(i+1, values[i]);
+            }
+
+            ResultData data;
+
+            if (log.isDebugEnabled()) {
+                log.debug("executing: " + statement);
+            }
+
+            ResultSet result = preparedStatement.executeQuery();
+            try {
+                data = new ResultData(preparedStatement.getMetaData())
+                    .addAll(result);
+            }
+            finally {
+                result.close();
+            }
+
+            element = new Element(key, data);
+            cache.put(element);
+
+            return data;
+        }
+
+        protected ResultData executeUncached(
+            Connection  connection,
+            StackFrames frames
+        )
+        throws SQLException
+        {
+            log.debug("executeUncached");
+            if (preparedStatement == null) {
+                if (log.isDebugEnabled()) {
+                    log.debug("preparing statement: " + statement);
+                }
+                preparedStatement = connection.prepareStatement(statement);
+            }
+
+            for (Map.Entry<String, List<Integer>> entry: positions.entrySet()) {
+                Object value = frames.get(entry.getKey());
+                for (Integer index: entry.getValue()) {
+                    preparedStatement.setObject(index+1, value);
+                }
+            }
+
+            if (log.isDebugEnabled()) {
+                log.debug("executing: " + statement);
+            }
+
+            ResultSet result = preparedStatement.executeQuery();
+            try {
+                return new ResultData(preparedStatement.getMetaData())
+                    .addAll(result);
+            }
+            finally {
+                result.close();
+            }
+        }
+
+        public ResultData execute(
+            Connection  connection,
+            StackFrames frames,
+            boolean     cached
+        )
+        throws SQLException
+        {
+            if (!cached) {
+                return executeUncached(connection, frames);
+            }
+
+            Cache cache = CacheFactory.getCache(DATACAGE_DB_CACHE);
+
+            return cache != null
+                ? executeCached(cache, connection, frames)
+                : executeUncached(connection, frames);
+        }
+
+        public void close() {
+            if (preparedStatement != null) {
+                try {
+                    preparedStatement.close();
+                }
+                catch (SQLException sqle) {
+                }
+                preparedStatement = null;
+            }
+        }
+    } // class Instance
+
+    public CompiledStatement() {
+    }
+
+    public CompiledStatement(String original) {
+        this.original = original;
+        // TreeMap to ensure order
+        positions = new TreeMap<String, List<Integer>>();
+        compile();
+    }
+
+    protected void compile() {
+
+        StringBuffer sb = new StringBuffer();
+
+        Matcher m = VAR.matcher(original);
+
+        int index = 0;
+
+        // Find variables like ${varname}.
+        while (m.find()) {
+            String key = m.group(1).toUpperCase();
+            List<Integer> indices = positions.get(key);
+            if (indices == null) {
+                indices = new ArrayList<Integer>();
+                positions.put(key, indices);
+            }
+            indices.add(index);
+            m.appendReplacement(sb, "?");
+            ++index;
+        }
+
+        m.appendTail(sb);
+
+        numVars = index;
+
+        statement = sb.toString();
+    }
+
+    public String getStatement() {
+        return statement;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/datacage/templating/FunctionResolver.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,325 @@
+package org.dive4elements.river.artifacts.datacage.templating;
+
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import javax.xml.namespace.QName;
+
+import javax.xml.xpath.XPathFunction;
+import javax.xml.xpath.XPathFunctionException;
+import javax.xml.xpath.XPathFunctionResolver;
+
+import org.apache.log4j.Logger;
+
+
+/** Resolves functions (e.g. dc:contains) in Datacage/Meta-Data system. */
+public class FunctionResolver
+implements   XPathFunctionResolver
+{
+    /** Home logger. */
+    private static Logger log = Logger.getLogger(FunctionResolver.class);
+
+    public static final String FUNCTION_NAMESPACE_URI = "dc";
+
+    public static final double FAR_AWAY = 99999d;
+
+    public static final class Entry {
+
+        String        name;
+        XPathFunction function;
+        int           arity;
+
+        public Entry() {
+        }
+
+        public Entry(String name, XPathFunction function, int arity) {
+            this.name     = name;
+            this.function = function;
+            this.arity    = arity;
+        }
+    } // class Entry
+
+    /** List of functions. */
+    protected List<Entry> functions;
+
+    protected Builder.BuildHelper buildHelper;
+
+
+    public FunctionResolver() {
+        this(null);
+    }
+
+    public FunctionResolver(Builder.BuildHelper buildHelper) {
+        this.buildHelper = buildHelper;
+
+        functions = new ArrayList<Entry>();
+
+        addFunction("contains", 2, new XPathFunction() {
+            @Override
+            public Object evaluate(List args) throws XPathFunctionException {
+                return contains(args);
+            }
+        });
+
+        addFunction("fromValue", 3, new XPathFunction() {
+            @Override
+            public Object evaluate(List args) throws XPathFunctionException {
+                return fromValue(args);
+            }
+        });
+
+        addFunction("toValue", 3, new XPathFunction() {
+            @Override
+            public Object evaluate(List args) throws XPathFunctionException {
+                return toValue(args);
+            }
+        });
+
+        addFunction("replace", 3, new XPathFunction() {
+            @Override
+            public Object evaluate(List args) throws XPathFunctionException {
+                return replace(args);
+            }
+        });
+
+        addFunction("has-result", 0, new XPathFunction() {
+            @Override
+            public Object evaluate(List args) throws XPathFunctionException {
+                return FunctionResolver.this.buildHelper.hasResult();
+            }
+        });
+
+        addFunction("date-format", 2, new XPathFunction() {
+            @Override
+            public Object evaluate(List args) throws XPathFunctionException {
+                return dateFormat(args);
+            }
+        });
+    }
+
+    /**
+     * Create a new function.
+     * @param name  Name of the function.
+     * @param arity Number of arguments for function.
+     * @param function the function itself.
+     */
+    public void addFunction(String name, int arity, XPathFunction function) {
+        functions.add(new Entry(name, function, arity));
+    }
+
+    @Override
+    public XPathFunction resolveFunction(QName functionName, int arity) {
+
+        if (!functionName.getNamespaceURI().equals(FUNCTION_NAMESPACE_URI)) {
+            return null;
+        }
+
+        String name = functionName.getLocalPart();
+        for (Entry entry: functions) {
+            if (entry.arity == arity && entry.name.equals(name)) {
+                return entry.function;
+            }
+        }
+
+        return null;
+    }
+
+    /** Implementation of case-ignoring dc:contains. */
+    public Object contains(List args) throws XPathFunctionException {
+        Object haystack = args.get(0);
+        Object needle   = args.get(1);
+
+        if (needle instanceof String && !(haystack instanceof String)) {
+            needle = ((String)needle).toUpperCase();
+        }
+
+        try {
+            if (haystack instanceof Collection) {
+                return Boolean.valueOf(
+                    ((Collection)haystack).contains(needle));
+            }
+
+            if (haystack instanceof Map) {
+                return Boolean.valueOf(
+                    ((Map)haystack).containsKey(needle));
+            }
+
+            if (haystack instanceof Object []) {
+                for (Object straw: (Object [])haystack) {
+                    if (straw.equals(needle)) {
+                        return Boolean.TRUE;
+                    }
+                }
+            }
+
+            if (haystack instanceof String && needle instanceof String) {
+                String h = (String)haystack;
+                String n = (String)needle;
+                return h.contains(n);
+            }
+
+            return Boolean.FALSE;
+        }
+        catch (Exception e) {
+            log.error(e);
+            throw new XPathFunctionException(e);
+        }
+    }
+
+    /** Implementation for getting the minimum value of location or distance
+     *  dc:fromValue.
+     */
+    public Object fromValue(List args) throws XPathFunctionException {
+        Object mode      = args.get(0);
+        Object locations = args.get(1);
+        Object from      = args.get(2);
+
+        if (!(mode instanceof String)){
+            return -FAR_AWAY;
+        }
+
+        if (mode.equals("locations")) {
+            if (!(locations instanceof String)) {
+                return -FAR_AWAY;
+            }
+            String loc = ((String)locations).replace(" ", "");
+            String[] split = loc.split(",");
+            if (split.length < 1) {
+                return -FAR_AWAY;
+            }
+            try {
+                double min = Double.parseDouble(split[0]);
+                for (int i = 1; i < split.length; ++i) {
+                    double v = Double.parseDouble(split[i]);
+                    if (v < min) {
+                        min = v;
+                    }
+                }
+                return min;
+            }
+            catch (NumberFormatException nfe) {
+                return -FAR_AWAY;
+            }
+        }
+        else if (mode.equals("distance")) {
+            if (!(from instanceof String)) {
+                return -FAR_AWAY;
+            }
+            String f = (String)from;
+            try {
+                return Double.parseDouble(f);
+            }
+            catch(NumberFormatException nfe) {
+                return -FAR_AWAY;
+            }
+        }
+        else {
+            return -FAR_AWAY;
+        }
+    }
+
+    /** Implementation for getting the maximum value of location or distance
+     *  dc:toValue.
+     */
+    public Object toValue(List args) throws XPathFunctionException {
+        Object mode      = args.get(0);
+        Object locations = args.get(1);
+        Object to        = args.get(2);
+
+        if (!(mode instanceof String)){
+            return FAR_AWAY;
+        }
+
+        if (mode.equals("locations")) {
+            if (!(locations instanceof String)) {
+                return FAR_AWAY;
+            }
+            try {
+                String loc = ((String)locations).replace(" ", "");
+                String[] split = loc.split(",");
+                if (split.length < 1) {
+                    return FAR_AWAY;
+                }
+                double max = Double.parseDouble(split[0]);
+                for (int i = 1; i < split.length; ++i) {
+                    double v = Double.parseDouble(split[i]);
+                    if (v > max) {
+                        max = v;
+                    }
+                }
+                return max;
+            }
+            catch (NumberFormatException nfe) {
+                return FAR_AWAY;
+            }
+        }
+        else if (mode.equals("distance")) {
+            if (!(to instanceof String)) {
+                return FAR_AWAY;
+            }
+            else {
+                String t = (String)to;
+                try {
+                    return Double.parseDouble(t);
+                }
+                catch(NumberFormatException nfe) {
+                    return FAR_AWAY;
+                }
+            }
+        }
+        else {
+            return FAR_AWAY;
+        }
+    }
+
+    /** Implementation for doing a string replace
+     *  dc:replace
+     */
+    public Object replace(List args) throws XPathFunctionException {
+        Object haystack    = args.get(0);
+        Object needle      = args.get(1);
+        Object replacement = args.get(2);
+
+        if (needle instanceof String &&
+            haystack instanceof String &&
+            replacement instanceof String) {
+            return ((String)haystack).replace(
+                    (String)needle, (String)replacement);
+        } else {
+            return haystack;
+        }
+    }
+
+    public Object dateFormat(List args) throws XPathFunctionException {
+        Object pattern = args.get(0);
+        Object date    = args.get(1);
+
+        try {
+            // Oracle does not return a date object but an oracle.sql.TIMESTAMP
+            Method meth = date.getClass().getMethod("dateValue", new Class[] {});
+            date = meth.invoke(date, new Object [] {});
+        } catch (IllegalArgumentException e) {
+        } catch (IllegalAccessException e) {
+        } catch (InvocationTargetException e) {
+        } catch (NoSuchMethodException e) {
+        }
+        if (pattern instanceof String && date instanceof Date) {
+            try {
+                // TODO: Take locale into account.
+                return new SimpleDateFormat((String)pattern).format((Date)date);
+            }
+            catch (IllegalArgumentException iae) {
+                throw new XPathFunctionException(iae);
+            }
+        }
+        return "";
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/datacage/templating/ResultData.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,84 @@
+package org.dive4elements.river.artifacts.datacage.templating;
+
+import java.io.Serializable;
+
+import java.sql.ResultSetMetaData;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import java.util.List;
+import java.util.ArrayList;
+
+import org.apache.log4j.Logger;
+
+
+/** Result Data from a DB/SQL query. */
+public class ResultData
+implements   Serializable
+{
+    private static Logger log = Logger.getLogger(ResultData.class);
+
+    protected String [] columns;
+
+    protected List<Object []> rows;
+
+    public ResultData() {
+        rows = new ArrayList<Object []>();
+    }
+
+    public ResultData(String [] columns, List<Object []> rows) {
+        this.columns = columns;
+        this.rows = rows;
+    }
+
+    public ResultData(ResultSetMetaData meta)
+    throws SQLException
+    {
+        this();
+
+        boolean debug = log.isDebugEnabled();
+
+        int N = meta.getColumnCount();
+
+        columns = new String[N];
+
+        if (debug) {
+            log.debug("ResultSet column names:");
+        }
+
+        for (int i = 1; i <= N; ++i) {
+            columns[i-1] = meta.getColumnLabel(i).toUpperCase();
+            if (debug) {
+                log.debug("    " + i + ": " + columns[i-1]);
+            }
+        }
+    }
+
+    public String [] getColumnLabels() {
+        return columns;
+    }
+
+    public ResultData addAll(ResultSet result) throws SQLException {
+        while (result.next()) {
+            add(result);
+        }
+        return this;
+    }
+
+    public void add(ResultSet result) throws SQLException {
+        Object [] row = new Object[columns.length];
+        for (int i = 0; i < columns.length; ++i) {
+            row[i] = result.getObject(i+1);
+        }
+        rows.add(row);
+    }
+
+    public List<Object []> getRows() {
+        return rows;
+    }
+
+    public boolean isEmpty() {
+        return rows.isEmpty();
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/datacage/templating/StackFrames.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,168 @@
+package org.dive4elements.river.artifacts.datacage.templating;
+
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.List;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.xml.xpath.XPathVariableResolver;
+
+import javax.xml.namespace.QName;
+
+import org.apache.log4j.Logger;
+
+
+/**
+ * Maintains stack of 'frames' which are maps from string to object.
+ * Used for variables in datacage/meta-data system.
+ */
+public class StackFrames
+implements   XPathVariableResolver
+{
+    private static Logger log = Logger.getLogger(StackFrames.class);
+
+    public static final Object NULL = new Object();
+
+    /** The frames (used like a stack). */
+    protected List<Map<String, Object>> frames;
+
+    public StackFrames() {
+        frames = new ArrayList<Map<String, Object>>();
+    }
+
+    public StackFrames(Map<String, Object> initialFrame) {
+        this();
+        if (initialFrame != null) {
+            frames.add(new HashMap<String, Object>(initialFrame));
+        }
+    }
+
+    /** Push a new String->Object map. */
+    public void enter() {
+        frames.add(new HashMap<String, Object>());
+    }
+
+    /** Pop/Remove last String->Object map. */
+    public void leave() {
+        frames.remove(frames.size()-1);
+    }
+
+    /** Put Key/Value in last String->Object map. */
+    public void put(String key, Object value) {
+        int N = frames.size();
+        if (N > 0) {
+            frames.get(N-1).put(key, value);
+        }
+    }
+
+    /** Put multiple Key/Values in last String->Object map. */
+    public void put(String [] keys, Object [] values) {
+        Map<String, Object> top = frames.get(frames.size()-1);
+        for (int i = 0; i < keys.length; ++i) {
+            top.put(keys[i], values[i]);
+        }
+    }
+
+    /** Check last frame (string->object map) for key. */
+    public boolean containsKey(String key) {
+        key = key.toUpperCase();
+        for (int i = frames.size()-1; i >= 0; --i) {
+            if (frames.get(i).containsKey(key)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Get element (variable) key.
+     * Returns null if not found.
+     * @param key name to resolve
+     * @return resolution, null if not found.
+     */
+    public Object get(String key) {
+        return get(key, null);
+    }
+
+    /** result[0] is modified with value when true returned.
+     * @return false if key not found in any frame. */
+    public boolean getStore(String key, Object [] result) {
+
+        key = key.toUpperCase();
+
+        for (int i = frames.size()-1; i >= 0; --i) {
+            Map<String, Object> frame = frames.get(i);
+            if (frame.containsKey(key)) {
+                result[0] = frame.get(key);
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    public Object get(String key, Object def) {
+
+        key = key.toUpperCase();
+
+        for (int i = frames.size()-1; i >= 0; --i) {
+            Map<String, Object> frame = frames.get(i);
+            if (frame.containsKey(key)) {
+                return frame.get(key);
+            }
+        }
+
+        return def;
+    }
+
+    public Object getNull(String key) {
+        return getNull(key, null);
+    }
+
+    public Object getNull(String key, Object def) {
+
+        key = key.toUpperCase();
+
+        for (int i = frames.size()-1; i >= 0; --i) {
+            Map<String, Object> frame = frames.get(i);
+            if (frame.containsKey(key)) {
+                Object value = frame.get(key);
+                return value != null ? value : NULL;
+            }
+        }
+
+        return def;
+    }
+
+    @Override
+    public Object resolveVariable(QName variableName) {
+        if (log.isDebugEnabled()) {
+            log.debug("resolve var: " + variableName);
+        }
+
+        return getNull(variableName.getLocalPart());
+    }
+
+    public String dump() {
+        StringBuilder sb = new StringBuilder("[");
+        Set<String> already = new HashSet<String>();
+
+        boolean first = true;
+
+        for (int i = frames.size()-1; i >= 0; --i) {
+            Map<String, Object> frame = frames.get(i);
+            for (Map.Entry<String, Object> entry: frame.entrySet()) {
+                if (already.add(entry.getKey())) {
+                    if (first) { first = false;   }
+                    else       { sb.append(", "); }
+                    sb.append('\'').append(entry.getKey())
+                      .append("'='").append(entry.getValue()).append('\'');
+                }
+            }
+        }
+        return sb.append(']').toString();
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/datacage/templating/TypeConverter.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,31 @@
+package org.dive4elements.river.artifacts.datacage.templating;
+
+public class TypeConverter
+{
+    private TypeConverter() {
+    }
+
+    public static Object convert(Object object, String type) {
+
+        if (type == null) {
+            return object;
+        }
+
+        if ("Integer".equals(type)) {
+            return Integer.valueOf(object.toString());
+        }
+
+        if ("Double".equals(type)) {
+            return Double.valueOf(object.toString());
+        }
+
+        if ("String".equals(type)) {
+            return object.toString();
+        }
+
+        // TODO: Add more types
+
+        return object;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/geom/Lines.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,403 @@
+package org.dive4elements.river.artifacts.geom;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Iterator;
+
+import java.awt.geom.Point2D;
+import java.awt.geom.Line2D;
+
+import org.dive4elements.river.artifacts.math.Linear;
+
+import org.apache.log4j.Logger;
+
+import gnu.trove.TDoubleArrayList;
+
+/**
+ * Utility to create lines (intersect water with cross-section etc).
+ */
+public class Lines
+{
+    private static Logger log = Logger.getLogger(Lines.class);
+
+    public static final double EPSILON = 1e-4;
+
+    public static enum Mode { UNDEF, WET, DRY };
+
+
+    /** Never instantiate Lines, use static functions instead. */
+    protected Lines() {
+    }
+
+
+    /**
+     * Calculate area of polygon with four vertices.
+     * @return area of polygon with four vertices.
+     */
+    public static double area(Point2D p1, Point2D p2, Point2D p3, Point2D p4) {
+        double[] x = new double[] {p1.getX(), p2.getX(), p3.getX(), p4.getX(), p1.getX() };
+        double[] y = new double[] {p1.getY(), p2.getY(), p3.getY(), p4.getY(), p1.getY() };
+        double area = 0d;
+        for (int i=0; i <4; i++) {
+            area += (x[i] * y[i+1]) - (x[i+1] * y[i]);
+        }
+        return Math.abs(area * 0.5d);
+    }
+
+
+    /**
+     * Calculate the 'length' of the given lines.
+     * @param lines lines of which to calculate length.
+     */
+    public static double length(List<Line2D> lines) {
+        double sum = 0d;
+        for (Line2D line: lines) {
+            double xDiff = line.getX1() - line.getX2();
+            double yDiff = line.getY1() - line.getY2();
+            sum += Math.sqrt(xDiff*xDiff + yDiff*yDiff);
+        }
+        return sum;
+    }
+
+
+    /** List of lines and a double-precision area. */
+    private static class ListWithArea {
+        public List<Line2D> lines;
+        public double area;
+        public ListWithArea(List<Line2D> lines, double area) {
+            this.lines = lines;
+            this.area = area;
+        }
+    }
+
+
+    /**
+     * For a cross section given as points and a waterlevel (in meters),
+     * create a set of lines that represent the water surface, assuming it
+     * is distributed horizontally equally.
+     * @param points the points describing the river bed.
+     * @param waterLevel the height of the horizontal water line.
+     * @return A list of Lines representing the water surface and the
+     *         calculated area between water surface and river bed.
+     */
+    public static ListWithArea fillWater(List<Point2D> points, double waterLevel) {
+
+        boolean debug = log.isDebugEnabled();
+
+        if (debug) {
+            log.debug("fillWater");
+            log.debug("----------------------------");
+        }
+
+        List<Line2D> result = new ArrayList();
+
+        int N = points.size();
+
+        if (N == 0) {
+            return new ListWithArea(result, 0d);
+        }
+
+        if (N == 1) {
+            Point2D p = points.get(0);
+            // Only generate point if over profile
+            if (waterLevel > p.getY()) {
+                result.add(new Line2D.Double(
+                    p.getX(), waterLevel,
+                    p.getX(), waterLevel));
+            }
+            // TODO continue calculating area.
+            return new ListWithArea(result, 0d);
+        }
+
+        double minX =  Double.MAX_VALUE;
+        double minY =  Double.MAX_VALUE;
+        double maxX = -Double.MAX_VALUE;
+        double maxY = -Double.MAX_VALUE;
+
+        // To ensure for sequences of equals x's that
+        // the original index order is preserved.
+        for (Point2D p: points) {
+            double x = p.getX(), y = p.getY();
+            if (x < minX) minX = x;
+            if (x > maxX) maxX = x;
+            if (y < minY) minY = y;
+            if (y > maxY) maxY = y;
+        }
+
+        if (minY > waterLevel) { // profile completely over water level
+            log.debug("complete over water");
+            return new ListWithArea(result, 0d);
+        }
+
+        if (waterLevel > maxY) { // water floods profile
+            log.debug("complete under water");
+            result.add(new Line2D.Double(minX, waterLevel, maxX, waterLevel));
+            return new ListWithArea(result, 0d);
+        }
+
+        // Water is sometimes above, sometimes under profile.
+        Mode mode = Mode.UNDEF;
+
+        double startX = minX;
+
+        double area = 0d;
+        // Walking along the profile.
+        for (int i = 1; i < N; ++i) {
+            Point2D p1 = points.get(i-1);
+            Point2D p2 = points.get(i);
+
+            if (p1.getY() < waterLevel && p2.getY() < waterLevel) {
+                // completely under water
+                if (debug) {
+                    log.debug("under water: " + p1 + " " + p2);
+                }
+                if (mode != Mode.WET) {
+                    startX = p1.getX();
+                    mode = Mode.WET;
+                }
+                area += area(p1, p2,
+                    new Point2D.Double(p2.getX(), waterLevel),
+                    new Point2D.Double(p1.getX(), waterLevel));
+                continue;
+            }
+
+            // TODO trigger area calculation
+            if (p1.getY() > waterLevel && p2.getY() > waterLevel) {
+                if (debug) {
+                    log.debug("over water: " + p1 + " " + p2);
+                }
+                // completely over water
+                if (mode == Mode.WET) {
+                    log.debug("over/wet");
+                    result.add(new Line2D.Double(
+                        startX, waterLevel,
+                        p1.getX(), waterLevel));
+                }
+                mode = Mode.DRY;
+                continue;
+            }
+
+            // TODO trigger area calculation
+            if (Math.abs(p1.getX() - p2.getX()) < EPSILON) {
+                // vertical line
+                switch (mode) {
+                    case WET:
+                        log.debug("vertical/wet");
+                        mode = Mode.DRY;
+                        result.add(new Line2D.Double(
+                            startX, waterLevel,
+                            p1.getX(), waterLevel));
+                        break;
+                    case DRY:
+                        log.debug("vertical/dry");
+                        mode = Mode.WET;
+                        startX = p2.getX();
+                        break;
+                    default: // UNDEF
+                        log.debug("vertical/undef");
+                        if (p2.getY() < waterLevel) {
+                            mode = Mode.WET;
+                            startX = p2.getX();
+                        }
+                        else {
+                            mode = Mode.DRY;
+                        }
+                }
+                continue;
+            }
+
+            // check if waterlevel directly hits the vertices;
+
+            boolean p1W = Math.abs(waterLevel - p1.getY()) < EPSILON;
+            boolean p2W = Math.abs(waterLevel - p2.getY()) < EPSILON;
+
+            // TODO trigger area calculation
+            if (p1W || p2W) {
+                if (debug) {
+                    log.debug("water hits vertex: " + p1 + " " + p2 + " " + mode);
+                }
+                if (p1W && p2W) { // parallel to water -> dry
+                    log.debug("water hits both vertices");
+                    if (mode == Mode.WET) {
+                        result.add(new Line2D.Double(
+                            startX, waterLevel,
+                            p1.getX(), waterLevel));
+                    }
+                    mode = Mode.DRY;
+                }
+                else if (p1W) { // p1 == waterlevel
+                    log.debug("water hits first vertex");
+                    if (p2.getY() > waterLevel) { // --> dry
+                        if (mode == Mode.WET) {
+                            result.add(new Line2D.Double(
+                                startX, waterLevel,
+                                p1.getX(), waterLevel));
+                        }
+                        mode = Mode.DRY;
+                    }
+                    else { // --> wet
+                        if (mode != Mode.WET) {
+                            startX = p1.getX();
+                            mode = Mode.WET;
+                        }
+                        area += area(p1, p2,
+                            new Point2D.Double(p2.getX(), waterLevel),
+                            new Point2D.Double(p2.getX(), waterLevel));
+                    }
+                }
+                else { // p2 == waterlevel
+                    log.debug("water hits second vertex");
+                    if (p1.getY() > waterLevel) { // --> wet
+                        if (mode != Mode.WET) {
+                            startX = p2.getX();
+                            mode = Mode.WET;
+                        }
+                    }
+                    else { // --> dry
+                        if (mode == Mode.WET) {
+                            result.add(new Line2D.Double(
+                                startX, waterLevel,
+                                p2.getX(), waterLevel));
+                        }
+                        mode = Mode.DRY;
+                        area += area(p1, p2,
+                            new Point2D.Double(p1.getX(), waterLevel),
+                            new Point2D.Double(p1.getX(), waterLevel));
+                    }
+                }
+                if (debug) {
+                    log.debug("mode is now: " + mode);
+                }
+                continue;
+            }
+
+            // TODO trigger area calculation
+            // intersection case
+            double x = Linear.linear(
+                waterLevel,
+                p1.getY(), p2.getY(),
+                p1.getX(), p2.getX());
+
+            if (debug) {
+                log.debug("intersection p1:" + p1);
+                log.debug("intersection p2:" + p2);
+                log.debug("intersection at x: " + x);
+            }
+
+            // Add area of that part of intersection that is 'wet'.
+            if (p1.getY() > waterLevel) {
+                area += area(new Point2D.Double(x, waterLevel),
+                             p2,
+                             new Point2D.Double(p2.getX(), waterLevel),
+                             new Point2D.Double(x, waterLevel));
+            }
+            else {
+                area += area(new Point2D.Double(x, waterLevel),
+                             p1,
+                             new Point2D.Double(p1.getX(), waterLevel),
+                             new Point2D.Double(x, waterLevel));
+            }
+
+            switch (mode) {
+                case WET:
+                    log.debug("intersect/wet");
+                    mode = Mode.DRY;
+                    result.add(new Line2D.Double(
+                        startX, waterLevel,
+                        x, waterLevel));
+                    break;
+
+                case DRY:
+                    log.debug("intersect/dry");
+                    mode   = Mode.WET;
+                    startX = x;
+                    break;
+
+                default: // UNDEF
+                    log.debug("intersect/undef");
+                    if (p2.getY() > waterLevel) {
+                        log.debug("intersect/undef/over");
+                        mode = Mode.DRY;
+                        result.add(new Line2D.Double(
+                            p1.getX(), waterLevel,
+                            x, waterLevel));
+                    }
+                    else {
+                        mode = Mode.WET;
+                        startX = x;
+                    }
+            } // switch mode
+        } // for all points p[i] and p[i-1]
+
+        if (mode == Mode.WET) {
+            result.add(new Line2D.Double(
+                startX, waterLevel,
+                maxX, waterLevel));
+        }
+
+        return new ListWithArea(result, area);
+    }
+
+
+    /**
+     * Class holding points that form lines and the calculated length.
+     */
+    public static class LineData {
+        public double [][] points;
+        public double width;
+        public double area;
+        public LineData(double[][] points, double width, double area) {
+            this.points = points;
+            this.width = width;
+            this.area = area;
+        }
+    }
+
+
+    /** Return length of a single line. */
+    public static double lineLength(Line2D line) {
+        double xDiff = line.getX1() - line.getX2();
+        double yDiff = line.getY1() - line.getY2();
+        return Math.sqrt(xDiff*xDiff + yDiff*yDiff);
+    }
+
+
+    /**
+     * @param points the riverbed.
+     */
+    public static LineData createWaterLines(
+        List<Point2D> points,
+        double        waterlevel
+    ) {
+        ListWithArea listAndArea = fillWater(points, waterlevel);
+        List<Line2D> lines = listAndArea.lines;
+
+        TDoubleArrayList lxs = new TDoubleArrayList();
+        TDoubleArrayList lys = new TDoubleArrayList();
+        double linesLength = 0.0f;
+
+        for (Iterator<Line2D> iter = lines.iterator(); iter.hasNext();) {
+            Line2D  line = iter.next();
+            Point2D p1   = line.getP1();
+            Point2D p2   = line.getP2();
+            lxs.add(p1.getX());
+            lys.add(p1.getY());
+            lxs.add(p2.getX());
+            lys.add(p2.getY());
+
+            // Length calculation.
+            linesLength += lineLength(line);
+
+            if (iter.hasNext()) {
+                lxs.add(Double.NaN);
+                lys.add(Double.NaN);
+            }
+        }
+
+        return new LineData(
+            new double [][] { lxs.toNativeArray(), lys.toNativeArray() },
+            linesLength, listAndArea.area
+            );
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/map/PrintMap.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,131 @@
+package org.dive4elements.river.artifacts.map;
+
+import java.awt.Color;
+import java.awt.Rectangle;
+
+import java.io.File;
+import java.io.IOException;
+
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+
+import java.util.List;
+
+import java.net.URL;
+import java.net.MalformedURLException;
+
+import javax.imageio.ImageIO;
+
+
+import org.geotools.data.ows.Layer;
+import org.geotools.data.ows.WMSCapabilities;
+import org.geotools.data.wms.WebMapServer;
+import org.geotools.geometry.jts.ReferencedEnvelope;
+import org.geotools.map.MapContext;
+import org.geotools.map.WMSMapLayer;
+import org.geotools.ows.ServiceException;
+import org.geotools.renderer.lite.StreamingRenderer;
+import org.geotools.renderer.GTRenderer;
+
+
+public class PrintMap {
+
+    public static final String DEFAULT_WMS = "http://map1.naturschutz.rlp.de/service_lanis/mod_wms/wms_getmap.php?mapfile=group_gdide&REQUEST=GetCapabilities&SERVICE=WMS";
+    public static final String DEFAULT_OUTFILE = "~/map.jpeg";
+
+    public static final String MAPSERVER = System.getProperty("wms", DEFAULT_WMS);
+    public static final String MAP_IMAGE = System.getProperty("outfile", DEFAULT_OUTFILE);
+
+
+    public static void main(String[] args) {
+        System.out.println("-> start PrintMap");
+        System.out.println("  -> Print layers of WMS: " + MAPSERVER);
+
+        try {
+            WebMapServer    server = getMapserver();
+            WMSMapLayer[] wmsLayer = getWMSLayers(server);
+
+            MapContext mapcontent = new MapContext( wmsLayer );
+            mapcontent.setTitle(" NEW MAP CONTENT TITLE ");
+
+            printMap(mapcontent);
+        }
+        catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        System.out.println("-> finished PrintMap");
+    }
+
+
+    public static void printMap(MapContext map) throws Exception {
+        int imageWidth = 600;
+
+        GTRenderer renderer = new StreamingRenderer();
+        renderer.setContext(map);
+
+        Rectangle imageBounds = null;
+        ReferencedEnvelope mapBounds = null;
+
+        try {
+            mapBounds = map.getLayerBounds();
+            double heightToWidth = mapBounds.getSpan(1) / mapBounds.getSpan(0);
+            imageBounds = new Rectangle(
+                0, 0,
+                imageWidth,
+                (int) Math.round(imageWidth * heightToWidth));
+
+        }
+        catch (Exception e) {
+            // failed to access map layers
+            throw new RuntimeException(e);
+        }
+
+        BufferedImage image = new BufferedImage(imageBounds.width, imageBounds.height, BufferedImage.TYPE_INT_RGB);
+        Graphics2D gr = image.createGraphics();
+        gr.setPaint(Color.WHITE);
+        gr.fill(imageBounds);
+
+        try {
+            renderer.paint(gr, imageBounds, mapBounds);
+            File fileToSave = new File(MAP_IMAGE);
+            ImageIO.write(image, "jpeg", fileToSave);
+        }
+        catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+
+    public static WebMapServer getMapserver() throws MalformedURLException, IOException, ServiceException {
+        return new WebMapServer(getServerUrl());
+    }
+
+
+    public static URL getServerUrl() throws MalformedURLException {
+        return new URL(MAPSERVER);
+    }
+
+
+    public static WMSMapLayer[] getWMSLayers(WebMapServer server) {
+        if (server == null) {
+            System.out.println("WebMapServer == null");
+            throw new RuntimeException("WebMapServer == null");
+        }
+
+        WMSCapabilities capabilities = server.getCapabilities();
+
+        List<Layer> layers = capabilities.getLayerList();
+        WMSMapLayer[] wmslayers = new WMSMapLayer[layers.size()];
+
+        for (int i = 0, L = layers.size(); i < L; i++) {
+            Layer l = layers.get(i);
+
+            System.out.println("   -> add layer: " + l);
+            wmslayers[i] = new WMSMapLayer(server, l);
+        }
+
+        return wmslayers;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/map/RiverMapfileGeneratorStarter.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,41 @@
+package org.dive4elements.river.artifacts.map;
+
+import org.dive4elements.artifactdatabase.LifetimeListener;
+import org.dive4elements.artifacts.GlobalContext;
+import org.dive4elements.river.utils.RiverMapfileGenerator;
+
+import org.apache.log4j.Logger;
+import org.w3c.dom.Document;
+
+/**
+ * LifetimeListener that creates a Mapfile containing river axis layers.
+ * The listener is called when the server has completed its startup.
+ *
+ * @author <a href="mailto:christian.lins@intevation.de">Christian Lins</a>
+ */
+public class RiverMapfileGeneratorStarter implements LifetimeListener {
+
+    private static Logger logger = Logger.getLogger(RiverMapfileGeneratorStarter.class);
+
+    @Override
+    public void setup(Document document) {
+        // Nothing to setup here
+    }
+
+    /**
+     * Calls RiverMapfileGenerator.generate().
+     */
+    @Override
+    public void systemUp(GlobalContext globalContext) {
+        logger.debug("systemUp()");
+
+        RiverMapfileGenerator fmfg = new RiverMapfileGenerator();
+        fmfg.generate();
+    }
+
+    @Override
+    public void systemDown(GlobalContext globalContext) {
+        // No, we're not cleaning up our generated mapfile
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/AddScaleFunction.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,19 @@
+package org.dive4elements.river.artifacts.math;
+
+public class AddScaleFunction
+implements   Function
+{
+    protected double b;
+    protected double m;
+
+    public AddScaleFunction(double b, double m) {
+        this.b = b;
+        this.m = m;
+    }
+
+    @Override
+    public double value(double x) {
+        return (x + b)*m;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/BackJumpCorrector.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,217 @@
+package org.dive4elements.river.artifacts.math;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.analysis.interpolation.SplineInterpolator;
+
+import org.apache.commons.math.analysis.polynomials.PolynomialSplineFunction;
+
+import org.apache.commons.math.ArgumentOutsideDomainException;
+
+import org.apache.commons.math.exception.MathIllegalArgumentException;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.river.artifacts.model.Calculation;
+
+import org.dive4elements.river.utils.DoubleUtil;
+
+public class BackJumpCorrector
+implements   Serializable
+{
+    private static Logger log = Logger.getLogger(BackJumpCorrector.class);
+
+    protected ArrayList<Double> backjumps;
+
+    protected double [] corrected;
+
+    public BackJumpCorrector() {
+        backjumps = new ArrayList<Double>();
+    }
+
+    public boolean hasBackJumps() {
+        return !backjumps.isEmpty();
+    }
+
+    public List<Double> getBackJumps() {
+        return backjumps;
+    }
+
+    public double [] getCorrected() {
+        return corrected;
+    }
+
+    public boolean doCorrection(
+        double []   km,
+        double []   ws,
+        Calculation errors
+    ) {
+        boolean wsUp = DoubleUtil.isIncreasing(ws);
+
+        if (wsUp) {
+            km = DoubleUtil.swapClone(km);
+            ws = DoubleUtil.swapClone(ws);
+        }
+
+        boolean kmUp = DoubleUtil.isIncreasing(km);
+
+        if (!kmUp) {
+            km = DoubleUtil.sumDiffs(km);
+        }
+
+        if (log.isDebugEnabled()) {
+            log.debug("BackJumpCorrector.doCorrection ------- enter");
+            log.debug("  km increasing: " + DoubleUtil.isIncreasing(km));
+            log.debug("  ws increasing: " + DoubleUtil.isIncreasing(ws));
+            log.debug("BackJumpCorrector.doCorrection ------- leave");
+        }
+
+        boolean hasBackJumps = doCorrectionClean(km, ws, errors);
+
+        if (hasBackJumps && wsUp) {
+            // mirror back
+            DoubleUtil.swap(corrected);
+        }
+
+        return hasBackJumps;
+    }
+
+    protected boolean doCorrectionClean(
+        double []   km,
+        double []   ws,
+        Calculation errors
+    ) {
+        int N = km.length;
+
+        if (N != ws.length) {
+            throw new IllegalArgumentException("km.length != ws.length");
+        }
+
+        if (N < 2) {
+            return false;
+        }
+
+        SplineInterpolator interpolator = null;
+
+        for (int i = 1; i < N; ++i) {
+            if (ws[i] <= ws[i-1]) {
+                // no back jump
+                continue;
+            }
+            backjumps.add(km[i]);
+
+            if (corrected == null) {
+                // lazy cloning
+                ws = corrected = (double [])ws.clone();
+            }
+
+            double above = aboveWaterKM(km, ws, i);
+
+            if (Double.isNaN(above)) { // run over start km
+                // fill all previous
+                for (int j = 0; j < i; ++j) {
+                    ws[j] = ws[i];
+                }
+                continue;
+            }
+
+            double distance = Math.abs(km[i] - above);
+
+            double quarterDistance = 0.25*distance;
+
+            double start = above - quarterDistance;
+
+            double startHeight = DoubleUtil.interpolateSorted(km, ws, start);
+
+            if (Double.isNaN(startHeight)) {
+                // run over start km
+                startHeight = ws[0];
+            }
+
+            double between = above + quarterDistance;
+
+            double aboveHeight = ws[i] + 0.25*(startHeight - ws[i]);
+
+            double [] x = { start,  above,  between };
+            double [] y = { startHeight, aboveHeight, ws[i] };
+
+            if (log.isDebugEnabled()) {
+                for (int j = 0; j < x.length; ++j) {
+                    log.debug("   " + x[j] + " -> " + y[j]);
+                }
+            }
+
+            if (interpolator == null) {
+                interpolator = new SplineInterpolator();
+            }
+
+            PolynomialSplineFunction spline;
+
+            try {
+                spline = interpolator.interpolate(x, y);
+            }
+            catch (MathIllegalArgumentException miae) {
+                errors.addProblem("spline.creation.failed");
+                log.error(miae);
+                continue;
+            }
+
+            try {
+                if (log.isDebugEnabled()) {
+                    log.debug("spline points:");
+                    for (int j = 0; j < x.length; ++j) {
+                        log.debug(x[j] + " " + y[j] + " " + spline.value(x[j]));
+                    }
+                }
+
+                int j = i-1;
+
+                for (; j >= 0 && km[j] >= between; --j) {
+                    ws[j] = ws[i];
+                }
+
+                for (; j >= 0 && ws[j] < startHeight; --j) {
+                    ws[j] = spline.value(km[j]);
+                }
+            }
+            catch (ArgumentOutsideDomainException aode) {
+                errors.addProblem("spline.interpolation.failed");
+                log.error("spline interpolation failed", aode);
+            }
+        } // for all km
+
+        return !backjumps.isEmpty();
+    }
+
+
+    protected static double aboveWaterKM(
+        double [] km,
+        double [] ws,
+        int       wIndex
+    ) {
+        double w = ws[wIndex];
+
+        while (--wIndex >= 0) {
+            // still under water
+            if (ws[wIndex] < w) continue;
+
+            if (ws[wIndex] > w) {
+                // f(ws[wIndex])   = km[wIndex]
+                // f(ws[wIndex+1]) = km[wIndex+1]
+                return Linear.linear(
+                    w,
+                    ws[wIndex], ws[wIndex+1],
+                    km[wIndex], km[wIndex+1]);
+            }
+            else {
+                return km[wIndex];
+            }
+        }
+
+        return Double.NaN;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/Distance.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,15 @@
+package org.dive4elements.river.artifacts.math;
+
+/** Helper to calculate distance(s). */
+public final class Distance {
+    /** Return distance between two values. */
+    public static double distance(double a, double b) {
+        return Math.abs(a - b);
+    }
+
+    /** Return whether a and be are within a certain distance. */
+    public static boolean within(double a, double b, double threshold) {
+        return distance(a, b) <= threshold;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/FilterFunction.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,16 @@
+package org.dive4elements.river.artifacts.math;
+
+public class FilterFunction implements Function {
+
+    protected Function parent;
+
+    public FilterFunction(Function parent) {
+        this.parent = parent;
+    }
+
+    @Override
+    public double value(double x) {
+        return parent.value(x);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/Function.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,6 @@
+package org.dive4elements.river.artifacts.math;
+
+public interface Function {
+    double value(double x);
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/GrubbsOutlier.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,113 @@
+package org.dive4elements.river.artifacts.math;
+
+import java.util.List;
+
+import org.apache.commons.math.MathException;
+
+import org.apache.commons.math.distribution.TDistributionImpl;
+
+import org.apache.commons.math.stat.descriptive.moment.Mean;
+import org.apache.commons.math.stat.descriptive.moment.StandardDeviation;
+
+import org.apache.log4j.Logger;
+
+public class GrubbsOutlier
+{
+    public static final double EPSILON = 1e-5;
+
+    public static final double DEFAULT_ALPHA = 0.05;
+
+    private static Logger log = Logger.getLogger(GrubbsOutlier.class);
+
+    protected GrubbsOutlier() {
+    }
+
+    public static Integer findOutlier(List<Double> values) {
+        return findOutlier(values, DEFAULT_ALPHA, null);
+    }
+
+    public static Integer findOutlier(
+        List<Double> values,
+        double alpha,
+        double[] stdDevResult
+    ) {
+        boolean debug = log.isDebugEnabled();
+
+        if (debug) {
+            log.debug("outliers significance: " + alpha);
+        }
+
+        alpha = 1d - alpha;
+
+        int N = values.size();
+
+        if (debug) {
+            log.debug("Values to check: " + N);
+        }
+
+        if (N < 3) {
+            return null;
+        }
+
+        Mean mean = new Mean();
+        StandardDeviation std = new StandardDeviation();
+
+        for (Double value: values) {
+            double v = value.doubleValue();
+            mean.increment(v);
+            std .increment(v);
+        }
+
+        double m = mean.getResult();
+        double s = std.getResult();
+
+        if (debug) {
+            log.debug("mean: " + m);
+            log.debug("std dev: " + s);
+        }
+
+        double maxZ = -Double.MAX_VALUE;
+        int iv = -1;
+        for (int i = N-1; i >= 0; --i) {
+            double v = values.get(i).doubleValue();
+            double z = Math.abs(v - m);
+            if (z > maxZ) {
+                maxZ = z;
+                iv = i;
+            }
+        }
+
+        if (Math.abs(s) < EPSILON) {
+            return null;
+        }
+
+        maxZ /= s;
+
+        TDistributionImpl tdist = new TDistributionImpl(N-2);
+
+        double t;
+
+        try {
+            t = tdist.inverseCumulativeProbability(alpha/(N+N));
+        }
+        catch (MathException me) {
+            log.error(me);
+            return null;
+        }
+
+        t *= t;
+
+        double za = ((N-1)/Math.sqrt(N))*Math.sqrt(t/(N-2d+t));
+
+        if (debug) {
+            log.debug("max: " + maxZ + " crit: " + za);
+        }
+        if (stdDevResult != null) {
+            stdDevResult[0] = std.getResult();
+        }
+        return maxZ > za
+            ? Integer.valueOf(iv)
+            : 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/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/Identity.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,15 @@
+package org.dive4elements.river.artifacts.math;
+
+public final class Identity
+implements         Function
+{
+    public static final Identity IDENTITY = new Identity();
+
+    public Identity() {
+    }
+
+    public double value(double x) {
+        return x;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/Linear.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,78 @@
+package org.dive4elements.river.artifacts.math;
+
+public final class Linear
+implements         Function
+{
+    private double m;
+    private double b;
+
+    public Linear(
+        double x1, double x2,
+        double y1, double y2
+    ) {
+        // y1 = m*x1 + b
+        // y2 = m*x2 + b
+        // y2 - y1 = m*(x2 - x1)
+        // m = (y2 - y1)/(x2 - x1) # x2 != x1
+        // b = y1 - m*x1
+
+        if (x1 == x2) {
+            m = 0;
+            b = 0.5*(y1 + y2);
+        }
+        else {
+            m = (y2 - y1)/(x2 - x1);
+            b = y1 - m*x1;
+        }
+    }
+
+    public static final double linear(
+        double x,
+        double x1, double x2,
+        double y1, double y2
+    ) {
+        // y1 = m*x1 + b
+        // y2 = m*x2 + b
+        // y2 - y1 = m*(x2 - x1)
+        // m = (y2 - y1)/(x2 - x1) # x2 != x1
+        // b = y1 - m*x1
+
+        if (x1 == x2) {
+            return 0.5*(y1 + y2);
+        }
+        double m = (y2 - y1)/(x2 - x1);
+        double b = y1 - m*x1;
+        return x*m + b;
+    }
+
+    @Override
+    public double value(double x) {
+        return m*x + b;
+    }
+
+    public static final double factor(double x, double p1, double p2) {
+        // 0 = m*p1 + b <=> b = -m*p1
+        // 1 = m*p2 + b
+        // 1 = m*(p2 - p1)
+        // m = 1/(p2 - p1) # p1 != p2
+        // f(x) = x/(p2-p1) - p1/(p2-p1) <=> (x-p1)/(p2-p1)
+
+        return p1 == p2 ? 0.0 : (x-p1)/(p2-p1);
+    }
+
+    public static final double weight(double factor, double a, double b) {
+        //return (1.0-factor)*a + factor*b;
+        return a + factor*(b-a);
+    }
+
+    public static final void weight(
+        double factor,
+        double [] a, double [] b, double [] c
+    ) {
+        int N = Math.min(Math.min(a.length, b.length), c.length);
+        for (int i = 0; i < N; ++i) {
+            c[i] = weight(factor, a[i], b[i]);
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/LinearFilterFunction.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,24 @@
+package org.dive4elements.river.artifacts.math;
+
+public class LinearFilterFunction
+extends      FilterFunction
+{
+    protected double m;
+    protected double b;
+
+    public LinearFilterFunction(double m, double b) {
+        this(Identity.IDENTITY, m, b);
+    }
+
+    public LinearFilterFunction(Function parent, double m, double b) {
+        super(parent);
+        this.m = m;
+        this.b = b;
+    }
+
+    @Override
+    public double value(double x) {
+        return super.value(x)*m + b;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/LinearFunction.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,19 @@
+package org.dive4elements.river.artifacts.math;
+
+public class LinearFunction
+implements   Function
+{
+    protected double m;
+    protected double b;
+
+    public LinearFunction(double m, double b) {
+        this.m = m;
+        this.b = b;
+    }
+
+    @Override
+    public double value(double x) {
+        return x*m + b;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/MovingAverage.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,67 @@
+package org.dive4elements.river.artifacts.math;
+
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+
+public class MovingAverage
+{
+
+    public static double[][] simple(double[][] values, double radius) {
+        TreeMap<Double, Double> map = toMap(values);
+        int N = map.size();
+        double [] xs = new double[N];
+        double [] ys = new double[N];
+        int ndx = 0;
+        for (double x: map.keySet()) {
+            SortedMap<Double, Double> range =
+                map.subMap(x-radius, true, x+radius, true);
+            double avg = 0d;
+            for (double v: range.values()) {
+                avg += v;
+            }
+            avg /= range.size();
+            xs[ndx] = x;
+            ys[ndx] = avg;
+            ndx++;
+        }
+        return new double [][] { xs, ys };
+    }
+
+    public static double[][] weighted(double[][] values, double radius) {
+        TreeMap<Double, Double> map = toMap(values);
+        int N = map.size();
+        double [] xs = new double[N];
+        double [] ys = new double[N];
+        int ndx = 0;
+        double _1radius = 1d/radius;
+        for (double x: map.keySet()) {
+            double avg = 0d;
+            double weights = 0d;
+            for (Map.Entry<Double, Double> e:
+                map.subMap(x-radius, false, x+radius, false).entrySet()
+            ) {
+                double weight = 1d - Math.abs(x - e.getKey())*_1radius;
+                avg += weight*e.getValue();
+                weights += weight;
+            }
+            avg /= weights;
+            xs[ndx] = x;
+            ys[ndx] = avg;
+            ndx++;
+        }
+        return new double [][] { xs, ys };
+    }
+
+    private static TreeMap<Double, Double> toMap(double[][] values) {
+        TreeMap<Double, Double> map = new TreeMap<Double, Double>();
+        double [] xs = values[0];
+        double [] ys = values[1];
+        for (int i = 0; i < xs.length; i++) {
+            map.put(xs[i], ys[i]);
+        }
+        return map;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/NaNFunction.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,16 @@
+package org.dive4elements.river.artifacts.math;
+
+public final class NaNFunction
+implements         Function
+{
+    public static final Function INSTANCE = new NaNFunction();
+
+    private NaNFunction() {
+    }
+
+    @Override
+    public double value(double x) {
+        return Double.NaN;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/StdDevOutlier.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,73 @@
+package org.dive4elements.river.artifacts.math;
+
+import java.util.List;
+
+import org.apache.commons.math.stat.descriptive.moment.StandardDeviation;
+
+import org.apache.log4j.Logger;
+
+public class StdDevOutlier
+{
+    public static final double DEFAULT_FACTOR = 3;
+
+    private static Logger log = Logger.getLogger(StdDevOutlier.class);
+
+    protected StdDevOutlier() {
+    }
+
+    public static Integer findOutlier(List<Double> values) {
+        return findOutlier(values, DEFAULT_FACTOR, null);
+    }
+
+    public static Integer findOutlier(
+        List<Double> values,
+        double       factor,
+        double []    stdDevResult
+    ) {
+        boolean debug = log.isDebugEnabled();
+
+        if (debug) {
+            log.debug("factor for std dev: " + factor);
+        }
+
+        int N = values.size();
+
+        if (debug) {
+            log.debug("Values to check: " + N);
+        }
+
+        if (N < 3) {
+            return null;
+        }
+
+        StandardDeviation stdDev = new StandardDeviation();
+
+        double maxValue = -Double.MAX_VALUE;
+        int    maxIndex = -1;
+        for (int i = N-1; i >= 0; --i) {
+            double value = Math.abs(values.get(i));
+            stdDev.increment(value);
+            if (value > maxValue) {
+                maxValue = value;
+                maxIndex = i;
+            }
+        }
+
+        double sd = stdDev.getResult();
+
+        double accepted = factor * sd;
+
+        if (debug) {
+            log.debug("std dev: " + stdDev);
+            log.debug("accepted: " + accepted);
+            log.debug("max value: " + maxValue);
+        }
+
+        if (stdDevResult != null) {
+            stdDevResult[0] = sd;
+        }
+
+        return maxValue > accepted ? maxIndex : 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/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/Sub.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,17 @@
+package org.dive4elements.river.artifacts.math;
+
+public final class Sub
+implements         Function
+{
+    private double s;
+
+    public Sub(double s) {
+        this.s = s;
+    }
+
+    @Override
+    public double value(double x) {
+        return x - s;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/UnivariateRealFunctionFunction.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,34 @@
+package org.dive4elements.river.artifacts.math;
+
+import org.apache.commons.math.FunctionEvaluationException;
+
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+
+public final class UnivariateRealFunctionFunction
+implements         Function
+{
+    private UnivariateRealFunction function;
+
+    public UnivariateRealFunctionFunction(UnivariateRealFunction function) {
+        this.function = function;
+    }
+
+    @Override
+    public double value(double x) {
+        try {
+            return function.value(x);
+        }
+        catch (FunctionEvaluationException fee) {
+            return Double.NaN;
+        }
+    }
+
+    public UnivariateRealFunction getFunction() {
+        return function;
+    }
+
+    public void setFunction(UnivariateRealFunction function) {
+        this.function = function;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/Utils.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,61 @@
+package org.dive4elements.river.artifacts.math;
+
+
+public final class Utils {
+
+    public static final double EPSILON = 1e-3;
+
+    private Utils() {
+    }
+
+    public static final boolean epsilonEquals(double a, double b) {
+        return epsilonEquals(a, b, EPSILON);
+    }
+
+    public static final boolean epsilonEquals(double a, double b, double eps) {
+        return Math.abs(a - b) < eps;
+    }
+
+    public static int relativeCCW(
+        double x1, double y1,
+        double x2, double y2,
+        double px, double py
+    ) {
+        if ((epsilonEquals(x1, x2) && epsilonEquals(y1, y2))
+        || ((epsilonEquals(x1, px) && epsilonEquals(y1, py)))) {
+            return 0; // Coincident points.
+        }
+        // Translate to the origin.
+        x2 -= x1;
+        y2 -= y1;
+        px -= x1;
+        py -= y1;
+        double slope2 = y2 / x2;
+        double slopep = py / px;
+        if (epsilonEquals(slope2, slopep)
+        || (epsilonEquals(x2, 0.0) && epsilonEquals(px, 0.0))) {
+            return y2 > EPSILON // Colinear.
+                ? (py < -EPSILON ? -1 : py > y2 ? 1 : 0)
+                : (py > -EPSILON ? -1 : py < y2 ? 1 : 0);
+        }
+        if (x2 >= EPSILON && slope2 >= EPSILON) {
+            return px >= EPSILON // Quadrant 1.
+                ? (slope2 > slopep ? 1 : -1)
+                : (slope2 < slopep ? 1 : -1);
+        }
+
+        if (y2 > EPSILON) {
+            return px < -EPSILON // Quadrant 2.
+                ? (slope2 > slopep ? 1 : -1)
+                : (slope2 < slopep ? 1 : -1);
+        }
+        if (slope2 >= EPSILON) {
+            return px >= EPSILON // Quadrant 3.
+                ? (slope2 < slopep ? 1 : -1)
+                : (slope2 > slopep ? 1 : -1);
+        }
+        return px < -EPSILON // Quadrant 4.
+            ? (slope2 < slopep ? 1 : -1)
+            : (slope2 > slopep ? 1 : -1);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/WKmsOperation.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,148 @@
+package org.dive4elements.river.artifacts.math;
+
+import org.dive4elements.river.artifacts.model.WKms;
+import org.dive4elements.river.artifacts.model.WKmsImpl;
+
+import java.util.Arrays;
+
+public abstract class WKmsOperation
+{
+    public static final double EPSILON = 1e-6;
+
+    public static final class KmW
+    implements                Comparable<KmW>
+    {
+        protected double km;
+        protected double w;
+
+        public KmW(double km, double w) {
+            this.km = km;
+            this.w  = w;
+        }
+
+        public int compareTo(KmW other) {
+            return km < other.km
+                ? -1
+                : km > other.km ? +1 : 0;
+        }
+
+        public boolean kmEquals(KmW other) {
+            return Math.abs(km - other.km) < EPSILON;
+        }
+
+        public double subtract(KmW other) {
+            return w - other.w;
+        }
+    } // class KmW
+
+    public static final WKmsOperation SUBTRACTION = new WKmsOperation() {
+
+        @Override
+        public WKms operate(WKms a, WKms b) {
+            return subtract(a, b);
+        }
+    };
+
+    protected WKmsOperation() {
+    }
+
+    public abstract WKms operate(WKms a, WKms b);
+
+    /**
+     * Subtract two series from each other, interpolate values
+     * missing in one series in the other.
+     */
+    public static WKms subtract(WKms minuend, WKms subtrahend) {
+
+        int M = minuend   .size();
+        int S = subtrahend.size();
+
+        // Don't subtract empty sets
+        if (M < 1 || S < 1) {
+            return new WKmsImpl();
+        }
+
+        KmW [] ms = new KmW[M];
+        KmW [] ss = new KmW[S];
+
+        for (int i = 0; i < M; ++i) {
+            ms[i] = new KmW(minuend.getKm(i), minuend.getW(i));
+        }
+
+        for (int i = 0; i < S; ++i) {
+            ss[i] = new KmW(subtrahend.getKm(i), subtrahend.getW(i));
+        }
+
+        Arrays.sort(ms);
+        Arrays.sort(ss);
+
+        // no overlap -> empty result set
+        if (ms[0].km > ss[S-1].km || ss[0].km > ms[M-1].km) {
+            return new WKmsImpl();
+        }
+
+        WKmsImpl result = new WKmsImpl();
+
+        int mi = 0;
+        int si = 0;
+
+        OUT: while (mi < M && si < S) {
+            KmW m = ms[mi];
+            KmW s = ss[si];
+
+            if (m.km + EPSILON < s.km) {
+                // minuend is before subtrahend
+
+                while (ms[mi].km + EPSILON < s.km) {
+                    if (++mi >= M) {
+                        break OUT;
+                    }
+                }
+
+                if (ms[mi].km + EPSILON > s.km) {
+                    double mw = Linear.linear(
+                        s.km,
+                        ms[mi-1].km, ms[mi].km,
+                        ms[mi-1].w,  ms[mi].w);
+                    result.add(s.km, mw - s.w);
+                    ++si;
+                }
+                else { // s.km == ms[mi].km
+                    result.add(s.km, ms[mi].subtract(s));
+                    ++mi;
+                    ++si;
+                }
+            }
+            else if (m.km > s.km + EPSILON) {
+                // subtrahend is before minuend
+
+                while (m.km > ss[si].km + EPSILON) {
+                    if (++si >= S) {
+                        break OUT;
+                    }
+                }
+
+                if (ss[si].km + EPSILON > m.km) {
+                    double sw = Linear.linear(
+                        m.km,
+                        ss[si-1].km, ss[si].km,
+                        ss[si-1].w,  ss[si].w);
+                    result.add(m.km, m.w - sw);
+                }
+                else { // ss[si].km == m.km
+                    result.add(m.km, m.subtract(ss[si]));
+                    ++mi;
+                    ++si;
+                }
+            }
+            else { // m.km == s.km
+                result.add(s.km, m.subtract(s));
+                ++mi;
+                ++si;
+            }
+        }
+
+        return result;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/fitting/App.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,127 @@
+package org.dive4elements.river.artifacts.math.fitting;
+
+import java.util.List;
+import java.util.Map;
+import java.util.ArrayList;
+import java.util.TreeMap;
+import java.util.Comparator;
+
+import java.io.IOException;
+import java.io.BufferedReader;
+import java.io.Reader;
+import java.io.InputStreamReader;
+
+import org.apache.commons.math.optimization.fitting.CurveFitter;
+
+import org.apache.commons.math.optimization.general.LevenbergMarquardtOptimizer;
+
+import org.apache.commons.math.MathException;
+
+public class App
+{
+    public static final double EPS = 1e-5;
+
+    public static final String FUNCTION_NAME =
+        System.getProperty("function", "linear");
+
+    public static final Comparator<Double> EPS_CMP =
+        new Comparator<Double>()  {
+            @Override
+            public int compare(Double a, Double b) {
+                double diff = a - b;
+                if (diff < -EPS) return -1;
+                if (diff >  EPS) return +1;
+                return 0;
+            }
+        };
+
+    public static final List<Double []>readPoints(Reader reader)
+    throws IOException
+    {
+        Map<Double, Double> map = new TreeMap<Double, Double>(EPS_CMP);
+
+        BufferedReader input = new BufferedReader(reader);
+
+        String line;
+        while ((line = input.readLine()) != null) {
+            if ((line = line.trim()).length() == 0 || line.startsWith("#")) {
+                continue;
+            }
+
+            String [] parts = line.split("\\s+");
+
+            if (parts.length < 2) {
+                continue;
+            }
+
+            try {
+                Double x = Double.valueOf(parts[0]);
+                Double y = Double.valueOf(parts[1]);
+
+                Double old = map.put(x, y);
+
+                if (old != null) {
+                    System.err.println("duplicate x: " + x);
+                }
+            }
+            catch (NumberFormatException nfe) {
+                nfe.printStackTrace();
+            }
+        }
+
+        List<Double []> list = new ArrayList<Double []>(map.size());
+
+        for (Map.Entry<Double, Double> entry: map.entrySet()) {
+            list.add(new Double [] { entry.getKey(), entry.getValue() });
+        }
+
+        return list;
+    }
+
+    public static void main(String [] args) {
+
+        Function function = FunctionFactory
+            .getInstance()
+            .getFunction(FUNCTION_NAME);
+
+        if (function == null) {
+            System.err.println("Cannot find function '" + FUNCTION_NAME + "'.");
+            System.exit(1);
+        }
+
+        List<Double []> points = null;
+
+        try {
+            points = readPoints(new InputStreamReader(System.in));
+        }
+        catch (IOException ioe) {
+            ioe.printStackTrace();
+            System.exit(1);
+        }
+
+        LevenbergMarquardtOptimizer lmo = new LevenbergMarquardtOptimizer();
+
+        CurveFitter cf = new CurveFitter(lmo);
+
+        for (Double [] point: points) {
+            cf.addObservedPoint(point[0], point[1]);
+        }
+
+        double [] parameters = null;
+
+        try {
+            parameters = cf.fit(function, function.getInitialGuess());
+        }
+        catch (MathException me) {
+            me.printStackTrace();
+            System.exit(1);
+        }
+
+        String [] parameterNames = function.getParameterNames();
+
+        for (int i = 0; i < parameterNames.length; ++i) {
+            System.err.println(parameterNames[i] + ": " + parameters[i]);
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/fitting/Exp.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,62 @@
+package org.dive4elements.river.artifacts.math.fitting;
+
+public class Exp
+extends      Function
+{
+    public static final Derivative DERIVATIVE =
+        new Derivative("W'(Q) = a^Q*log(a)*m") {
+
+        @Override
+        public org.dive4elements.river.artifacts.math.Function
+            instantiate(double [] parameters)
+        {
+                  double m     = parameters[0];
+            final double a     = parameters[1];
+            final double logam = Math.log(a)*m;
+
+            return new org.dive4elements.river.artifacts.math.Function() {
+                @Override
+                public double value(double Q) {
+                    return Math.pow(a, Q)*logam;
+                }
+            };
+        }
+    };
+
+    public static final Function INSTANCE = new Exp();
+
+    public Exp() {
+        super(
+            "exp",
+            "W(Q) = m * a^Q + b",
+            new String [] { "m", "a", "b" });
+    }
+
+    @Override
+    public double value(double x, double [] parameters) {
+        return parameters[0]*Math.pow(parameters[1], x) + parameters[2];
+    }
+
+    @Override
+    public double [] gradient(double Q, double [] parameters) {
+        double m = parameters[0];
+        double a = parameters[1];
+        double b = parameters[2];
+        return new double [] {
+            Math.pow(a, Q),
+            Math.pow(a, Q-1d)*m*Q,
+            1d
+        };
+    }
+
+    @Override
+    public Derivative getDerivative() {
+        return DERIVATIVE;
+    }
+
+    @Override
+    public Function getInverse() {
+        return InvExp.INSTANCE;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/fitting/Function.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,100 @@
+package org.dive4elements.river.artifacts.math.fitting;
+
+import org.apache.commons.math.FunctionEvaluationException;
+
+import org.apache.commons.math.optimization.fitting.ParametricRealFunction;
+
+import org.dive4elements.river.utils.DoubleUtil;
+
+public abstract class Function
+implements            ParametricRealFunction
+{
+    protected String    name;
+    protected String    description;
+    protected String [] parameterNames;
+    protected double [] initialGuess;
+
+    public static abstract class Derivative {
+
+        protected String description;
+
+        public Derivative() {
+        }
+
+        public Derivative(String description) {
+            this.description = description;
+        }
+
+        public String getDescription() {
+            return description;
+        }
+
+        public abstract org.dive4elements.river.artifacts.math.Function
+            instantiate(double [] parameters);
+
+    } // interface Derivative
+
+    public Function() {
+    }
+
+    public Function(
+        String    name,
+        String    description,
+        String [] parameterNames
+    ) {
+        this(name,
+            description,
+            parameterNames,
+            DoubleUtil.fill(parameterNames.length, 1d));
+    }
+
+    public Function(
+        String    name,
+        String    description,
+        String [] parameterNames,
+        double [] initialGuess
+    ) {
+        this.name           = name;
+        this.description    = description;
+        this.parameterNames = parameterNames;
+        this.initialGuess   = initialGuess;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public String [] getParameterNames() {
+        return parameterNames;
+    }
+
+    public double [] getInitialGuess() {
+        return initialGuess;
+    }
+
+    public org.dive4elements.river.artifacts.math.Function instantiate(
+        final double [] parameters
+    ) {
+        return new org.dive4elements.river.artifacts.math.Function() {
+
+            @Override
+            public double value(double x) {
+                try {
+                    return Function.this.value(x, parameters);
+                }
+                catch (FunctionEvaluationException fee) {
+                    return Double.NaN;
+                }
+            }
+        };
+    }
+
+    public abstract Derivative getDerivative();
+
+    public abstract Function getInverse();
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/fitting/FunctionFactory.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,44 @@
+package org.dive4elements.river.artifacts.math.fitting;
+
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public final class FunctionFactory
+{
+    private static FunctionFactory instance;
+
+    private Map<String, Function> functions;
+
+    private FunctionFactory() {
+        functions = new LinkedHashMap<String, Function>();
+
+        registerFunction(Log.INSTANCE);
+        registerFunction(Linear.INSTANCE);
+        registerFunction(LogLinear.INSTANCE);
+        registerFunction(Exp.INSTANCE);
+        registerFunction(Quad.INSTANCE);
+        registerFunction(Pow.INSTANCE);
+        registerFunction(SQPow.INSTANCE);
+    }
+
+    public static synchronized FunctionFactory getInstance() {
+        if (instance == null) {
+            instance = new FunctionFactory();
+        }
+        return instance;
+    }
+
+    public Function getFunction(String name) {
+        return functions.get(name);
+    }
+
+    public void registerFunction(Function function) {
+        functions.put(function.getName(), function);
+    }
+
+    public Collection<Function> getFunctions() {
+        return functions.values();
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/fitting/InvExp.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,66 @@
+package org.dive4elements.river.artifacts.math.fitting;
+
+public class InvExp
+extends      Function
+{
+    public static final Derivative DERIVATIVE =
+        new Derivative("W'(Q) = 1/(log(a)*(Q-b))") {
+
+        @Override
+        public org.dive4elements.river.artifacts.math.Function
+            instantiate(double [] parameters)
+        {
+            final double a = parameters[1];
+            final double b = parameters[2];
+            final double loga = Math.log(a);
+
+            return new org.dive4elements.river.artifacts.math.Function() {
+                @Override
+                public double value(double Q) {
+                    return 1d/(loga*(Q-a));
+                }
+            };
+        }
+    };
+
+    public static final Function INSTANCE = new InvExp();
+
+    public InvExp() {
+        super(
+            "inv-exp",
+            "Q(W) = log((W-b)/m)/log(a)",
+            new String [] { "m", "a", "b" });
+    }
+
+    @Override
+    public double value(double W, double [] parameters) {
+        double m = parameters[0];
+        double a = parameters[1];
+        double b = parameters[2];
+        return Math.log((W-b)/m)/Math.log(a);
+    }
+
+    @Override
+    public double [] gradient(double Q, double [] parameters) {
+        double m = parameters[0];
+        double a = parameters[1];
+        double b = parameters[2];
+        double loga = Math.log(a);
+        return new double [] {
+            -1d/(loga*m),
+            -Math.log((Q-b)/m)/(a*loga*loga),
+            -1d/(loga*(Q-b))
+        };
+    }
+
+    @Override
+    public Derivative getDerivative() {
+        return DERIVATIVE;
+    }
+
+    @Override
+    public Function getInverse() {
+        return Exp.INSTANCE;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/fitting/InvLinear.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,57 @@
+package org.dive4elements.river.artifacts.math.fitting;
+
+public class InvLinear
+extends      Function
+{
+    public static Function INSTANCE = new InvLinear();
+
+    public static final Derivative DERIVATIVE =
+        new Derivative("Q'(W) = 1/m") {
+
+        @Override
+        public org.dive4elements.river.artifacts.math.Function
+            instantiate(double [] parameters)
+        {
+            final double _1m = 1d/parameters[0];
+
+            return new org.dive4elements.river.artifacts.math.Function() {
+                @Override
+                public double value(double Q) {
+                    return _1m;
+                }
+            };
+        }
+    };
+
+    public InvLinear() {
+        super("inv-linear", "W(Q) = (Q-b)/m", new String [] { "m", "b" });
+    }
+
+    @Override
+    public double value(double Q, double [] parameters) {
+        double m = parameters[0];
+        double b = parameters[1];
+        return (Q-b)/m;
+    }
+
+    @Override
+    public double [] gradient(double Q, double [] parameters) {
+        double m = parameters[0];
+        double b = parameters[1];
+        return new double [] {
+            -(Q-b)/(m*m),
+            -1d/m
+        };
+    }
+
+    @Override
+    public Derivative getDerivative() {
+        return DERIVATIVE;
+    }
+
+    @Override
+    public Function getInverse() {
+        return Linear.INSTANCE;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/fitting/InvLog.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,55 @@
+package org.dive4elements.river.artifacts.math.fitting;
+
+public class InvLog
+extends      Function
+{
+    public static final Derivative DERIVATIVE =
+        new Derivative("Q'(W) = exp(W/m)/m") {
+
+        @Override
+        public org.dive4elements.river.artifacts.math.Function
+            instantiate(double [] parameters)
+        {
+            final double _1m = 1d / parameters[0];
+
+            return new org.dive4elements.river.artifacts.math.Function() {
+                @Override
+                public double value(double W) {
+                    return Math.exp(W*_1m)*_1m;
+                }
+            };
+        }
+    };
+
+    public static final Function INSTANCE = new InvLog();
+
+    public InvLog() {
+        super("inv-log",  "Q(W) = e^(W/m) - b", new String [] { "m", "b" });
+    }
+
+    @Override
+    public double value(double w, double [] parameters) {
+        double m = parameters[0];
+        double b = parameters[1];
+        return Math.exp(w/m) - b;
+    }
+
+    @Override
+    public double [] gradient(double w, double [] parameters) {
+        double m   = parameters[0];
+        double b   = parameters[1];
+        double ewm = Math.exp(w/m);
+        return new double [] { -w*ewm/(m*m), -1 };
+    }
+
+    @Override
+    public Derivative getDerivative() {
+        return DERIVATIVE;
+    }
+
+    @Override
+    public Function getInverse() {
+        return Log.INSTANCE;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/fitting/InvLogLinear.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,64 @@
+package org.dive4elements.river.artifacts.math.fitting;
+
+public class InvLogLinear
+extends      Function
+{
+    public static final Derivative DERIVATIVE =
+        new Derivative("Q'(W) = e^(W/a)/(a*m)") {
+
+        @Override
+        public org.dive4elements.river.artifacts.math.Function
+            instantiate(double [] parameters)
+        {
+            final double _1a = 1d/parameters[0];
+            final double _1am = 1d/(parameters[0] * parameters[1]);
+
+            return new org.dive4elements.river.artifacts.math.Function() {
+                @Override
+                public double value(double W) {
+                    return Math.exp(W*_1a)*_1am;
+                }
+            };
+        }
+    };
+    public static final Function INSTANCE = new InvLogLinear();
+
+    public InvLogLinear() {
+        super(
+            "inv-log-linear",
+            "Q(W)=(e^(W/a)-b)/m",
+            new String [] { "a", "m", "b" });
+    }
+
+    @Override
+    public double value(double W, double [] parameters) {
+        double a = parameters[0];
+        double m = parameters[1];
+        double b = parameters[2];
+        return (Math.exp(W/a) - b)/m;
+    }
+
+    @Override
+    public double [] gradient(double W, double [] parameters) {
+        double a   = parameters[0];
+        double m   = parameters[1];
+        double b   = parameters[2];
+        double eWa = Math.exp(W/a);
+        return new double [] {
+            -(W*eWa)/(a*a*m)
+            -(eWa-b)/(m*m),
+            -1/m
+        };
+    }
+
+    @Override
+    public Derivative getDerivative() {
+        return DERIVATIVE;
+    }
+
+    @Override
+    public Function getInverse() {
+        return LogLinear.INSTANCE;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/fitting/InvPow.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,72 @@
+package org.dive4elements.river.artifacts.math.fitting;
+
+public class InvPow
+extends      Function
+{
+    public static final Derivative DERIVATIVE =
+        new Derivative("Q'(W) = ((W-d)/a)^(1/c)/(c*(W-d))") {
+
+        @Override
+        public org.dive4elements.river.artifacts.math.Function
+            instantiate(double [] parameters)
+        {
+                  double  a = parameters[0];
+            final double  c = parameters[1];
+            final double  d = parameters[2];
+            final double _1a = 1d/a;
+            final double _1c = 1d/c;
+
+            return new org.dive4elements.river.artifacts.math.Function() {
+                @Override
+                public double value(double W) {
+                    double Wd = W-d;
+                    return Math.pow(Wd*_1a, _1c)/(c*Wd);
+                }
+            };
+        }
+    };
+
+    public static final Function INSTANCE = new InvPow();
+
+    public InvPow() {
+        super(
+            "pow",
+            "Q(W) = ((W-d)/a)^(1/c)",
+            new String [] { "a", "c", "d" });
+    }
+
+    @Override
+    public double value(double W, double [] parameters) {
+        double a = parameters[0];
+        double c = parameters[1];
+        double d = parameters[2];
+        return Math.pow((W-d)/a, 1d/c);
+    }
+
+    @Override
+    public double [] gradient(double W, double [] parameters) {
+        double a = parameters[0];
+        double c = parameters[1];
+        double d = parameters[2];
+        double _1c = 1d/c;
+        double Wdac = Math.pow((W-d)/a, 1d/c);
+        double Wd = W-d;
+        return new double [] {
+            -Wdac/(a*c),
+            (Wdac*Math.log(Wd/a))/(c*c),
+            -Wdac/(c*Wd)
+        };
+    }
+
+    @Override
+    public Derivative getDerivative() {
+        // TODO: Implement me!
+        return null;
+    }
+
+    @Override
+    public Function getInverse() {
+        return Pow.INSTANCE;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/fitting/InvQuad.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,69 @@
+package org.dive4elements.river.artifacts.math.fitting;
+
+public class InvQuad
+extends      Function
+{
+    public static final Derivative DERIVATIVE =
+        new Derivative("Q'(W) = 1/sqrt(4*n*(W-b)+m^2)") {
+
+        @Override
+        public org.dive4elements.river.artifacts.math.Function
+            instantiate(double [] parameters)
+        {
+                  double n  = parameters[0];
+                  double m  = parameters[1];
+            final double b  = parameters[2];
+            final double n4 = 4d*n;
+            final double mm = m*m;
+
+            return new org.dive4elements.river.artifacts.math.Function() {
+                @Override
+                public double value(double W) {
+                    return 1d/Math.sqrt(n4*(W-b)+mm);
+                }
+            };
+        }
+    };
+
+    public static final Function INSTANCE = new InvQuad();
+
+    public InvQuad() {
+        super(
+            "inv-quad",
+            "(sqrt(4*n*W-4*b*n+m^2)-m)/(2*n)",
+            new String [] { "n", "m", "b" });
+    }
+
+    @Override
+    public double value(double W, double [] parameters) {
+        double n = parameters[0];
+        double m = parameters[1];
+        double b = parameters[2];
+        return (Math.sqrt(4d*n*(W - b) + m*m)-m)/(2d*n);
+    }
+
+    @Override
+    public double [] gradient(double W, double [] parameters) {
+        double n  = parameters[0];
+        double m  = parameters[1];
+        double b  = parameters[2];
+        double Wb = W-b;
+        double sn4Wb = Math.sqrt(4d*n*Wb + m*m);
+        return new double [] {
+            Wb/(n*sn4Wb)-(sn4Wb-m)/(2d*n*n),
+            (m/sn4Wb-1d)/(2d*n),
+            -1d/sn4Wb
+        };
+    }
+
+    @Override
+    public Derivative getDerivative() {
+        return DERIVATIVE;
+    }
+
+    @Override
+    public Function getInverse() {
+        return Quad.INSTANCE;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/fitting/InvSQPow.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,64 @@
+package org.dive4elements.river.artifacts.math.fitting;
+
+public class InvSQPow
+extends      Function
+{
+    public static final Derivative DERIVATIVE =
+        new Derivative("Q'(S) = (S/a)^(1/b)/(b*S)") {
+
+        @Override
+        public org.dive4elements.river.artifacts.math.Function
+            instantiate(double [] parameters)
+        {
+            final double _1a = 1d/parameters[0];
+            final double   b = parameters[1];
+            final double _1b = 1d/b;
+
+            return new org.dive4elements.river.artifacts.math.Function() {
+                @Override
+                public double value(double S) {
+                    return Math.pow(S*_1a, _1b)/(b*S);
+                }
+            };
+        }
+    };
+    public static final Function INSTANCE = new InvSQPow();
+
+    public InvSQPow() {
+        super(
+            "inv-sq-pow",
+            "Q(S) = Q=(S/a)^(1/b)",
+            new String [] { "a", "b" });
+    }
+
+    @Override
+    public double value(double S, double [] parameters) {
+        double a = parameters[0];
+        double b = parameters[1];
+        return Math.pow(S/a, 1d/b);
+    }
+
+    @Override
+    public double [] gradient(double S, double [] parameters) {
+        double a     = parameters[0];
+        double b     = parameters[1];
+        double Sa    = S/a;
+        double _1b   = 1d/b;
+        double eSa1b = Math.pow(Sa, _1b);
+        return new double [] {
+            -eSa1b/(a*b),
+            -(eSa1b*Math.log(Sa))/(b*b)
+        };
+    }
+
+    @Override
+    public Derivative getDerivative() {
+        return DERIVATIVE;
+    }
+
+    @Override
+    public Function getInverse() {
+        return SQPow.INSTANCE;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/fitting/Linear.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,50 @@
+package org.dive4elements.river.artifacts.math.fitting;
+
+public class Linear
+extends      Function
+{
+    public static final Derivative DERIVATIVE =
+        new Derivative("W'(Q) = m") {
+
+        @Override
+        public org.dive4elements.river.artifacts.math.Function
+            instantiate(double [] parameters)
+        {
+            final double m = parameters[0];
+
+            return new org.dive4elements.river.artifacts.math.Function() {
+                @Override
+                public double value(double Q) {
+                    return m;
+                }
+            };
+        }
+    };
+
+    public static final Function INSTANCE = new Linear();
+
+    public Linear() {
+        super("linear", "W(Q) = m*Q + b", new String [] { "m", "b" });
+    }
+
+    @Override
+    public double value(double x, double [] parameters) {
+        return x*parameters[0] + parameters[1];
+    }
+
+    @Override
+    public double [] gradient(double x, double [] parameters) {
+        return new double [] { x, 1d };
+    }
+
+    @Override
+    public Derivative getDerivative() {
+        return DERIVATIVE;
+    }
+
+    @Override
+    public Function getInverse() {
+        return InvLinear.INSTANCE;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/fitting/Log.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,57 @@
+package org.dive4elements.river.artifacts.math.fitting;
+
+public class Log
+extends      Function
+{
+    public static final Derivative DERIVATIVE =
+        new Derivative("W'(Q) = m/(Q+b)") {
+
+        @Override
+        public org.dive4elements.river.artifacts.math.Function
+            instantiate(double [] parameters)
+        {
+            final double m = parameters[0];
+            final double b = parameters[1];
+
+            return new org.dive4elements.river.artifacts.math.Function() {
+                @Override
+                public double value(double Q) {
+                    return m/(Q+b);
+                }
+            };
+        }
+    };
+
+    public static final Function INSTANCE = new Log();
+
+    public Log() {
+        super("log",  "W(Q) = m*ln(Q + b)", new String [] { "m", "b" });
+    }
+
+    @Override
+    public double value(double x, double [] parameters) {
+        return parameters[0]*Math.log(x + parameters[1]);
+    }
+
+    @Override
+    public double [] gradient(double x, double [] parameters) {
+        double m = parameters[0];
+        double b = parameters[1];
+        double b_x = b + x;
+        return new double [] {
+            Math.log(b_x),
+            m/b_x
+        };
+    }
+
+    @Override
+    public Derivative getDerivative() {
+        return DERIVATIVE;
+    }
+
+    @Override
+    public Function getInverse() {
+        return InvLog.INSTANCE;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/fitting/LogLinear.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,65 @@
+package org.dive4elements.river.artifacts.math.fitting;
+
+public class LogLinear
+extends      Function
+{
+    public static final Derivative DERIVATIVE =
+        new Derivative("W'(Q) = a*m/(m*Q + b)") {
+
+        @Override
+        public org.dive4elements.river.artifacts.math.Function
+            instantiate(double [] parameters)
+        {
+            final double a = parameters[0];
+            final double m = parameters[1];
+            final double b = parameters[2];
+
+            return new org.dive4elements.river.artifacts.math.Function() {
+                @Override
+                public double value(double Q) {
+                    return a*m/(m*Q + b);
+                }
+            };
+        }
+    };
+
+    public static final Function INSTANCE = new LogLinear();
+
+    public LogLinear() {
+        super(
+            "log-linear",
+            "W(Q) = a*ln(m*Q + b)",
+            new String [] { "a", "m", "b" });
+    }
+
+    @Override
+    public double value(double x, double [] parameters) {
+        return parameters[0]*Math.log(parameters[1]*x + parameters[2]);
+    }
+
+    @Override
+    public double [] gradient(double x, double [] parameters) {
+        double a = parameters[0];
+        double m = parameters[1];
+        double b = parameters[2];
+
+        double lin = m*x + b;
+
+        return new double [] {
+            Math.log(lin),
+            a*x / lin,
+            a / lin
+        };
+    }
+
+    @Override
+    public Derivative getDerivative() {
+        return DERIVATIVE;
+    }
+
+    @Override
+    public Function getInverse() {
+        return InvLogLinear.INSTANCE;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/fitting/Pow.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,61 @@
+package org.dive4elements.river.artifacts.math.fitting;
+
+public class Pow
+extends      Function
+{
+    public static final Derivative DERIVATIVE =
+        new Derivative("W'(Q) = a*c*Q^(c-1)") {
+
+        @Override
+        public org.dive4elements.river.artifacts.math.Function
+            instantiate(double [] parameters)
+        {
+            final double a = parameters[0];
+            final double c = parameters[1];
+
+            return new org.dive4elements.river.artifacts.math.Function() {
+                @Override
+                public double value(double Q) {
+                    return a*c*Math.pow(Q, c-1);
+                }
+            };
+        }
+    };
+
+    public static final Function INSTANCE = new Pow();
+
+    public Pow() {
+        super(
+            "pow",
+            "W(Q) = a*Q^c + d",
+            new String [] { "a", "c", "d" });
+    }
+
+    @Override
+    public double value(double x, double [] parameters) {
+        return parameters[0]*Math.pow(x, parameters[1]) + parameters[2];
+    }
+
+    @Override
+    public double [] gradient(double x, double [] parameters) {
+        double a   = parameters[0];
+        double c   = parameters[1];
+        double x_c = Math.pow(x, c);
+        return new double [] {
+            x_c,
+            a*x_c*Math.log(x),
+            1d
+        };
+    }
+
+    @Override
+    public Derivative getDerivative() {
+        return DERIVATIVE;
+    }
+
+    @Override
+    public Function getInverse() {
+        return InvPow.INSTANCE;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/fitting/Quad.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,55 @@
+package org.dive4elements.river.artifacts.math.fitting;
+
+public class Quad
+extends      Function
+{
+    public static final Derivative DERIVATIVE =
+        new Derivative("W'(Q) = 2*n*Q+m") {
+
+        @Override
+        public org.dive4elements.river.artifacts.math.Function
+            instantiate(double [] parameters)
+        {
+            final double n2 = 2d*parameters[0];
+            final double m  = parameters[1];
+
+            return new org.dive4elements.river.artifacts.math.Function() {
+                @Override
+                public double value(double Q) {
+                    return n2*Q+m;
+                }
+            };
+        }
+    };
+
+    public static final Function INSTANCE = new Quad();
+
+    public Quad() {
+        super(
+            "quad",
+            "W(Q) = n*Q^2 + m*Q + b",
+            new String [] { "n", "m", "b" });
+    }
+
+    @Override
+    public double value(double x, double [] parameters) {
+        // n*Q^2 + m*Q + b <=> Q*(n*Q + m) + b
+        return x*(parameters[0]*x + parameters[1]) + parameters[2];
+    }
+
+    @Override
+    public double [] gradient(double x, double [] parameters) {
+        return new double [] { x*x, x, 1d };
+    }
+
+    @Override
+    public Derivative getDerivative() {
+        return DERIVATIVE;
+    }
+
+    @Override
+    public Function getInverse() {
+        return InvQuad.INSTANCE;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/math/fitting/SQPow.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,60 @@
+package org.dive4elements.river.artifacts.math.fitting;
+
+public class SQPow
+extends      Function
+{
+    public static final Derivative DERIVATIVE =
+        new Derivative("S'(Q) = a*b*Q^(b-1)") {
+
+        @Override
+        public org.dive4elements.river.artifacts.math.Function
+            instantiate(double [] parameters)
+        {
+            final double a = parameters[0];
+            final double b = parameters[1];
+
+            return new org.dive4elements.river.artifacts.math.Function() {
+                @Override
+                public double value(double Q) {
+                    return a*b*Math.pow(Q, b-1);
+                }
+            };
+        }
+    };
+
+    public static final Function INSTANCE = new SQPow();
+
+    public SQPow() {
+        super(
+            "sq-pow",
+            "S(Q) = a*Q^b",
+            new String [] { "a", "b" });
+    }
+
+    @Override
+    public double value(double x, double [] parameters) {
+        return parameters[0]*Math.pow(x, parameters[1]);
+    }
+
+    @Override
+    public double [] gradient(double q, double [] parameters) {
+        double a   = parameters[0];
+        double b   = parameters[1];
+        double q_b = Math.pow(q, b);
+        return new double [] {
+            q_b,
+            a*q_b*Math.log(q),
+        };
+    }
+
+    @Override
+    public Derivative getDerivative() {
+        return DERIVATIVE;
+    }
+
+    @Override
+    public Function getInverse() {
+        return InvSQPow.INSTANCE;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/AnnotationFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,103 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.AnnotationArtifact;
+
+import org.dive4elements.river.jfree.FLYSAnnotation;
+import org.dive4elements.river.jfree.StickyAxisAnnotation;
+
+import org.dive4elements.river.model.Annotation;
+import org.dive4elements.river.model.FastAnnotations;
+
+import org.dive4elements.artifactdatabase.state.DefaultFacet;
+import org.dive4elements.artifactdatabase.state.Facet;
+
+import org.dive4elements.river.utils.FLYSUtils;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+
+/**
+ * Facet to access Annotations (landmarks, POIs) of a river.
+ */
+public class AnnotationFacet
+extends      DefaultFacet
+{
+    /** Logger for this class. */
+    private static final Logger logger = Logger.getLogger(AnnotationFacet.class);
+
+
+    /**
+     * Trivial Constructor.
+     */
+    public AnnotationFacet() {
+    }
+
+
+    /**
+     * Trivial Constructor for a AnnotationFacet.
+     *
+     * @param index       Database-Index to use.
+     * @param name        Name (~type) of Facet.
+     * @param description Description of Facet.
+     */
+    public AnnotationFacet(int index, String name, String description) {
+        super(index, name, description);
+    }
+
+
+    /**
+     * Get List of Annotations for river from Artifact.
+     *
+     * @param artifact (Annotation-)Artifact to query for list of Annotations.
+     * @param context  Ignored.
+     */
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+
+        // TODO issue880: Make annotations available _per type_
+        AnnotationArtifact annotationArtifact = (AnnotationArtifact) artifact;
+
+        String riverName = FLYSUtils.getRivername((FLYSArtifact)artifact);
+
+        FastAnnotations fas = LocationProvider.getAnnotations(riverName);
+
+        String filterName = annotationArtifact.getFilterName();
+
+        FastAnnotations.Filter filter = (filterName == null)
+            ? FastAnnotations.IS_POINT
+            : new FastAnnotations.NameFilter(filterName);
+
+        List<StickyAxisAnnotation> xy =
+            new ArrayList<StickyAxisAnnotation>(fas.size());
+
+        for (Iterator<FastAnnotations.Annotation> iter =
+                fas.filter(filter); iter.hasNext();) {
+            FastAnnotations.Annotation fa = iter.next();
+
+            xy.add(new StickyAxisAnnotation(
+                fa.getPosition(),
+                (float)fa.getA(),
+                StickyAxisAnnotation.SimpleAxis.X_AXIS));
+        }
+
+        return new FLYSAnnotation(description, xy);
+    }
+
+
+    @Override
+    public Facet deepCopy() {
+        AnnotationFacet copy = new AnnotationFacet();
+        copy.set(this);
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/AnnotationsFactory.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,113 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.math.BigDecimal;
+
+import java.util.List;
+import java.util.Iterator;
+import java.util.Collections;
+
+import org.dive4elements.river.backend.SessionHolder;
+import org.dive4elements.river.model.Annotation;
+import org.dive4elements.river.model.River;
+
+import org.hibernate.Session;
+import org.hibernate.Query;
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class AnnotationsFactory {
+
+    /**
+     * Get Annotations which do not have a "b" ("to")-value set.
+     *
+     * @param river name of the river of interest.
+     *
+     * @return List of Annotations for river which have only "a" ("from")
+     *          value set.
+     */
+    public static List<Annotation> getPointAnnotations(String river) {
+        Session session = SessionHolder.HOLDER.get();
+
+        Query query = session.createQuery(
+            "from Annotation as an " +
+            "where an.range.b = null and an.range.river.name=:name " +
+            "order by range.a");
+        query.setParameter("name", river);
+        return query.list();
+    }
+
+
+    public static List<Annotation> getAnnotations(River river) {
+        Session session = SessionHolder.HOLDER.get();
+
+        Query query = session.createQuery(
+            "from Annotation as an where an.range.river = :river" +
+            " order by an.range.a");
+        query.setParameter("river", river);
+        return query.list();
+    }
+
+
+    public static Annotation getAnnotation(String river, double km) {
+        Session session = SessionHolder.HOLDER.get();
+
+        Query query = session.createQuery(
+            "from Annotation as a " +
+            "where a.range.river.name = :river AND a.range.a = :km");
+
+        query.setParameter("river", river);
+        query.setParameter("km", BigDecimal.valueOf(km));
+
+        List<Annotation> result = query.list();
+
+        return result != null && result.size() > 0 ? result.get(0) : null;
+    }
+
+
+    /**
+     * Get minimal "a" ("from") and maximal "b" ("to") value of annotations'
+     * ranges of a river.
+     *
+     * @param river name of the river of interest.
+     *
+     * @return Array containing minimal "a" and max "b" value of any
+     *         annotation stored for the given river.
+     */
+    public static double[] getAnnotationsBreadth(String river) {
+        Session session = SessionHolder.HOLDER.get();
+
+        Query minAQuery = session.createQuery(
+            "select min(a), max(b) from Range where river.name=:name");
+        minAQuery.setParameter("name", river);
+
+        double[] minAmaxB = {0.0f, 0.0f};
+        Object[] row = (Object[]) minAQuery.list().iterator().next();
+        minAmaxB[0] = ((BigDecimal) row[0]).doubleValue();
+        minAmaxB[1] = ((BigDecimal) row[1]).doubleValue();
+        return minAmaxB;
+    }
+
+
+    public static Iterator<Annotation> getAnnotationsIterator(
+        String riverName
+    ) {
+        Session session = SessionHolder.HOLDER.get();
+
+        Query riverQuery = session.createQuery(
+            "from River where name = :name");
+        riverQuery.setParameter("name", riverName);
+        List<River> rivers = riverQuery.list();
+        if (rivers.isEmpty()) {
+            return Collections.<Annotation>emptyList().iterator();
+        }
+
+        Query query = session.createQuery(
+            "from Annotation as an" +
+            " where an.range.river = :river order by an.range.a");
+        query.setParameter("river", rivers.get(0));
+
+        return (Iterator<Annotation>)query.iterate();
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/AreaFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,145 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.artifactdatabase.state.DefaultFacet;
+import org.dive4elements.artifactdatabase.state.Facet;
+
+import org.dive4elements.artifacts.DataProvider;
+
+import org.dive4elements.river.artifacts.AreaArtifact;
+
+
+/**
+ * Trival Facet for areas.
+ * Note that this Facet comes in two "types" (names):
+ *  <ul>
+ *    <li>CROSS_SECTION_AREA (cross_section.area) and</li>
+ *    <li>LONGITUDINAL_SECTION_AREA (longitudinal.area</li>
+ *  </ul>
+ * This is to support different diagram types without being painted in both
+ * at the same time. The name has to be given when constructing.
+ */
+public class AreaFacet
+extends      DefaultFacet
+{
+    private static Logger logger = Logger.getLogger(AreaFacet.class);
+
+    /**
+     * Constructor, set (maybe localized) description and name.
+     * @param idx Index given when querying artifact for data.
+     * @param name important to discern areas in different diagram types.
+     */
+    public AreaFacet(int idx, String name, String description) {
+        super(idx, name, description);
+    }
+
+
+    /**
+     * Gets Cross Section (profile).
+     * @param art artifact to get data from.
+     * @param context ignored
+     */
+    public Object getData(Artifact art, CallContext context) {
+        logger.debug("Get data for area.");
+
+        // Get information from artifact about which
+        // info to grab from blackboard.
+        //
+        // All compatible facets should provide their data
+        // under the key (Artifact-UUID + Facet-Index).
+        AreaArtifact artifact = (AreaArtifact) art;
+        Object lowerData      = null;
+        Object upperData      = null;
+        String stemFacetName  = null;
+
+        List<DataProvider> providers = context.
+            getDataProvider(artifact.getLowerDPKey());
+        if (providers.size() < 1) {
+            logger.warn("No 'lower' provider given for area [" +
+                artifact.getLowerDPKey() + "]");
+        }
+        else {
+            lowerData = providers.get(0).provideData(
+                artifact.getLowerDPKey(), null, context);
+            logger.debug("'Lower' data provider key for area [" +
+                artifact.getLowerDPKey() + "]");
+            stemFacetName = artifact.getLowerDPKey().split(":")[1];
+        }
+
+        providers = context.getDataProvider(artifact.getUpperDPKey());
+        if (providers.size() < 1) {
+            logger.warn("No 'upper' provider given for area [" +
+                artifact.getUpperDPKey() + "]");
+        }
+        else {
+            upperData = providers.get(0).provideData(
+                artifact.getUpperDPKey(), null, context);
+            logger.debug("'Upper' data provider key for area [" +
+                artifact.getUpperDPKey() + "]");
+            if (stemFacetName == null) {
+                stemFacetName = artifact.getUpperDPKey().split(":")[1];
+            }
+        }
+
+        if (upperData == null && lowerData == null) {
+            logger.warn("Not given 'upper' and 'lower' for area");
+        }
+
+        return new Data(stemFacetName, lowerData, upperData,
+            Boolean.valueOf(artifact.getPaintBetween()));
+    }
+
+
+    /** Do a deep copy. */
+    @Override
+    public Facet deepCopy() {
+        AreaFacet copy = new AreaFacet(this.index, this.name, this.description);
+        copy.set(this);
+        return copy;
+    }
+
+    /** Result data bundle. */
+    public class Data {
+        protected String  rootFacetName;
+        protected Object  upperData;
+        protected Object  lowerData;
+        protected boolean doPaintBetween;
+
+        /** Create a new result data bundle. */
+        public Data(String rootName, Object low, Object up, boolean between) {
+            this.rootFacetName  = rootName;
+            this.lowerData      = low;
+            this.upperData      = up;
+            this.doPaintBetween = between;
+        }
+
+        /** Get name of a facet that is involved in area generation
+         * to induce type (e.g. longitudinal_section.w -> "W over km"). */
+        public String getRootFacetName() {
+            return this.rootFacetName;
+        }
+
+        /** Get data for 'upper' curve of area. */
+        public Object getUpperData() {
+            return this.upperData;
+        }
+
+        /** Get data for 'lower' curve of area. */
+        public Object getLowerData() {
+            return this.lowerData;
+        }
+
+        /** Whether to fill whole area between (in contrast to 'under'
+         *  or 'over'). */
+        public boolean doPaintBetween() {
+            return this.doPaintBetween;
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/BlackboardDataFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,78 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifactdatabase.state.DefaultFacet;
+
+
+/**
+ * Facet that writes artifact-uui and facet index on the blackboard,
+ * delivers data if asked so.
+ */
+public class BlackboardDataFacet extends DefaultFacet {
+
+    public BlackboardDataFacet() {}
+
+    /** Do not instantiate a BlackboardDataFacet, subclass it instead. */
+    public BlackboardDataFacet(int idx, String name, String description) {
+        super(idx, name, description);
+    }
+
+
+    /** Do not instantiate a BlackboardDataFacet, subclass it instead. */
+    public BlackboardDataFacet(String name, String description) {
+        super(0, name, description);
+    }
+
+
+    /** Define key to which to respond when asked for 'blackboard'
+     * (DataProvider)- data. */
+    public String areaDataKey(Artifact art) {
+        return art.identifier() + ":" + getName() + ":" + getIndex();
+    }
+
+
+    /** Hey, We can ArtifactUUID+:+FacetName+:+FacetIndex (i.e. getData)! */
+    @Override
+    public List getStaticDataProviderKeys(Artifact art) {
+        List list = new ArrayList();
+        list.add(areaDataKey(art));
+        return list;
+    }
+
+
+    /**
+     * Can provide whatever getData returns.
+     * @param key      will respond on uuid+index
+     * @param param    ignored
+     * @param context  ignored
+     * @return whatever getData delivers.
+     */
+    @Override
+    public Object provideBlackboardData(Artifact artifact,
+        Object key,
+        Object param,
+        CallContext context
+    ) {
+        if (key.equals(areaDataKey(artifact))) {
+            return getData(artifact, context);
+        }
+        else {
+            return null;
+        }
+    }
+
+    /** Copy deeply. */
+    @Override
+    public Facet deepCopy() {
+        BlackboardDataFacet copy = new BlackboardDataFacet();
+        copy.set(this);
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/Calculation.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,167 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import java.io.Serializable;
+
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.river.artifacts.resources.Resources;
+
+import org.dive4elements.river.utils.Formatter;
+
+/** A calculation that can have result and problems. */
+public class Calculation
+implements   Serializable
+{
+    /** Problem of a calculation.
+     * Has location and message. */
+    public static class Problem
+    implements          Serializable
+    {
+        protected Double    km;
+        protected String    msg;
+        protected Object [] args;
+
+        public Problem() {
+        }
+
+        public Problem(String msg) {
+            this.msg = msg;
+        }
+
+        public Problem(String msg, Object [] args) {
+            this.msg  = msg;
+            this.args = args;
+        }
+
+        public Problem(double km, String msg) {
+            this.km  = km;
+            this.msg = msg;
+        }
+
+        public Problem(double km, String msg, Object [] args) {
+            this.km   = km;
+            this.msg  = msg;
+            this.args = args;
+        }
+
+        public Element toXML(Document document, CallMeta meta) {
+            Element problem = document.createElement("problem");
+            if (km != null) {
+                problem.setAttribute(
+                    "km",
+                    Formatter.getCalculationKm(meta).format(km));
+            }
+            String text = args != null
+                ? Resources.getMsg(meta, msg, msg, args)
+                : Resources.getMsg(meta, msg, msg);
+            problem.setTextContent(text);
+            return problem;
+        }
+
+        public String getMsg() {
+            return msg;
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (!(other instanceof Problem)) {
+                return false;
+            }
+            Problem o = (Problem)other;
+            return !(!msg.equals(o.msg)
+                || (km == null && o.km != null)
+                || (km != null && o.km == null)
+                || (km != null && !km.equals(o.km))
+                || !Arrays.equals(args, o.args));
+        }
+    } // class Problem
+
+    protected List<Problem> problems;
+
+    public Calculation() {
+    }
+
+    public Calculation(String msg) {
+        addProblem(msg);
+    }
+
+    /** New Calculation with error which can be translated given args. */
+    public Calculation(String msg, Object ... args) {
+        addProblem(msg, args);
+    }
+
+    protected List<Problem> checkProblems() {
+        if (problems == null) {
+            problems = new ArrayList<Problem>();
+        }
+        return problems;
+    }
+
+    public void addProblems(Calculation other) {
+        List<Problem> otherProblems = other.problems;
+        if (otherProblems != null) {
+            List<Problem> problems = checkProblems();
+            for (Problem problem: otherProblems) {
+                if (!problems.contains(problem)) {
+                    problems.add(problem);
+                }
+            }
+        }
+    }
+
+    public void addProblem(Problem problem) {
+        List<Problem> problems = checkProblems();
+        if (!problems.contains(problem)) {
+            problems.add(problem);
+        }
+    }
+
+    public void addProblem(String msg) {
+        addProblem(new Problem(msg));
+    }
+
+    public void addProblem(String msg, Object ... args) {
+        addProblem(new Problem(msg, args));
+    }
+
+    public void addProblem(double km, String msg) {
+        addProblem(new Problem(km, msg));
+    }
+
+    public void addProblem(double km, String msg, Object ... args) {
+        addProblem(new Problem(km, msg, args));
+    }
+
+    public boolean hasProblems() {
+        return problems != null && !problems.isEmpty();
+    }
+
+    public int numProblems() {
+        return problems != null ? problems.size() : 0;
+    }
+
+    public List<Problem> getProblems() {
+        return problems;
+    }
+
+    public void toXML(Document document, CallMeta meta) {
+
+        Element root = document.createElement("problems");
+
+        if (hasProblems()) {
+            for (Problem problem: problems) {
+                root.appendChild(problem.toXML(document, meta));
+            }
+        }
+
+        document.appendChild(root);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/Calculation1.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,73 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.util.ArrayList;
+
+import org.apache.log4j.Logger;
+
+public class Calculation1
+extends      Calculation
+{
+    private static Logger logger = Logger.getLogger(Calculation1.class);
+
+    protected double [] kms;
+    protected double [] qs;
+    protected double [] ws;
+    protected double    refKm;
+
+    public Calculation1() {
+    }
+
+    public Calculation1(
+        double [] kms,
+        double [] qs,
+        double [] ws,
+        double    refKm
+    ) {
+        this.kms   = kms;
+        this.qs    = qs;
+        this.ws    = ws;
+        this.refKm = refKm;
+    }
+
+    public CalculationResult calculate(WstValueTable wst) {
+
+        ArrayList<WQKms> results = new ArrayList<WQKms>();
+
+        String    prefix;
+        double [] origData;
+
+        if (ws != null) { prefix = "W="; origData = ws; }
+        else            { prefix = "Q="; origData = qs; }
+
+        int oldNumProblems = numProblems();
+
+        for (int i = 0; i < qs.length; i++) {
+
+            double [] oqs = new double[kms.length];
+            double [] ows = new double[kms.length];
+
+            boolean success =
+                wst.interpolate(qs[i], refKm, kms, ows, oqs, this) != null;
+
+            int newNumProblems = numProblems();
+
+            if (success) {
+                WQKms result = new WQKms(kms, oqs, ows, prefix + origData[i]);
+                if (oldNumProblems != newNumProblems) {
+                    logger.debug(
+                        qs[i] + " caused " + (newNumProblems-oldNumProblems) +
+                        " new problem(s).");
+                    result.removeNaNs();
+                }
+                results.add(result);
+            }
+
+            oldNumProblems = newNumProblems;
+        }
+
+        return new CalculationResult(
+            results.toArray(new WQKms[results.size()]),
+            this);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/Calculation2.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,50 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.util.Arrays;
+
+import org.apache.log4j.Logger;
+
+
+/** ComputedDischargeCurve. */
+public class Calculation2
+extends      Calculation
+{
+    private static Logger logger = Logger.getLogger(Calculation2.class);
+
+    protected double km;
+
+    public Calculation2() {
+    }
+
+    public Calculation2(double km) {
+        this.km = km;
+    }
+
+    public CalculationResult calculate(WstValueTable wst) {
+
+        logger.debug("Calculation2.calculate");
+
+        double [][] wqs = wst.interpolateWQ(km, this);
+
+        if (wqs == null || wqs[0].length == 0) {
+            addProblem("cannot.compute.discharge.curve");
+            return new CalculationResult(new WQKms[0], this);
+        }
+
+        double [] ws = wqs[0];
+        double [] qs = wqs[1];
+        double [] kms = new double[ws.length];
+
+        Arrays.fill(kms, km);
+
+        WQKms wqkms = new WQKms(kms, qs, ws, String.valueOf(km));
+
+        if (hasProblems()) {
+            logger.debug("found " + numProblems() + " problems.");
+            wqkms.removeNaNs();
+        }
+
+        return new CalculationResult(new WQKms[] { wqkms }, this);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/Calculation3.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,49 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.apache.log4j.Logger;
+
+public class Calculation3
+extends      Calculation
+{
+    private static Logger logger = Logger.getLogger(Calculation3.class);
+
+    protected double    km;
+    protected int    [] days;
+    protected double [] qs;
+
+    public Calculation3() {
+    }
+
+    public Calculation3(double km, int [] days, double [] qs) {
+        this.km   = km;
+        this.days = days;
+        this.qs   = qs;
+    }
+
+    public CalculationResult calculate(WstValueTable wst) {
+
+        double [] ws = wst.interpolateW(km, qs, new double[qs.length], this);
+
+        if (days == null || days.length == 0) {
+            addProblem(km, "cannot.find.ds");
+        }
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("Calculate duration curve data:");
+            logger.debug("    km       : " + km);
+            logger.debug("    num Days : " + (days != null ? days.length : 0));
+            logger.debug("    num Qs   : " + (qs != null ? qs.length : 0));
+            logger.debug("    result Ws: " + (ws != null ? ws.length : 0));
+        }
+
+        WQDay wqday = new WQDay(days, ws, qs);
+
+        if (hasProblems()) {
+            logger.debug("calculation caused "+numProblems()+" problem(s).");
+            wqday.removeNaNs();
+        }
+
+        return new CalculationResult(wqday, this);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/Calculation4.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,370 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.dive4elements.river.artifacts.access.Calculation4Access;
+
+import org.dive4elements.river.artifacts.math.BackJumpCorrector;
+import org.dive4elements.river.artifacts.math.Function;
+import org.dive4elements.river.artifacts.math.Identity;
+import org.dive4elements.river.artifacts.math.Linear;
+
+import org.dive4elements.river.artifacts.model.RiverFactory;
+
+import org.dive4elements.river.artifacts.model.WstValueTable.QPosition;
+
+import org.dive4elements.river.model.River;
+
+import org.dive4elements.river.utils.DoubleUtil;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+public class Calculation4
+extends      Calculation
+{
+    private static Logger logger = Logger.getLogger(Calculation4.class);
+
+    public static final double MINIMAL_STEP_WIDTH = 1e-5;
+
+    protected List<Segment> segments;
+
+    protected boolean isQ;
+    protected double  from;
+    protected double  to;
+    protected double  step;
+    protected String  river;
+
+    public Calculation4() {
+    }
+
+    public Calculation4(Calculation4Access access) {
+        logger.debug("Calculation4Access.cnst");
+        String        river    = access.getRiver();
+        List<Segment> segments = access.getSegments();
+        double []     range    = access.getFromToStep();
+        boolean       isQ      = access.isQ();
+
+        if (river == null) {
+            addProblem("no.river.selected");
+        }
+
+        if (range == null) {
+            addProblem("no.range.found");
+        }
+
+        if (segments == null || segments.isEmpty()) {
+            addProblem("cannot.create.segments");
+        }
+
+        if (!hasProblems()) {
+            this.river    = river;
+            this.segments = segments;
+            this.from     = range[0];
+            this.to       = range[1];
+            this.step     = range[2];
+            this.isQ      = isQ;
+        }
+    }
+
+    public CalculationResult calculate() {
+        if (hasProblems()) {
+            return new CalculationResult(new WQKms[0], this);
+        }
+
+        WstValueTable table = null;
+        River r = RiverFactory.getRiver(river);
+        if (r == null) {
+            addProblem("no.river.found");
+        }
+        else {
+            table = WstValueTableFactory.getTable(r);
+            if (table == null) {
+                addProblem("no.wst.for.river");
+            }
+            else {
+                Segment.setReferencePointConvertQ(segments, r, isQ, this);
+            }
+        }
+
+        return hasProblems()
+            ? new CalculationResult(new WQKms[0], this)
+            : innerCalculate(table);
+    }
+
+    protected CalculationResult innerCalculate(WstValueTable table) {
+        boolean debug = logger.isDebugEnabled();
+
+        if (debug) {
+            logger.debug(
+                "calculate from " + from + " to " + to + " step " + step);
+            logger.debug("# segments: " + segments.size());
+            for (Segment segment: segments) {
+                logger.debug("  " + segment);
+            }
+        }
+
+        int numResults = segments.get(0).values.length;
+
+        if (numResults < 1) {
+            logger.debug("no values given");
+            addProblem("no.values.given");
+            return new CalculationResult(new WQKms[0], this);
+        }
+
+
+        WQKms [] results = new WQKms[numResults];
+        for (int i = 0; i < results.length; ++i) {
+            results[i] = new WQKms();
+        }
+
+        if (Math.abs(step) < MINIMAL_STEP_WIDTH) {
+            step = MINIMAL_STEP_WIDTH;
+        }
+
+        if (from > to) {
+            step = -step;
+        }
+
+        QPosition [] qPositions = new QPosition[numResults];
+
+        Function [] functions = new Function[numResults];
+
+        double [] out = new double[2];
+
+        Segment sentinel = new Segment(Double.MAX_VALUE);
+        Segment s1 = sentinel, s2 = sentinel;
+
+        for (double pos = from;
+             from < to ? pos <= to : pos >= to;
+             pos = DoubleUtil.round(pos + step)
+        ) {
+            if (pos < s1.referencePoint || pos > s2.referencePoint) {
+                if (debug) {
+                    logger.debug("need to find new interval for " + pos);
+                }
+                // find new interval
+                if (pos <= segments.get(0).referencePoint) {
+                    // before first segment -> "gleichwertig"
+                    if (debug) {
+                        logger.debug("before first segment -> gleichwertig");
+                    }
+                    Segment   first  = segments.get(0);
+                    double [] values = first.values;
+                    double    refPos = first.referencePoint;
+                    for (int i = 0; i < qPositions.length; ++i) {
+                        qPositions[i] = table.getQPosition(
+                            refPos, values[i]);
+                    }
+                    sentinel.setReferencePoint(-Double.MAX_VALUE);
+                    s1 = sentinel;
+                    s2 = segments.get(0);
+                    Arrays.fill(functions, Identity.IDENTITY);
+                }
+                else if (pos >= segments.get(segments.size()-1).referencePoint) {
+                    // after last segment -> "gleichwertig"
+                    if (debug) {
+                        logger.debug("after last segment -> gleichwertig");
+                    }
+                    Segment   last   = segments.get(segments.size()-1);
+                    double [] values = last.values;
+                    double    refPos = last.referencePoint;
+                    for (int i = 0; i < qPositions.length; ++i) {
+                        qPositions[i] = table.getQPosition(
+                            refPos, values[i]);
+                    }
+                    sentinel.setReferencePoint(Double.MAX_VALUE);
+                    s1 = last;
+                    s2 = sentinel;
+                    Arrays.fill(functions, Identity.IDENTITY);
+                }
+                else { // "ungleichwertig"
+                    // find matching interval
+                    if (debug) {
+                        logger.debug("in segments -> ungleichwertig");
+                    }
+                    s1 = s2 = null;
+                    for (int i = 1, N = segments.size(); i < N; ++i) {
+                        Segment si1 = segments.get(i-1);
+                        Segment si  = segments.get(i);
+                        if (debug) {
+                            logger.debug("check " + pos + " in " +
+                                si1.referencePoint + " - " + si.referencePoint);
+                        }
+                        if (pos >= si1.referencePoint
+                        &&  pos <= si. referencePoint) {
+                            s1 = si1;
+                            s2 = si;
+                            break;
+                        }
+                    }
+
+                    if (s1 == null) {
+                        throw new IllegalStateException("no interval found");
+                    }
+
+                    Segment anchor, free;
+
+                    if (from > to) { anchor = s1; free = s2; }
+                    else           { anchor = s2; free = s1; }
+
+                    // build transforms based on "gleichwertiger" phase
+                    for (int i = 0; i < qPositions.length; ++i) {
+                        QPosition qi = table.getQPosition(
+                            anchor.referencePoint,
+                            anchor.values[i]);
+
+                        if ((qPositions[i] = qi) == null) {
+                            addProblem(pos, "cannot.find.q", anchor.values[i]);
+                            functions[i] = Identity.IDENTITY;
+                        }
+                        else {
+                            double qA = table.getQ(qi, anchor.referencePoint);
+                            double qF = table.getQ(qi, free  .referencePoint);
+
+                            functions[i] = Double.isNaN(qA) || Double.isNaN(qF)
+                                ? Identity.IDENTITY
+                                : new Linear(
+                                    qA, qF,
+                                    anchor.values[i], free.values[i]);
+
+                            if (debug) {
+                                logger.debug(
+                                    anchor.referencePoint + ": " +
+                                    qA + " -> " + functions[i].value(qA) +
+                                    " / " + free.referencePoint + ": " +
+                                    qF + " -> " + functions[i].value(qF));
+                            }
+                        }
+                    } // build transforms
+                } // "ungleichwertiges" interval
+            } // find matching interval
+
+            for (int i = 0; i < qPositions.length; ++i) {
+                QPosition qPosition = qPositions[i];
+
+                if (qPosition == null) {
+                    continue;
+                }
+
+                if (table.interpolate(pos, out, qPosition, functions[i])) {
+                    results[i].add(out[0], out[1], pos);
+                }
+                else {
+                    addProblem(pos, "cannot.interpolate.w.q");
+                }
+            }
+        }
+
+        // Backjump correction
+        for (int i = 0; i < results.length; ++i) {
+            BackJumpCorrector bjc = new BackJumpCorrector();
+
+            double [] ws  = results[i].getWs();
+            double [] kms = results[i].getKms();
+
+            if (bjc.doCorrection(kms, ws, this)) {
+                results[i] = new WQCKms(results[i], bjc.getCorrected());
+            }
+        }
+
+        // Name the curves.
+        for (int i = 0; i < results.length; ++i) {
+            results[i].setName(createName(i));
+        }
+
+        // Generate the "Umhuellende".
+        ConstantWQKms [] infoldings =
+            generateInfolding(table, results, from, to, step);
+
+        // TODO: Use qkms in a new result type.
+        WQKms [] newResults = new WQKms[results.length + infoldings.length];
+        System.arraycopy(
+            results, 0, newResults, 0, results.length);
+        System.arraycopy(
+            infoldings, 0, newResults, results.length, infoldings.length);
+
+        return new CalculationResult(newResults, this);
+    }
+
+    protected ConstantWQKms [] generateInfolding(
+        WstValueTable wst,
+        WQKms []      results,
+        double        from,
+        double        to,
+        double        step
+    ) {
+        WstValueTable.Column [] columns = wst.getColumns();
+
+        InfoldingColumns ic = new InfoldingColumns(columns);
+        ic.markInfoldingColumns(results);
+
+        List<ConstantWQKms> infoldings = new ArrayList<ConstantWQKms>();
+
+        boolean [] infoldingColumns = ic.getInfoldingColumns();
+
+        double [] kms = null;
+        double [] ws  = null;
+
+        for (int i = 0; i < infoldingColumns.length; ++i) {
+            if (!infoldingColumns[i]) {
+                continue;
+            }
+
+            if (kms == null) {
+                kms = DoubleUtil.explode(from, to, step);
+                ws  = new double[kms.length];
+            }
+
+            QRangeTree.QuickQFinder qf =
+                columns[i].getQRangeTree().new QuickQFinder();
+
+            int numProblemsBefore = numProblems();
+            double [] qs = qf.findQs(kms, this);
+
+            String name = columns[i].getName();
+            ConstantWQKms infolding = new ConstantWQKms(kms, qs, ws, name);
+
+            if (numProblems() > numProblemsBefore) {
+                infolding.removeNaNs();
+            }
+
+            infoldings.add(infolding);
+        }
+
+        for (int i = 0; i < infoldings.size(); i++) {
+            String name = infoldings.get(i).getName();
+            // TODO: i18n
+            if (i == 0) {
+                infoldings.get(i).setName("untere Umh\u00fcllende " + name);
+            }
+            else if (i ==  infoldings.size() - 1) {
+                infoldings.get(i).setName("obere Umh\u00fcllende " + name);
+            }
+            else {
+                infoldings.get(i).setName("geschnitten " + name);
+            }
+        }
+        return infoldings.toArray(new ConstantWQKms[infoldings.size()]);
+    }
+
+    // TODO: issue1109/2, merge with FixRealizingCalculation
+    protected String createName(int index) {
+        // TODO: i18n
+        StringBuilder sb = new StringBuilder(isQ ? "Q" : "W");
+        sb.append(" benutzerdefiniert (");
+        for (int i = 0, N = segments.size(); i < N; ++i) {
+            if (i > 0) {
+                sb.append("; ");
+            }
+            Segment segment = segments.get(i);
+            sb.append((segment.backup != null
+                ? segment.backup
+                : segment.values)[index]);
+        }
+        sb.append(')');
+        return sb.toString();
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/Calculation5.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,77 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.river.utils.Formatter;
+
+import java.text.NumberFormat;
+
+import java.util.ArrayList;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+
+public class Calculation5
+extends      Calculation
+{
+    private static Logger log = Logger.getLogger(Calculation5.class);
+
+    protected double    startKm;
+    protected double [] endKms;
+
+    public Calculation5() {
+    }
+
+    public Calculation5(double startKm, double [] endKms) {
+        this.startKm = startKm;
+        this.endKms  = endKms;
+    }
+
+    public CalculationResult calculate(
+        WstValueTable       wst,
+        Map<Double, Double> kms2gaugeDatums,
+        CallContext         context
+    ) {
+        ArrayList<WWQQ> results = new ArrayList<WWQQ>();
+
+        int numProblems = numProblems();
+
+        CallMeta meta = context.getMeta();
+
+        for (double endKm: endKms) {
+
+            double [][] wws = wst.relateWs(startKm, endKm, this);
+            int newNumProblems = numProblems();
+
+            if (wws.length == 4) {
+                WWQQ wwqq = new WWQQ(
+                    generateName(meta, startKm, endKm),
+                    startKm, kms2gaugeDatums.get(startKm), wws[0], wws[1],
+                    endKm,   kms2gaugeDatums.get(endKm),   wws[2], wws[3]);
+
+                if (newNumProblems > numProblems) {
+                    wwqq.removeNaNs();
+                }
+
+                results.add(wwqq);
+            }
+            numProblems = newNumProblems;
+        }
+
+        return new CalculationResult(
+            results.toArray(new WWQQ[results.size()]),
+            this);
+    }
+
+    protected static String generateName(
+        CallMeta meta,
+        double   startKm,
+        double   endKm
+    ) {
+        NumberFormat nf = Formatter.getCalculationKm(meta);
+        return "W(km " + nf.format(startKm) +
+               ") ~ W(km " + nf.format(endKm) + ")";
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/Calculation6.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,347 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.river.model.DischargeTable;
+import org.dive4elements.river.model.Gauge;
+import org.dive4elements.river.model.TimeInterval;
+
+
+/**
+ * Historical Discharge Calculation.
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class Calculation6 extends Calculation {
+
+    private static final Logger logger = Logger.getLogger(Calculation6.class);
+
+    private int mode;
+    private long[] timerange;
+    private double[] values;
+
+    public static final int MODE_W = 0;
+    public static final int MODE_Q = 1;
+
+    public static final double SCALE = 1d;
+
+    public Calculation6(int mode, long[] timerange, double[] values) {
+        this.mode = mode;
+        this.timerange = timerange;
+        this.values = values;
+    }
+
+    public CalculationResult calculate(Gauge gauge) {
+        if (!checkParameters() || gauge == null) {
+            logger.warn("Parameters not valid for calculation.");
+
+            return null;
+        }
+
+        if (logger.isDebugEnabled()) {
+            debug();
+        }
+
+        DischargeTable refTable = fetchReferenceTable(gauge);
+        List<DischargeTable> dts = fetchDischargeTables(gauge);
+
+        int numTables = dts.size();
+
+        logger.debug("Take " + numTables + " into account.");
+
+        if (numTables == 0) {
+            addProblem("cannot.find.hist.q.tables");
+        }
+
+        WQTimerange[] wqt = prepareTimerangeData(refTable, dts);
+        WQKms[] wqs = prepareWQData(dts);
+
+        logger.debug("Number of calculation results: " + wqt.length);
+
+        return new CalculationResult(new HistoricalDischargeData(wqt, wqs),
+            this);
+    }
+
+    protected boolean checkParameters() {
+        if (!(mode == MODE_W || mode == MODE_Q)) {
+            logger.warn("Invalid mode '" + mode + "' for calculation.");
+            return false;
+        }
+
+        if (timerange == null || timerange.length < 2) {
+            logger.warn("Invalid timerange for calculation.");
+            return false;
+        }
+
+        if (values == null || values.length == 0) {
+            logger.warn("No values for W or Q specified.");
+            return false;
+        }
+
+        return true;
+    }
+
+    protected DischargeTable fetchReferenceTable(Gauge gauge) {
+        return gauge.fetchMasterDischargeTable();
+    }
+
+    protected List<DischargeTable> fetchDischargeTables(Gauge gauge) {
+        List<DischargeTable> relevant = new ArrayList<DischargeTable>();
+        List<DischargeTable> all = gauge.getDischargeTables();
+
+        for (DischargeTable dt : all) {
+            if (isDischargeTableRelevant(dt)) {
+                relevant.add(dt);
+            }
+        }
+
+        return relevant;
+    }
+
+    /** True if timerange of given discharge table overlaps with timerange. */
+    protected boolean isDischargeTableRelevant(DischargeTable dt) {
+        TimeInterval ti = dt.getTimeInterval();
+
+        if (dt.getKind() == Gauge.MASTER_DISCHARGE_TABLE || ti == null) {
+            return false;
+        }
+
+        Date start = ti.getStartTime();
+        long startTime = start.getTime();
+
+        if (startTime >= timerange[0] && startTime <= timerange[1]) {
+            return true;
+        }
+
+        Date stop = ti.getStopTime();
+        long stopTime = stop != null ? stop.getTime() : -1l;
+
+        if (stopTime >= timerange[0] && stopTime <= timerange[1]) {
+            return true;
+        }
+
+        logger.debug("DischargeTable not in range: " + start + " -> " + stop);
+
+        return false;
+    }
+
+    protected WQTimerange[] prepareTimerangeData(DischargeTable refTable,
+        List<DischargeTable> dts) {
+        if (refTable == null) {
+            addProblem("cannot.find.hist.q.reftable");
+            return prepareSimpleData(dts);
+        }
+        else {
+            return prepareData(refTable, dts);
+        }
+    }
+
+    protected WQKms[] prepareWQData(List<DischargeTable> dts) {
+        WQKms[] wqs = new WQKms[dts.size()];
+
+        int idx = 0;
+
+        for (DischargeTable dt : dts) {
+            double[][] values = null;
+
+            if (dt.getKind() == DischargeTables.MASTER) {
+                values = DischargeTables.loadDischargeTableValues(dt,
+                    DischargeTables.MASTER_SCALE);
+
+            }
+            else {
+                values = DischargeTables.loadDischargeTableValues(dt,
+                    DischargeTables.HISTORICAL_SCALE);
+
+            }
+
+            wqs[idx++] = prepareWQ(dt, values);
+        }
+
+        return wqs;
+    }
+
+    protected WQKms prepareWQ(DischargeTable dt, double[][] values) {
+        double km = dt.getGauge().getStation().doubleValue();
+
+        double[] kms = new double[values[0].length];
+        Arrays.fill(kms, km);
+
+        return new HistoricalWQKms(kms, values[0], values[1],
+            String.valueOf(km), dt.getTimeInterval());
+    }
+
+    /** Without reference. */
+    protected WQTimerange[] prepareSimpleData(List<DischargeTable> dts) {
+        List<WQTimerange> wqts = new ArrayList<WQTimerange>(values.length);
+
+        for (double value : values) {
+            logger.debug("Prepare data for value: " + value);
+
+            String name = mode == MODE_W ? "W=" + value : "Q=" + value;
+            WQTimerange wqt = null;
+
+            for (DischargeTable dt : dts) {
+                Date[] ti = prepareTimeInterval(dt);
+                Timerange t = new Timerange(ti[0], ti[1]);
+                double w;
+                double q;
+
+                if (mode == MODE_W) {
+                    w = value;
+                    q = findValueForW(dt, w, DischargeTables.HISTORICAL_SCALE);
+
+                    if (Double.isNaN(q)) {
+                        logger.warn("Cannot find Q for W: " + w);
+                        addProblem("cannot.find.hist.q.for.w", w, ti[0], ti[1]);
+                        continue;
+                    }
+                }
+                else {
+                    q = value;
+                    w = findValueForQ(dt, q, DischargeTables.HISTORICAL_SCALE);
+                }
+
+                logger.debug("Q=" + q + " | W=" + w);
+
+                if (wqt == null) {
+                    wqt = new WQTimerange(name);
+                }
+
+                wqt.add(w, q, t);
+            }
+
+            if (wqt != null) {
+                wqts.add(wqt);
+            }
+        }
+
+        return wqts.toArray(new WQTimerange[wqts.size()]);
+    }
+
+    /** With reference. */
+    protected HistoricalWQTimerange[] prepareData(DischargeTable refTable,
+        List<DischargeTable> dts) {
+        List<HistoricalWQTimerange> wqts = new ArrayList<HistoricalWQTimerange>(
+            values.length);
+
+        for (double value : values) {
+            logger.debug("Prepare data plus diff for value: " + value);
+
+            String name = mode == MODE_W ? "W=" + value : "Q=" + value;
+            HistoricalWQTimerange wqt = null;
+
+            double ref;
+            double diff;
+
+            if (refTable != null && mode == MODE_W) {
+                ref = findValueForW(refTable, value,
+                    DischargeTables.MASTER_SCALE);
+            }
+            else if (refTable != null) {
+                ref = findValueForQ(refTable, value,
+                    DischargeTables.MASTER_SCALE);
+            }
+            else {
+                ref = Double.NaN;
+            }
+
+            for (DischargeTable dt : dts) {
+                Date[] ti = prepareTimeInterval(dt);
+
+                Timerange t = new Timerange(ti[0], ti[1]);
+                double w;
+                double q;
+
+                if (mode == MODE_W) {
+                    w = value;
+                    q = findValueForW(dt, w, DischargeTables.HISTORICAL_SCALE);
+
+                    if (Double.isNaN(q)) {
+                        logger.warn("Cannot find Q for W: " + w);
+                        addProblem("cannot.find.hist.q.for.w", w, ti[0], ti[1]);
+                        continue;
+                    }
+
+                    diff = ref - q;
+                }
+                else {
+                    q = value;
+                    w = findValueForQ(dt, q, DischargeTables.HISTORICAL_SCALE);
+
+                    if (Double.isNaN(w)) {
+                        logger.warn("Cannot find W for Q: " + q);
+                        addProblem("cannot.find.hist.w.for.q", q, ti[0], ti[1]);
+                        continue;
+                    }
+                    diff = ref - w;
+                }
+
+                logger.debug("Q=" + q + " | W=" + w + " | Ref = " + ref);
+
+                if (wqt == null) {
+                    wqt = new HistoricalWQTimerange(name);
+                }
+
+                wqt.add(w, q, diff, t);
+            }
+
+            if (wqt != null) {
+                wqts.add(wqt);
+            }
+        }
+
+        return (HistoricalWQTimerange[]) wqts
+            .toArray(new HistoricalWQTimerange[wqts.size()]);
+    }
+
+    /** Returns discharge table interval as Date[]. */
+    protected Date[] prepareTimeInterval(DischargeTable dt) {
+        TimeInterval ti = dt.getTimeInterval();
+
+        Date start = ti.getStartTime();
+        Date end = ti.getStopTime();
+
+        if (end == null) {
+            logger.warn("TimeInterval has no stop time set!");
+
+            end = new Date();
+        }
+
+        return new Date[] { start, end };
+    }
+
+    protected double findValueForW(DischargeTable dt, double w, double scale) {
+        double[][] vs = DischargeTables.loadDischargeTableValues(dt, scale);
+        double[] qs = DischargeTables.getQsForW(vs, w);
+        return qs.length == 0 ? Double.NaN : qs[0];
+    }
+
+    protected double findValueForQ(DischargeTable dt, double q, double scale) {
+        double[][] vs = DischargeTables.loadDischargeTableValues(dt, scale);
+        double[] ws = DischargeTables.getWsForQ(vs, q);
+
+        return ws.length == 0 ? Double.NaN : ws[0];
+    }
+
+    /**
+     * Writes the parameters used for this calculation to logger.
+     */
+    public void debug() {
+        StringBuilder sb = new StringBuilder();
+        for (double value : values) {
+            sb.append(String.valueOf(value) + " ");
+        }
+
+        logger.debug("========== Calculation6 ==========");
+        logger.debug("   Mode:         " + mode);
+        logger.debug("   Timerange:    " + timerange[0] + " - " + timerange[1]);
+        logger.debug("   Input values: " + sb.toString());
+        logger.debug("==================================");
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/CalculationMessage.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,46 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.dive4elements.artifacts.Message;
+
+
+public class CalculationMessage implements Message {
+
+    protected String message;
+    protected int    steps;
+    protected int    currentStep;
+
+
+    public CalculationMessage() {
+    }
+
+
+    public CalculationMessage(int steps, int currentStep, String message) {
+        this.steps       = steps;
+        this.currentStep = currentStep;
+        this.message     = message;
+    }
+
+
+    public int getSteps() {
+        return steps;
+    }
+
+
+    public int getCurrentStep() {
+        return currentStep;
+    }
+
+
+    public String getMessage() {
+        return message;
+    }
+
+
+    @Override
+    public String getText() {
+        return
+            String.valueOf(currentStep) + "/" + String.valueOf(steps) +
+            " - " + getMessage();
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/CalculationResult.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,45 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.io.Serializable;
+
+/**
+ * Wraps result(s) of a Calculation and eventual error reports.
+ */
+public class CalculationResult
+implements   Serializable
+{
+    protected Object      data;
+    protected Calculation report;
+
+    public CalculationResult() {
+    }
+
+    public CalculationResult(Calculation report) {
+        this(null, report);
+    }
+
+    /**
+     * @param report report (e.g. error messages).
+     */
+    public CalculationResult(Object data, Calculation report) {
+        this.data   = data;
+        this.report = report;
+    }
+
+    public Object getData() {
+        return data;
+    }
+
+    public void setData(Object data) {
+        this.data = data;
+    }
+
+    public Calculation getReport() {
+        return report;
+    }
+
+    public void setReport(Calculation report) {
+        this.report = report;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/ConstantWQKms.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,30 @@
+package org.dive4elements.river.artifacts.model;
+
+public class ConstantWQKms
+extends      WQKms
+{
+    public ConstantWQKms() {
+        this("");
+    }
+
+    public ConstantWQKms(String name) {
+        super(name);
+    }
+
+    public ConstantWQKms(int capacity) {
+        this(capacity, "");
+    }
+
+    public ConstantWQKms(int capacity, String name) {
+        super(capacity, name);
+    }
+
+    public ConstantWQKms(double [] kms, double [] qs, double [] ws) {
+        this(kms, qs, ws, "");
+    }
+
+    public ConstantWQKms(double [] kms, double [] qs, double [] ws, String name) {
+        super(kms, qs, ws, name);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/CrossSectionFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,122 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+
+import org.dive4elements.river.artifacts.CrossSectionArtifact;
+
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+
+/**
+ * Trival Facet for Cross Sections (profiles).
+ */
+public class CrossSectionFacet
+extends      BlackboardDataFacet
+implements   FacetTypes {
+
+    public static String BLACKBOARD_CS_MASTER_DATA
+        = "crosssection.masterprofile.data";
+
+    public static String BLACKBOARD_CS_PREV_KM
+        = "crosssection.masterprofile.km.prev";
+
+    public static String BLACKBOARD_CS_NEXT_KM
+        = "crosssection.masterprofile.km.next";
+
+
+    private static Logger logger = Logger.getLogger(CrossSectionFacet.class);
+
+    protected ComputeType type;
+
+
+    /** Trivial constructor, set (maybe localized) description. */
+    public CrossSectionFacet(int idx, String description) {
+        super(idx, CROSS_SECTION, description);
+        type = ComputeType.ADVANCE;
+    }
+
+
+    /** Tell world we know about crosssection masters data and its index. */
+    public List getStaticDataProviderKeys(Artifact art) {
+        CrossSectionArtifact artifact = (CrossSectionArtifact) art;
+        List keys = new ArrayList();
+        if (artifact.isMaster()) {
+            keys.add(BLACKBOARD_CS_MASTER_DATA);
+            keys.add(BLACKBOARD_CS_PREV_KM);
+            keys.add(BLACKBOARD_CS_NEXT_KM);
+        }
+        keys.add(artifact.identifier() + getIndex());
+        keys.addAll(super.getStaticDataProviderKeys(art));
+        return keys;
+    }
+
+
+    /**
+     * Can provide the master cross section lines or its index.
+     * @param artifact crosssection-artifact
+     * @param key      will respond on BLACKBOARD_CS_MASTER_DATA
+     * @param param    ignored
+     * @param context  ignored
+     * @return data from artifact (cross section master track).
+     */
+    public Object provideBlackboardData(Artifact artifact,
+        Object key,
+        Object param,
+        CallContext context
+    ) {
+        CrossSectionArtifact crossSection = (CrossSectionArtifact) artifact;
+
+        if (key.equals(BLACKBOARD_CS_MASTER_DATA)) {
+            return crossSection.searchCrossSectionLine();
+        }
+        else if (key.equals(artifact.identifier() + getIndex())) {
+            return getData(artifact, context);
+        }
+        else if (key.equals(BLACKBOARD_CS_NEXT_KM)) {
+            return crossSection.getNextKm();
+        }
+        else if (key.equals(BLACKBOARD_CS_PREV_KM)) {
+            return crossSection.getPrevKm();
+        }
+        else {
+            Object obj = super.provideBlackboardData(artifact, key, param,
+                context);
+            if (obj == null) {
+                logger.warn("Cannot provide data for key: " + key);
+            }
+            return obj;
+        }
+    }
+
+
+    /**
+     * Gets Cross Section (profile).
+     * @param art artifact to get data from.
+     * @param context ignored
+     */
+    public Object getData(Artifact art, CallContext context) {
+        logger.debug("Get data for cross section");
+
+        CrossSectionArtifact artifact = (CrossSectionArtifact)art;
+
+        return artifact.getCrossSectionData();
+    }
+
+
+    /** Do a deep copy. */
+    @Override
+    public Facet deepCopy() {
+        CrossSectionFacet copy = new CrossSectionFacet(this.index, this.description);
+        copy.set(this);
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/CrossSectionFactory.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,112 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.util.List;
+
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.Element;
+
+import org.dive4elements.river.backend.SessionHolder;
+import org.dive4elements.river.model.CrossSection;
+import org.dive4elements.river.model.River;
+
+import org.dive4elements.river.artifacts.cache.CacheFactory;
+
+import org.hibernate.Session;
+import org.hibernate.Query;
+
+
+/**
+ * Get Cross Sections.
+ */
+public class CrossSectionFactory {
+
+    protected final static String CACHE_NAME = "cross_sections";
+
+    // TODO use caching consistently, streamline acces.
+    /**
+     * Get CrossSections for an instantiated River.
+     *
+     * @param river river object.
+     *
+     * @return List of Cross Sections of river.
+     */
+    public static List<CrossSection> getCrossSections(River river) {
+        return getCrossSections(river.getName());
+    }
+
+
+    /**
+     * Get Cross Sections for a river by name.
+     *
+     * @param riverName name of the river of interest.
+     *
+     * @return List of Cross Sections of river.
+     */
+    public static List<CrossSection> getCrossSections(String riverName) {
+        Session session = SessionHolder.HOLDER.get();
+        Query query = session.createQuery(
+            "from CrossSection where river.name = :rivername");
+        query.setParameter("rivername", riverName);
+        return query.list();
+    }
+
+
+    /**
+     * True if the given section is the "newest" for that river.
+     * @param section Given section
+     * @return true if the section has the most advanced end of its validity interval
+     *         or the most advanced start of its validity interval.
+     */
+    public static boolean isNewest(CrossSection section) {
+        Session session = SessionHolder.HOLDER.get();
+        Query query = session.createQuery(
+            "from CrossSection where river.id = :riverid "
+            + " order by timeInterval.stopTime desc, timeInterval.startTime desc");
+        query.setParameter("riverid", section.getRiver().getId());
+
+        List result = query.list();
+
+        if (result == null || result.isEmpty()) {
+            return true;
+        }
+        else {
+            CrossSection cs = (CrossSection) result.get(0);
+            return section.getId().equals(cs.getId());
+        }
+    }
+
+
+    /**
+     *  Get a specific CrossSection from db.
+     *  @param id The dbid of the cross-section to load.
+     */
+    public static CrossSection getCrossSection(int id) {
+        Cache cache = CacheFactory.getCache(CACHE_NAME);
+        if (cache != null) {
+            Element element = cache.get(Integer.valueOf(id));
+            if (element != null) {
+                return (CrossSection) element.getValue();
+            }
+        }
+
+        CrossSection section = getCrossSectionUncached(id);
+        if (cache != null) {
+            Element element = new Element(Integer.valueOf(id), section);
+            cache.put(element);
+        }
+
+        return section;
+    }
+
+
+    /** Get specific CrossSection from database. */
+    protected static CrossSection getCrossSectionUncached(int id) {
+        Session session = SessionHolder.HOLDER.get();
+        Query query = session.createQuery(
+                "from CrossSection where id=:id");
+        query.setParameter("id", id);
+        List<CrossSection> list = query.list();
+        return list.isEmpty() ? null : list.get(0);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/CrossSectionWaterLineFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,101 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.apache.log4j.Logger;
+
+import java.util.List;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.DataProvider;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+
+import org.dive4elements.river.artifacts.WaterLineArtifact;
+
+import org.dive4elements.river.model.FastCrossSectionLine;
+
+import org.dive4elements.river.artifacts.geom.Lines;
+
+
+/**
+ * Facet for Waterlines in Cross Sections.
+ */
+public class CrossSectionWaterLineFacet
+extends      BlackboardDataFacet
+implements   FacetTypes {
+
+    /** Private logger to use. */
+    private static Logger logger =
+        Logger.getLogger(CrossSectionWaterLineFacet.class);
+
+
+    /** Trivial constructor, set (maybe localized) description. */
+    public CrossSectionWaterLineFacet(int idx, String description) {
+        super(idx, CROSS_SECTION_WATER_LINE, description);
+    }
+
+
+    /**
+     * Trivial constructor, set (maybe localized) description.
+     * @param idx Index of this facet.
+     * @param name 'type' of this facet.
+     * @param description (maybe) localized user-visible description.
+     */
+    public CrossSectionWaterLineFacet(int idx, String name, String description) {
+        super(idx, name, description);
+    }
+
+
+    /**
+     * Gets waterline (crossed with cross section) of waterlevel.
+     */
+    public Object getData(Artifact artifact, CallContext context) {
+        logger.debug("Get data for cross section water line");
+
+        List<DataProvider> providers = context.
+            getDataProvider(CrossSectionFacet.BLACKBOARD_CS_MASTER_DATA);
+        if (providers.size() < 1) {
+            logger.warn("Could not find Cross-Section data provider.");
+            return new Lines.LineData(new double[][] {}, 0d, 0d);
+        }
+
+        Object crossSection = providers.get(0)
+            .provideData(CrossSectionFacet.BLACKBOARD_CS_MASTER_DATA,
+                null, context);
+        Object nextKm = providers.get(0).
+            provideData(CrossSectionFacet.BLACKBOARD_CS_NEXT_KM, null, context);
+        Object prevKm = providers.get(0).
+            provideData(CrossSectionFacet.BLACKBOARD_CS_PREV_KM, null, context);
+        if (prevKm == null)
+            prevKm = new Double(-1d);
+        if (nextKm == null)
+            nextKm = new Double(-1d);
+
+        if (!(artifact instanceof WaterLineArtifact)) {
+            logger.error("CrossSectionWaterLineFacet needs WaterLineArtifact");
+            return new Lines.LineData(new double[][] {}, 0d,0d);
+        }
+        WaterLineArtifact lineArtifact = (WaterLineArtifact) artifact;
+
+        if (crossSection != null) {
+            return lineArtifact.getWaterLines(this.getIndex(),
+                (FastCrossSectionLine) crossSection, (Double) nextKm,
+                (Double) prevKm, context);
+        }
+        else {
+            return new Lines.LineData(new double[][] {}, 0d,0d);
+        }
+    }
+
+
+    /** Do a deep copy. */
+    @Override
+    public Facet deepCopy() {
+        CrossSectionWaterLineFacet copy = new CrossSectionWaterLineFacet(
+            this.getIndex(),
+            this.description);
+        copy.set(this);
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/DataFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,107 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+public class DataFacet
+extends      BlackboardDataFacet
+{
+    protected ComputeType type;
+    protected String      hash;
+    protected String      stateId;
+
+
+    /** Trivial constructor. */
+    public DataFacet() {
+    }
+
+    /**
+     * Defaults to ADVANCE Compute type.
+     * @param name Name of the facet.
+     * @param description maybe localized description of the facet.
+     */
+    public DataFacet(String name, String description) {
+        this(name, description, ComputeType.ADVANCE);
+    }
+
+
+    public DataFacet(String name, String description, ComputeType type) {
+        this(name, description, type, null);
+    }
+
+
+    public DataFacet(
+        String      name,
+        String      description,
+        ComputeType type,
+        String      hash
+    ) {
+        super(name, description);
+        this.type = type;
+        this.hash = hash;
+    }
+
+
+    public DataFacet(
+        String      name,
+        String      description,
+        ComputeType type,
+        String      hash,
+        String      stateId
+    ) {
+        super(name, description);
+        this.type    = type;
+        this.hash    = hash;
+        this.stateId = stateId;
+    }
+
+
+    public DataFacet(
+        int         index,
+        String      name,
+        String      description,
+        ComputeType type,
+        String      hash,
+        String      stateId
+    ) {
+        super(index, name, description);
+        this.type    = type;
+        this.hash    = hash;
+        this.stateId = stateId;
+    }
+
+
+    /**
+     * Return computation result.
+     */
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        FLYSArtifact flys = (FLYSArtifact)artifact;
+        String    theHash = (hash != null) ? hash : flys.hash();
+
+        return (stateId != null && stateId.length() > 0)
+            ? flys.compute(context, theHash, stateId, type, false)
+            : flys.compute(context, theHash, type, false);
+    }
+
+
+    /**
+     * Return a deep copy.
+     */
+    @Override
+    public Facet deepCopy() {
+        DataFacet copy = new DataFacet();
+        copy.set(this);
+        copy.type    = type;
+        copy.hash    = hash;
+        copy.stateId = stateId;
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/DateRange.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,51 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.io.Serializable;
+import java.util.Date;
+
+public class DateRange
+implements   Serializable
+{
+    private static final long serialVersionUID = -2553914795388094818L;
+
+    protected Date from;
+    protected Date to;
+
+    public DateRange(Date from, Date to) {
+        this.from = from;
+        this.to   = to;
+    }
+
+    public Date getFrom() {
+        return from;
+    }
+
+    public void setFrom(Date from) {
+        this.from = from;
+    }
+
+    public Date getTo() {
+        return to;
+    }
+
+    public void setTo(Date to) {
+        this.to = to;
+    }
+
+    private static final boolean equalDates(Date a, Date b) {
+        if (a == null && b != null) return false;
+        if (a != null && b == null) return false;
+        if (a == null) return true;
+        return a.equals(b);
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof DateRange)) {
+            return false;
+        }
+        DateRange o = (DateRange)other;
+        return equalDates(from, o.from) && equalDates(to, o.to);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/DifferenceCurveFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,68 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.WINFOArtifact;
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+
+/**
+ * Facet with the curve of a subtraction of two waterlevel-lines.
+ * TODO inherit directly from DataFacet? Check whether this Facet is obsolete.
+ */
+public class DifferenceCurveFacet extends WaterlevelFacet {
+
+    private static Logger logger = Logger.getLogger(DifferenceCurveFacet.class);
+
+
+    public DifferenceCurveFacet() {
+    }
+
+    public DifferenceCurveFacet(
+        int         index,
+        String      name,
+        String      description,
+        ComputeType type,
+        String      stateID,
+        String      hash
+
+    ) {
+        super(index, name, description, type, stateID, hash);
+    }
+
+    /**
+     * Get difference curve data.
+     * @return a WKms at given index.
+     */
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        WINFOArtifact winfo = (WINFOArtifact)artifact;
+
+        CalculationResult res = (CalculationResult)
+            winfo.compute(context, hash, stateId, type, false);
+
+        WKms [] wkms = (WKms [])res.getData();
+
+        WKms result = wkms[index];
+        logger.debug("Got difference curve data (" + result.getName()
+            + ") at index: " + index);
+
+        return result;
+    }
+
+
+    /** Copy deeply. */
+    @Override
+    public Facet deepCopy() {
+        WaterlevelFacet copy = new DifferenceCurveFacet();
+        copy.set(this);
+        copy.type    = type;
+        copy.stateId = stateId;
+        copy.hash    = hash;
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/DischargeTables.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,312 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Arrays;
+
+import java.io.Serializable;
+
+import org.hibernate.Session;
+import org.hibernate.Query;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.river.backend.SessionHolder;
+import org.dive4elements.river.model.Gauge;
+import org.dive4elements.river.model.DischargeTable;
+import org.dive4elements.river.model.DischargeTableValue;
+
+import gnu.trove.TDoubleArrayList;
+
+/** Documentation goes here. */
+public class DischargeTables
+implements   Serializable
+{
+    /** Private logger. */
+    private static Logger log = Logger.getLogger(DischargeTables.class);
+
+    /** Scale to convert discharge table values of master table into [cm]. */
+    public static final double MASTER_SCALE = 100d;
+
+    /** Scale to convert discharge table values of historical tables into [cm]. */
+    public static final double HISTORICAL_SCALE = 1d;
+
+    public static final int MASTER = 0;
+
+    protected List<String> gaugeNames;
+
+    protected String riverName;
+
+    protected double scale;
+
+    protected int    kind;
+
+    protected Map<String, double [][]> values;
+
+    public DischargeTables() {
+    }
+
+    public DischargeTables(String riverName, String gaugeName) {
+        this(riverName, gaugeName, MASTER);
+    }
+
+    public DischargeTables(String riverName, String gaugeName, int kind) {
+        this(riverName, new String [] { gaugeName }, kind);
+    }
+
+    public DischargeTables(String riverName, String [] gaugeNames) {
+        this(riverName, gaugeNames, MASTER);
+    }
+
+    public DischargeTables(String riverName, String [] gaugeNames, int kind) {
+        this(riverName, Arrays.asList(gaugeNames), kind);
+    }
+
+    public DischargeTables(
+        String       riverName,
+        List<String> gaugeNames,
+        int          kind
+    ) {
+        scale           = Double.NaN;
+        this.kind       = kind;
+        this.riverName  = riverName;
+        this.gaugeNames = gaugeNames;
+    }
+
+    public double [][] getFirstTable() {
+        return getFirstTable(MASTER_SCALE);
+    }
+
+    public double [][] getFirstTable(double scale) {
+        Map<String, double [][]> values = getValues(scale);
+        for (double [][] table: values.values()) {
+            return table;
+        }
+        return null;
+    }
+
+    public Map<String, double [][]> getValues() {
+        return getValues(MASTER_SCALE);
+    }
+
+    public Map<String, double [][]> getValues(double scale) {
+        if (values == null || scale != this.scale) {
+            values = loadValues(scale);
+            this.scale = scale;
+        }
+        return values;
+    }
+
+    /**
+     * Returns mapping of gauge name to values.
+     */
+    protected Map<String, double [][]> loadValues(double scale) {
+        Map<String, double [][]> values = new HashMap<String, double [][]>();
+
+        Session session = SessionHolder.HOLDER.get();
+
+        Query gaugeQuery = session.createQuery(
+            "from Gauge where name=:gauge and river.name=:river");
+        gaugeQuery.setParameter("river", riverName);
+
+        for (String gaugeName: gaugeNames) {
+            gaugeQuery.setParameter("gauge", gaugeName);
+            List<Gauge> gauges = gaugeQuery.list();
+            if (gauges.isEmpty()) {
+                log.warn(
+                    "no gauge '"+gaugeName+"' at river '"+riverName+"'");
+                continue;
+            }
+            Gauge gauge = gauges.get(0);
+
+            List<DischargeTable> tables = gauge.getDischargeTables();
+
+            if (tables.isEmpty()) {
+                log.warn(
+                    "no discharge table for gauge '" + gaugeName + "'");
+                continue;
+            }
+
+            // TODO: Filter by time interval
+            DischargeTable table = null;
+            for (DischargeTable dt : tables) {
+                if (dt.getKind() == 0) {
+                    table = dt;
+                    break;
+                }
+            }
+            if (table == null) {
+                table = tables.get(0);
+            }
+            double [][] vs = loadDischargeTableValues(table, scale);
+
+            values.put(gaugeName, vs);
+        }
+
+        return values;
+    }
+
+
+    /**
+     * @param table The discharge table
+     * @param scale The scale factor to adjust W and Q values.
+     *
+     * @return the values of a discharge table.
+     */
+    public static double[][] loadDischargeTableValues(
+        DischargeTable table,
+        double         scale
+    ) {
+        List<DischargeTableValue> dtvs = table.getDischargeTableValues();
+
+        final double [][] vs = new double[2][dtvs.size()];
+
+        int idx = 0;
+        for (DischargeTableValue dtv: dtvs) {
+            double q = dtv.getQ().doubleValue();
+            vs[0][idx] = q * scale;
+            vs[1][idx] = dtv.getW().doubleValue() * scale;
+            ++idx;
+        }
+
+        return vs;
+    }
+
+    private static final double EPSILON = 1e-5;
+
+    private static final boolean epsEquals(double a, double b) {
+        return Math.abs(a - b) < EPSILON;
+    }
+
+    private static final boolean between(double a, double b, double x) {
+        if (a > b) { double t = a; a = b; b = t; }
+        return x > a && x < b;
+    }
+
+    /**
+     * Find or interpolate Qs from q/w array.
+     * @param values [[q0,q1,q2],[w0,w1,w2]]
+     * @param w      W value to look for in values.
+     */
+    public static double [] getQsForW(double [][] values, double w) {
+
+        boolean debug = log.isDebugEnabled();
+
+        if (debug) {
+            log.debug("getQsForW: W = " + w);
+        }
+
+        double [] qs = values[0];
+        double [] ws = values[1];
+
+        int N = Math.min(qs.length, ws.length);
+
+        if (N == 0) {
+            if (debug) {
+                log.debug("Q(" + w + ") = []");
+            }
+            return new double [0];
+        }
+
+        TDoubleArrayList outQs = new TDoubleArrayList();
+
+        if (epsEquals(ws[0], w)) {
+            outQs.add(qs[0]);
+        }
+
+        for (int i = 1; i < N; ++i) {
+            if (epsEquals(ws[i], w)) {
+                outQs.add(qs[i]);
+            }
+            else if (between(ws[i-1], ws[i], w)) {
+                double w1 = ws[i-1];
+                double w2 = ws[i];
+                double q1 = qs[i-1];
+                double q2 = qs[i];
+
+                // q1 = m*w1 + b
+                // q2 = m*w2 + b
+                // q2 - q1 = m*(w2 - w1)
+                // m = (q2 - q1)/(w2 - w1) # w2 != w1
+                // b = q1 - m*w1
+                // w1 != w2
+
+                double m = (q2 - q1)/(w2 - w1);
+                double b = q1 - m*w1;
+                double q = w*m + b;
+
+                outQs.add(q);
+            }
+        }
+
+        double [] result = outQs.toNativeArray();
+
+        if (debug) {
+            log.debug("Q(" + w + ") = " + Arrays.toString(result));
+        }
+
+        return result;
+    }
+
+    public static double [] getWsForQ(double [][] values, double q) {
+
+        boolean debug = log.isDebugEnabled();
+
+        if (debug) {
+            log.debug("getWsForQ: W = " + q);
+        }
+
+        double [] qs = values[0];
+        double [] ws = values[1];
+
+        int N = Math.min(qs.length, ws.length);
+
+        if (N == 0) {
+            if (debug) {
+                log.debug("W(" + q + ") = []");
+            }
+            return new double [0];
+        }
+
+        TDoubleArrayList outWs = new TDoubleArrayList();
+
+        if (epsEquals(qs[0], q)) {
+            outWs.add(ws[0]);
+        }
+
+        for (int i = 1; i < N; ++i) {
+            if (epsEquals(qs[i], q)) {
+                outWs.add(ws[i]);
+            }
+            else if (between(qs[i-1], qs[i], q)) {
+                double w1 = ws[i-1];
+                double w2 = ws[i];
+                double q1 = qs[i-1];
+                double q2 = qs[i];
+
+                // w1 = m*q1 + b
+                // w2 = m*q2 + b
+                // w2 - w1 = m*(q2 - q1)
+                // m = (w2 - w1)/(q2 - q1) # q2 != q1
+                // b = w1 - m*q1
+                // q1 != q2
+
+                double m = (w2 - w1)/(q2 - q1);
+                double b = w1 - m*q1;
+                double w = q*m + b;
+
+                outWs.add(w);
+            }
+        }
+
+        double [] result = outWs.toNativeArray();
+
+        if (debug) {
+            log.debug("W(" + q + ") = " + Arrays.toString(result));
+        }
+
+        return result;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/DurationCurveFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,98 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.util.List;
+import java.util.ArrayList;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.artifactdatabase.state.DefaultFacet;
+import org.dive4elements.artifactdatabase.state.Facet;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.WINFOArtifact;
+
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+
+/**
+ * Data of a duration curve.
+ */
+public class DurationCurveFacet extends DefaultFacet {
+
+    private static Logger logger = Logger.getLogger(DurationCurveFacet.class);
+
+    /** Blackboard data provider key for durationcurve (wqday) data. */
+    public static String BB_DURATIONCURVE = "durationcurve";
+
+    /** Blackboard data provider key for km of durationcurve. */
+    public static String BB_DURATIONCURVE_KM = "durationcurve.km";
+
+    public DurationCurveFacet() {
+    }
+
+    public DurationCurveFacet(String name, String description) {
+        super(0, name, description);
+    }
+
+
+    /**
+     * Expose state computation from WINFOArtifact.
+     */
+    public Object getData(Artifact artifact, CallContext context) {
+        logger.debug("Get data for duration curve data");
+
+        WINFOArtifact winfo = (WINFOArtifact)artifact;
+
+        CalculationResult cr = (CalculationResult)winfo.compute(
+            context, ComputeType.ADVANCE, false);
+
+        return cr.getData();
+    }
+
+
+    @Override
+    public List getStaticDataProviderKeys(Artifact art) {
+        List list = new ArrayList();
+        list.add(BB_DURATIONCURVE);
+        list.add(BB_DURATIONCURVE_KM);
+        return list;
+    }
+
+
+    /**
+     * Can provide whatever getData returns and additionally the location.
+     * @param key      will respond on BB_DURATIONCURVE +KM
+     * @param param    ignored
+     * @param context  ignored
+     * @return whatever getData delivers or location.
+     */
+    @Override
+    public Object provideBlackboardData(Artifact artifact,
+        Object key,
+        Object param,
+        CallContext context
+    ) {
+        if (key.equals(BB_DURATIONCURVE)) {
+            return getData(artifact, context);
+        }
+        else if (key.equals(BB_DURATIONCURVE_KM)) {
+            return ((FLYSArtifact)artifact).getDataAsString("ld_locations");
+        }
+        else {
+            return null;
+        }
+    }
+
+
+    /** Create a deep copy. */
+    @Override
+    public Facet deepCopy() {
+        DurationCurveFacet copy = new DurationCurveFacet();
+        copy.set(this);
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/EmptyFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,33 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.artifactdatabase.state.DefaultFacet;
+import org.dive4elements.artifactdatabase.state.Facet;
+
+public class EmptyFacet
+extends      DefaultFacet
+{
+    /** Trivial constructor. */
+    public EmptyFacet() {
+        super(0, "empty.facet", "empty.facet");
+    }
+
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        return null;
+    }
+
+
+    /**
+     * Return a deep copy.
+     */
+    @Override
+    public Facet deepCopy() {
+        EmptyFacet copy = new EmptyFacet();
+        copy.set(this);
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/FacetTypes.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,380 @@
+package org.dive4elements.river.artifacts.model;
+
+/** 'Types' of facets. */
+public interface FacetTypes {
+
+    /** Static helper class to determine if a Facet name is of a generalized
+     * 'type'. */
+    public static class IS {
+        public static boolean WQ_KM(String type) {
+           return type.equals(DISCHARGE_LONGITUDINAL_W)
+               || type.equals(LONGITUDINAL_W);
+        }
+        public static boolean W_KM(String type) {
+            return type.equals(STATIC_WKMS)
+               || type.equals(HEIGHTMARKS_POINTS)
+               || WQ_KM(type);
+        }
+        public static boolean AREA(String type) {
+            return type.equals(AREA)
+                || type.equals(CROSS_SECTION_AREA)
+                || type.equals(LONGITUDINAL_SECTION_AREA);
+        }
+        public static boolean W(String type) {
+            return type.equals(LONGITUDINAL_W)
+                || type.equals(DISCHARGE_LONGITUDINAL_W)
+                || type.equals(DURATION_W)
+                || type.equals(STATIC_WKMS)
+                || type.equals(STATIC_WQKMS_W);
+        }
+        public static boolean Q(String type) {
+            return type.equals(LONGITUDINAL_Q)
+                || type.equals(DISCHARGE_LONGITUDINAL_Q)
+                || type.equals(DURATION_Q)
+                || type.equals(STATIC_WQKMS_Q);
+        }
+        public static boolean V(String type) {
+            return type.equals(FLOW_VELOCITY_MAINCHANNEL)
+                || type.equals(FLOW_VELOCITY_TOTALCHANNEL);
+        }
+        public static boolean T(String type) {
+            return type.equals(FLOW_VELOCITY_TAU);
+        }
+        public static boolean H(String type) {
+            return type.equals(MIDDLE_BED_HEIGHT_SINGLE)
+                || type.equals(MIDDLE_BED_HEIGHT_EPOCH);
+        }
+        public static boolean MANUALPOINTS(String type) {
+            return type.endsWith("manualpoints");
+        }
+        public static boolean MANUALLINE(String type) {
+            return type.endsWith("manualline");
+        }
+        public static boolean SQ_CURVE(String type) {
+            if (type.equals(SQ_A_CURVE)
+                || type.equals(SQ_B_CURVE)
+                || type.equals(SQ_C_CURVE)
+                || type.equals(SQ_D_CURVE)
+                || type.equals(SQ_E_CURVE)
+                || type.equals(SQ_F_CURVE)
+                || type.equals(SQ_A_OUTLIER_CURVE)
+                || type.equals(SQ_B_OUTLIER_CURVE)
+                || type.equals(SQ_C_OUTLIER_CURVE)
+                || type.equals(SQ_D_OUTLIER_CURVE)
+                || type.equals(SQ_E_OUTLIER_CURVE)
+                || type.equals(SQ_F_OUTLIER_CURVE)
+                )
+            {
+                return true;
+            }
+
+            return false;
+        }
+        public static boolean SQ_MEASUREMENT(String type) {
+            if (type.equals(SQ_A_MEASUREMENT)
+                || type.equals(SQ_B_MEASUREMENT)
+                || type.equals(SQ_C_MEASUREMENT)
+                || type.equals(SQ_D_MEASUREMENT)
+                || type.equals(SQ_E_MEASUREMENT)
+                || type.equals(SQ_F_MEASUREMENT)
+                || type.equals(SQ_A_OUTLIER_MEASUREMENT)
+                || type.equals(SQ_B_OUTLIER_MEASUREMENT)
+                || type.equals(SQ_C_OUTLIER_MEASUREMENT)
+                || type.equals(SQ_D_OUTLIER_MEASUREMENT)
+                || type.equals(SQ_E_OUTLIER_MEASUREMENT)
+                || type.equals(SQ_F_OUTLIER_MEASUREMENT)
+                )
+            {
+                return true;
+            }
+
+            return false;
+        }
+        public static boolean SQ_OUTLIER(String type) {
+            if (type.equals(SQ_A_OUTLIER)
+                || type.equals(SQ_B_OUTLIER)
+                || type.equals(SQ_C_OUTLIER)
+                || type.equals(SQ_D_OUTLIER)
+                || type.equals(SQ_E_OUTLIER)
+                || type.equals(SQ_F_OUTLIER))
+            {
+                return true;
+            }
+
+            return false;
+        }
+        public static boolean SEDIMENT_LOAD(String type) {
+            return type.startsWith("sedimentload");
+        }
+        public static boolean SEDIMENT_LOAD_NO_FLOAT(String type) {
+            return type.startsWith("sedimentload") && !type.contains("susp");
+        }
+    };
+
+    /** Available diagram/chart-types.  This enum is evaluated at certain
+     * places to iterate over chart-types to e.g. dynamically generate
+     * Facet-names (ManualPoints for example). */
+    public enum ChartType {
+        FD("fix_derivate_curve"),
+        LS("longitudinal_section"),
+        CS("cross_section"),
+        DLS("discharge_longitudinal_section"),
+        CDC("computed_discharge_curve"),
+        DUC("duration_curve"),
+        DIC("discharge_curve"),
+        RC("reference_curve"),
+        RCN("reference_curve_normalized"),
+        WD("wdifferences"),
+        FWQC("fix_wq_curve"),
+        FDWC("fix_deltawt_curve"),
+        FLSC("fix_longitudinal_section_curve"),
+        FDC("fix_derivate_curve"),
+        EWQ("extreme_wq_curve"),
+        BHM("bedheight_middle"),
+        BLS("bed_longitudinal_section"),
+        SLS("sedimentload_ls"),
+        FV("flow_velocity"),
+        SQA("sq_relation_a"),
+        SQB("sq_relation_b"),
+        W_D("w_differences"),
+        SQC("sq_relation_c"),
+        SQD("sq_relation_d"),
+        SQE("sq_relation_e"),
+        SQF("sq_relation_f"),
+        HD("historical_discharge"),
+        HDWQ("historical_discharge_wq");
+
+        private String chartTypeString;
+
+        ChartType(String description) {
+            this.chartTypeString = description;
+        }
+
+        @Override
+        public String toString() {
+            return chartTypeString;
+        }
+    }
+
+    String AREA                        = "area";
+    String CROSS_SECTION_AREA          = "cross_section.area";
+    String LONGITUDINAL_SECTION_AREA   = "longitudinal_section.area";
+
+    String FLOODMAP_WSPLGEN            = "floodmap.wsplgen";
+    String FLOODMAP_BARRIERS           = "floodmap.barriers";
+    String FLOODMAP_USERSHAPE          = "floodmap.usershape";
+    String FLOODMAP_RIVERAXIS          = "floodmap.riveraxis";
+    @Deprecated
+    String FLOODMAP_WMSBACKGROUND      = "floodmap.wmsbackground";
+    String FLOODMAP_KMS                = "floodmap.kms";
+    String FLOODMAP_QPS                = "floodmap.qps";
+    String FLOODMAP_HWS_LINES          = "floodmap.hws_lines";
+    String FLOODMAP_HWS_POINTS         = "floodmap.hws_points";
+    String FLOODMAP_HYDR_BOUNDARY      = "floodmap.hydr_boundaries";
+    String FLOODMAP_HYDR_BOUNDARY_POLY = "floodmap.hydr_boundaries_poly";
+    String FLOODMAP_CATCHMENT          = "floodmap.catchment";
+    String FLOODMAP_FLOODPLAIN         = "floodmap.floodplain";
+    String FLOODMAP_LINES              = "floodmap.lines";
+    String FLOODMAP_BUILDINGS          = "floodmap.buildings";
+    String FLOODMAP_FIXPOINTS          = "floodmap.fixpoints";
+    String FLOODMAP_FLOODMARKS         = "floodmap.floodmarks";
+    String FLOODMAP_FLOODMAPS          = "floodmap.floodmaps";
+    String FLOODMAP_GAUGE_LOCATION     = "floodmap.gauge_location";
+    String FLOODMAP_EXTERNAL_WMS       = "floodmap.externalwms";
+    String FLOODMAP_JETTIES            = "floodmap.jetties";
+
+    String DISCHARGE_LONGITUDINAL_W = "discharge_longitudinal_section.w";
+    String DISCHARGE_LONGITUDINAL_Q = "discharge_longitudinal_section.q";
+    String DISCHARGE_LONGITUDINAL_Q_INFOLD = "discharge_longitudinal_section.q.infolding";
+    String DISCHARGE_LONGITUDINAL_Q_INFOLD_CUT = "discharge_longitudinal_section.q.cutting";
+    String DISCHARGE_LONGITUDINAL_C = "discharge_longitudinal_section.c";
+
+    String LONGITUDINAL_W = "longitudinal_section.w";
+    String LONGITUDINAL_Q = "longitudinal_section.q";
+    String LONGITUDINAL_ANNOTATION   = "longitudinal_section.annotations";
+    String LONGITUDINAL_MANUALPOINTS = "longitudinal_section.manualpoints";
+
+    String W_DIFFERENCES = "w_differences";
+
+    String COMPUTED_DISCHARGE_Q = "computed_discharge_curve.q";
+    String COMPUTED_DISCHARGE_MAINVALUES_Q = "computed_discharge_curve.mainvalues.q";
+    String COMPUTED_DISCHARGE_MAINVALUES_W = "computed_discharge_curve.mainvalues.w";
+
+    String MAINVALUES_Q = "mainvalues.q";
+    String MAINVALUES_W = "mainvalues.w";
+
+    String CROSS_SECTION = "cross_section";
+    String CROSS_SECTION_WATER_LINE = "cross_section_water_line";
+
+    String HYK = "hyk";
+
+    String DISCHARGE_CURVE = "discharge_curve.curve";
+    String GAUGE_DISCHARGE_CURVE = "gauge_discharge_curve";
+    String GAUGE_DISCHARGE_CURVE_AT_EXPORT = "gauge_discharge_curve_at_export";
+
+    String DURATION_W = "duration_curve.w";
+    String DURATION_Q = "duration_curve.q";
+    String DURATION_MAINVALUES_Q = "duration_curve.mainvalues.q";
+
+    String MANUALPOINTS = "manualpoints";
+    String MANUALLINE = "manualline";
+
+    String QSECTOR        = "qsectors";
+
+    String STATIC_WQ      = "other.wq";
+    String STATIC_WQ_ANNOTATIONS = "other.wq.annotations";
+    String STATIC_WKMS    = "other.wkms";
+    String STATIC_WKMS_MARKS = "other.wkms.marks";
+    String STATIC_WQKMS   = "other.wqkms";
+    String STATIC_WQKMS_W = "other.wqkms.w";
+    String STATIC_WQKMS_Q = "other.wqkms.q";
+    String STATIC_WKMS_INTERPOL = "other.wkms.interpol";
+
+    String HEIGHTMARKS_POINTS = "heightmarks_points";
+
+    String CSV = "csv";
+    String WST = "wst";
+    String AT  = "at";
+    String PDF = "pdf";
+
+    String REPORT = "report";
+
+    String HISTORICAL_DISCHARGE_Q      = "historical_discharge.historicalq";
+    String HISTORICAL_DISCHARGE_Q_DIFF = "historical_discharge.historicalq.diff";
+    String HISTORICAL_DISCHARGE_W      = "historical_discharge.historicalw";
+    String HISTORICAL_DISCHARGE_W_DIFF = "historical_discharge.historicalw.diff";
+    String HISTORICAL_DISCHARGE_WQ_Q   = "historical_discharge.wq.q";
+    String HISTORICAL_DISCHARGE_WQ_W   = "historical_discharge.wq.w";
+    String HISTORICAL_DISCHARGE_WQ_CURVE = "historical_discharge.wq.curve";
+    String HISTORICAL_DISCHARGE_MAINVALUES_Q = "historical_discharge.mainvalues.q";
+    String HISTORICAL_DISCHARGE_MAINVALUES_W = "historical_discharge.mainvalues.w";
+
+    String REFERENCE_CURVE = "reference_curve";
+    String REFERENCE_CURVE_NORMALIZED = "reference_curve_normalized";
+
+    String FLOW_VELOCITY_MAINCHANNEL           = "flow_velocity.mainchannel";
+    String FLOW_VELOCITY_TOTALCHANNEL          = "flow_velocity.totalchannel";
+    String FLOW_VELOCITY_TAU                   = "flow_velocity.tau";
+    String FLOW_VELOCITY_MAINCHANNEL_FILTERED  = "flow_velocity.mainchannel.filtered";
+    String FLOW_VELOCITY_TOTALCHANNEL_FILTERED = "flow_velocity.totalchannel.filtered";
+    String FLOW_VELOCITY_TAU_FILTERED          = "flow_velocity.tau.filtered";
+    String FLOW_VELOCITY_ANNOTATION            = "flow_velocity.annotation";
+    String FLOW_VELOCITY_MEASUREMENT           = "flow_velocity.measurement";
+    String FLOW_VELOCITY_DISCHARGE             = "flow_velocity.discharge";
+
+    String MIDDLE_BED_HEIGHT_SINGLE     = "bedheight_middle.single";
+    String MIDDLE_BED_HEIGHT_EPOCH      = "bedheight_middle.epoch";
+    String MIDDLE_BED_HEIGHT_ANNOTATION = "bedheight_middle.annotation";
+
+    String BED_QUALITY_POROSITY_TOPLAYER         = "bed_longitudinal_section.porosity_toplayer";
+    String BED_QUALITY_POROSITY_SUBLAYER         = "bed_longitudinal_section.porosity_sublayer";
+    String BED_QUALITY_BED_DIAMETER_TOPLAYER     = "bed_longitudinal_section.bed_diameter_toplayer";
+    String BED_QUALITY_BED_DIAMETER_SUBLAYER     = "bed_longitudinal_section.bed_diameter_sublayer";
+    String BED_QUALITY_SEDIMENT_DENSITY_TOPLAYER = "bed_longitudinal_section.sediment_density_toplayer";
+    String BED_QUALITY_SEDIMENT_DENSITY_SUBLAYER = "bed_longitudinal_section.sediment_density_sublayer";
+    String BED_QUALITY_BEDLOAD_DIAMETER          = "bed_longitudinal_section.bedload_diameter";
+
+    String BED_DIFFERENCE_YEAR          = "bedheight_difference.year";
+    String BED_DIFFERENCE_YEAR_FILTERED = "bedheight_difference.year.filtered";
+    String BED_DIFFERENCE_HEIGHT_YEAR   = "bedheight_difference.height_year";
+    String BED_DIFFERENCE_HEIGHT_YEAR_FILTERED   = "bedheight_difference.height_year.filtered";
+    String BED_DIFFERENCE_EPOCH         = "bedheight_difference.epoch";
+    String BED_DIFFERENCE_EPOCH_FILTERED = "bedheight_difference.epoch.filtered";
+    String BED_DIFFERENCE_MORPH_WIDTH   = "bedheight_difference.morph_width";
+    String BED_DIFFERENCE_YEAR_HEIGHT1  = "bedheight_difference.year.height1";
+    String BED_DIFFERENCE_YEAR_HEIGHT2  = "bedheight_difference.year.height2";
+    String BED_DIFFERENCE_YEAR_HEIGHT1_FILTERED = "bedheight_difference.year.height1.filtered";
+    String BED_DIFFERENCE_YEAR_HEIGHT2_FILTERED = "bedheight_difference.year.height2.filtered";
+    String BED_DIFFERENCE_EPOCH_HEIGHT1 = "bedheight_difference.epoch.height1";
+    String BED_DIFFERENCE_EPOCH_HEIGHT2 = "bedheight_difference.epoch.height2";
+    String BED_DIFFERENCE_EPOCH_HEIGHT1_FILTERED = "bedheight_difference.epoch.height1.filtered";
+    String BED_DIFFERENCE_EPOCH_HEIGHT2_FILTERED = "bedheight_difference.epoch.height2.filtered";
+    String MORPHOLOGIC_WIDTH = "morph-width";
+
+    String SEDIMENT_LOAD_COARSE        = "sedimentload.coarse";
+    String SEDIMENT_LOAD_SAND          = "sedimentload.sand";
+    String SEDIMENT_LOAD_FINEMIDDLE    = "sedimentload.finemiddle";
+    String SEDIMENT_LOAD_SUSP_SAND     = "sedimentload.susp_sand";
+    String SEDIMENT_LOAD_SUSP_SAND_BED = "sedimentload.susp_sand_bed";
+    String SEDIMENT_LOAD_SUSP_SEDIMENT = "sedimentload.susp_sediment";
+    String SEDIMENT_LOAD_TOTAL         = "sedimentload.total";
+    String SEDIMENT_LOAD_TOTAL_LOAD    = "sedimentload.total_load";
+
+    String SQ_OVERVIEW       = "sq_overview";
+
+    String SQ_A_CURVE       = "sq_a_curve";
+    String SQ_A_MEASUREMENT = "sq_a_measurement";
+    String SQ_A_OUTLIER     = "sq_a_outlier";
+    String SQ_A_OUTLIER_CURVE = "sq_a_outlier_curve";
+    String SQ_A_OUTLIER_MEASUREMENT = "sq_a_outlier_measurement";
+
+    String SQ_B_CURVE       = "sq_b_curve";
+    String SQ_B_MEASUREMENT = "sq_b_measurement";
+    String SQ_B_OUTLIER     = "sq_b_outlier";
+    String SQ_B_OUTLIER_CURVE  = "sq_b_outlier_curve";
+    String SQ_B_OUTLIER_MEASUREMENT  = "sq_b_outlier_measurement";
+
+    String SQ_C_CURVE       = "sq_c_curve";
+    String SQ_C_MEASUREMENT = "sq_c_measurement";
+    String SQ_C_OUTLIER     = "sq_c_outlier";
+    String SQ_C_OUTLIER_CURVE = "sq_c_outlier_curve";
+    String SQ_C_OUTLIER_MEASUREMENT = "sq_c_outlier_measurement";
+
+    String SQ_D_CURVE       = "sq_d_curve";
+    String SQ_D_MEASUREMENT = "sq_d_measurement";
+    String SQ_D_OUTLIER     = "sq_d_outlier";
+    String SQ_D_OUTLIER_CURVE = "sq_d_outlier_curve";
+    String SQ_D_OUTLIER_MEASUREMENT = "sq_d_outlier_measurement";
+
+    String SQ_E_CURVE       = "sq_e_curve";
+    String SQ_E_MEASUREMENT = "sq_e_measurement";
+    String SQ_E_OUTLIER     = "sq_e_outlier";
+    String SQ_E_OUTLIER_CURVE = "sq_e_outlier_curve";
+    String SQ_E_OUTLIER_MEASUREMENT = "sq_e_outlier_curve_measurement";
+
+    String SQ_F_CURVE       = "sq_f_curve";
+    String SQ_F_MEASUREMENT = "sq_f_measurement";
+    String SQ_F_OUTLIER     = "sq_f_outlier";
+    String SQ_F_OUTLIER_CURVE = "sq_f_outlier_curve";
+    String SQ_F_OUTLIER_MEASUREMENT = "sq_f_outlier_measurement";
+
+    String RELATIVE_POINT = "relativepoint";
+
+    String FIX_ANALYSIS_EVENTS_DWT = "fix_analysis_events_dwt";
+    String FIX_ANALYSIS_EVENTS_LS = "fix_analysis_events_ls";
+    String FIX_ANALYSIS_EVENTS_WQ = "fix_analysis_events_wq";
+
+    String FIX_EVENTS = "fix_events_wqkms";
+
+    String FIX_REFERENCE_EVENTS_DWT = "fix_reference_events_dwt";
+    String FIX_REFERENCE_EVENTS_LS = "fix_reference_events_ls";
+    String FIX_REFERENCE_EVENTS_WQ = "fix_reference_events_wq";
+    String FIX_REFERENCE_PERIOD_DWT = "fix_reference_period_dwt";
+
+    // Note that AVERAGE_DWT will get a postfix (e.g. ..._dwt_1)
+    String FIX_SECTOR_AVERAGE_DWT = "fix_sector_average_dwt";
+    String FIX_SECTOR_AVERAGE_LS = "fix_sector_average_ls";
+    String FIX_SECTOR_AVERAGE_WQ = "fix_sector_average_wq";
+    String FIX_SECTOR_AVERAGE_LS_DEVIATION = "fix_sector_average_ls_deviation";
+
+    String FIX_WQ_CURVE = "fix_wq_curve";
+    String FIX_OUTLIER = "fix_outlier";
+
+    String FIX_ANALYSIS_PERIODS_DWT = "fix_analysis_periods_dwt";
+    String FIX_ANALYSIS_PERIODS_LS = "fix_analysis_periods_ls";
+    String FIX_ANALYSIS_PERIODS_WQ = "fix_analysis_periods_wq";
+
+    String FIX_DERIVATE_CURVE = "fix_derivate_curve";
+
+    String FIX_DEVIATION_DWT = "fix_deviation_dwt";
+    String FIX_DEVIATION_LS = "fix_deviation_ls";
+
+    String FIX_PARAMETERS = "fix_parameters";
+
+    String STATIC_BEDHEIGHT = "static_bedheight";
+
+    String EXTREME_WQ_CURVE = "extreme_wq_curve";
+
+    String EXTREME_WQ_CURVE_BASE = "extreme_wq_curve_base";
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/FastCrossSectionChunk.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,83 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.util.List;
+import java.util.Collections;
+
+import java.io.Serializable;
+
+import org.dive4elements.river.model.CrossSection;
+
+import org.dive4elements.river.model.FastCrossSectionLine;
+
+import org.apache.log4j.Logger;
+
+public class FastCrossSectionChunk
+implements   Serializable
+{
+    private static Logger log = Logger.getLogger(FastCrossSectionChunk.class);
+
+    public static final String PREFIX = "FCSC:";
+    public static final double KM_RANGE = 1.0;
+
+    protected double startKm;
+    protected int    crossSectionId;
+
+    protected List<FastCrossSectionLine> crossSectionLines;
+
+    public FastCrossSectionChunk() {
+    }
+
+    public FastCrossSectionChunk(CrossSection cs, double km) {
+
+        crossSectionId = cs.getId();
+        startKm = Math.floor(km);
+        double stopKm = startKm + KM_RANGE;
+
+        long startTime = System.currentTimeMillis();
+
+        crossSectionLines = cs.getFastLines(startKm, stopKm);
+
+        long stopTime = System.currentTimeMillis();
+
+        if (log.isDebugEnabled()) {
+            log.debug("Fetching cross section lines took " +
+                (float)(stopTime-startTime)/1000f + " secs.");
+        }
+    }
+
+    public FastCrossSectionLine getCrossSectionLine(double km) {
+        FastCrossSectionLine key = new FastCrossSectionLine(km);
+        int pos = Collections.binarySearch(
+            crossSectionLines, key, FastCrossSectionLine.KM_CMP);
+        return pos < 0 ? null : crossSectionLines.get(pos);
+    }
+
+    public static String createHashKey(CrossSection cs, double km) {
+        return PREFIX + cs.getId() + ":" + (int)Math.floor(km);
+    }
+
+    public String getHashKey() {
+        return PREFIX + crossSectionId + ":" + (int)Math.floor(startKm);
+    }
+
+    public double getStartKm() {
+        return startKm;
+    }
+
+    public void setStartKm(double startKm) {
+        this.startKm = startKm;
+    }
+
+    public double getStopKm() {
+        return startKm + KM_RANGE;
+    }
+
+    public int getCrossSectionId() {
+        return crossSectionId;
+    }
+
+    public void setCrossSectionId(int crossSectionId) {
+        this.crossSectionId = crossSectionId;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/FastCrossSectionLineFactory.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,69 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.dive4elements.river.artifacts.cache.CacheFactory;
+
+import org.dive4elements.river.model.CrossSection;
+
+import org.dive4elements.river.model.FastCrossSectionLine;
+
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.Element;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+public class FastCrossSectionLineFactory
+{
+    private static Logger log =
+        Logger.getLogger(FastCrossSectionLineFactory.class);
+
+    public static final String CACHE_NAME = "fast-cross-section-lines";
+
+    private FastCrossSectionLineFactory() {
+    }
+
+    public static FastCrossSectionLine getCrossSectionLine(
+        CrossSection cs,
+        double       km
+    ) {
+        Cache cache = CacheFactory.getCache(CACHE_NAME);
+
+        boolean debug = log.isDebugEnabled();
+
+        if (cache == null) {
+            if (debug) {
+                log.debug("No cross section chunk cache configured.");
+            }
+            List<FastCrossSectionLine> lines = cs.getFastLines(km, km);
+            return lines.isEmpty() ? null : lines.get(0);
+        }
+
+        String cacheKey = FastCrossSectionChunk.createHashKey(cs, km);
+
+        Element element = cache.get(cacheKey);
+
+        FastCrossSectionChunk fcsc;
+
+        if (element != null) {
+            if (debug) {
+                log.debug("Found cross section chunk in cache id: " +
+                    cs.getId() + " km: " + km);
+            }
+
+            fcsc = (FastCrossSectionChunk)element.getValue();
+        }
+        else {
+            if (debug) {
+                log.debug("Not found cross section chunk in cache id: " +
+                    cs.getId() + " km: " + km + " -> loading");
+            }
+            fcsc = new FastCrossSectionChunk(cs, km);
+            element = new Element(cacheKey, fcsc);
+            cache.put(element);
+        }
+
+        return fcsc.getCrossSectionLine(km);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/FixingsColumn.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,62 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.dive4elements.river.artifacts.math.Linear;
+
+import java.util.Arrays;
+
+import java.io.Serializable;
+
+public class FixingsColumn
+implements   Serializable
+{
+    protected double [] kms;
+    protected double [] ws;
+
+    protected QRangeTree qs;
+
+    public FixingsColumn() {
+    }
+
+    public FixingsColumn(
+        double []  kms,
+        double []  ws,
+        QRangeTree qs
+    ) {
+        this.kms = kms;
+        this.ws  = ws;
+        this.qs  = qs;
+    }
+
+    public boolean getW(double km, double [] w) {
+        return getW(km, w, 0);
+    }
+
+    public boolean getW(double km, double [] w, int index) {
+
+        if (kms.length == 0 || km < kms[0] || km > kms[kms.length-1]) {
+            w[index] = Double.NaN;
+            return true;
+        }
+
+        int idx = Arrays.binarySearch(kms, km);
+
+        if (idx >= 0) {
+            w[index] = ws[idx];
+            return true;
+        }
+
+        idx = -idx - 1;
+
+        w[index] = Linear.linear(km, kms[idx-1], kms[idx], ws[idx-1], ws[idx]);
+        return false;
+    }
+
+    public double getQ(double km) {
+        return qs.findQ(km);
+    }
+
+    public QRangeTree getQRanges() {
+        return qs;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/FixingsColumnFactory.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,131 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.dive4elements.river.artifacts.model.FixingsOverview.Fixing;
+
+import org.dive4elements.river.artifacts.cache.CacheFactory;
+
+import org.dive4elements.river.backend.SessionHolder;
+
+import java.util.List;
+
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.Element;
+
+import org.hibernate.Session;
+import org.hibernate.SQLQuery;
+
+import org.hibernate.type.StandardBasicTypes;
+
+import org.apache.log4j.Logger;
+
+public class FixingsColumnFactory
+{
+    private static Logger log = Logger.getLogger(FixingsColumnFactory.class);
+
+    public static final String CACHE_NAME = "fixings-columns";
+
+    public static final String SQL_COLUMN_WS =
+        "SELECT wcv.position AS km, wcv.w AS w " +
+        "FROM wst_column_values wcv " +
+        "WHERE wst_column_id = :column_id " +
+        "ORDER by wcv.position";
+
+    public static final String SQL_COLUMN_QS =
+        "SELECT wqr.q AS q, r.a AS a, r.b AS b " +
+        "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 FixingsColumnFactory INSTANCE =
+        new FixingsColumnFactory();
+
+    private FixingsColumnFactory() {
+    }
+
+    public static FixingsColumnFactory getInstance() {
+        return INSTANCE;
+    }
+
+    public FixingsColumn getColumnData(Fixing.Column column) {
+
+        boolean debug = log.isDebugEnabled();
+
+        if (debug) {
+            log.debug("FixingsColumnFactory.getColumnData");
+        }
+
+        Cache cache = CacheFactory.getCache(CACHE_NAME);
+
+        if (cache == null) {
+            if (debug) {
+                log.debug("Cache unconfigured.");
+            }
+            return getUncached(column);
+        }
+
+        Integer cacheKey = Integer.valueOf(column.getId());
+        Element element  = cache.get(cacheKey);
+
+        if (element != null) {
+            if (debug) {
+                log.debug("Column " + cacheKey + " found in cache.");
+            }
+            return (FixingsColumn)element.getValue();
+        }
+        else {
+            FixingsColumn result = getUncached(column);
+            if (result != null) {
+                if (debug) {
+                    log.debug("Store column " + cacheKey + " into cache.");
+                }
+                cache.put(new Element(cacheKey, result));
+            }
+            return result;
+        }
+    }
+
+    protected FixingsColumn getUncached(Fixing.Column column) {
+        Session session = SessionHolder.HOLDER.get();
+
+        SQLQuery sqlQuery = session.createSQLQuery(SQL_COLUMN_WS)
+            .addScalar("km", StandardBasicTypes.DOUBLE)
+            .addScalar("w",  StandardBasicTypes.DOUBLE);
+
+        sqlQuery.setInteger("column_id", column.getId());
+
+        List<Object []> results = sqlQuery.list();
+
+        if (results.isEmpty()) {
+            return null;
+        }
+
+        double [] kms = new double[results.size()];
+        double [] ws  = new double[kms.length];
+
+        for (int i = 0; i < kms.length; ++i) {
+            Object [] row = results.get(i);
+            kms[i] = ((Double)row[0]).doubleValue();
+            ws [i] = ((Double)row[1]).doubleValue();
+        }
+
+        sqlQuery = session.createSQLQuery(SQL_COLUMN_QS)
+            .addScalar("q", StandardBasicTypes.DOUBLE)
+            .addScalar("a", StandardBasicTypes.DOUBLE)
+            .addScalar("b", StandardBasicTypes.DOUBLE);
+
+        sqlQuery.setInteger("column_id", column.getId());
+
+        results = sqlQuery.list();
+
+        if (results.isEmpty()) {
+            return null;
+        }
+
+        QRangeTree qs = new QRangeTree(
+            results, QRangeTree.WITHOUT_COLUMN, 0, results.size());
+
+        return new FixingsColumn(kms, ws, qs);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/FixingsFilterBuilder.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,247 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.dive4elements.river.artifacts.model.FixingsOverview.AndFilter;
+import org.dive4elements.river.artifacts.model.FixingsOverview.DateFilter;
+import org.dive4elements.river.artifacts.model.FixingsOverview.DateRangeFilter;
+
+import org.dive4elements.river.artifacts.model.FixingsOverview.Fixing.Filter;
+
+import org.dive4elements.river.artifacts.model.FixingsOverview.IdFilter;
+import org.dive4elements.river.artifacts.model.FixingsOverview.IdsFilter;
+import org.dive4elements.river.artifacts.model.FixingsOverview.KmFilter;
+import org.dive4elements.river.artifacts.model.FixingsOverview.NotFilter;
+import org.dive4elements.river.artifacts.model.FixingsOverview.OrFilter;
+import org.dive4elements.river.artifacts.model.FixingsOverview.SectorFilter;
+import org.dive4elements.river.artifacts.model.FixingsOverview.SectorRangeFilter;
+
+import java.text.ParsePosition;
+import java.text.SimpleDateFormat;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+public class FixingsFilterBuilder
+{
+    private static Logger log = Logger.getLogger(FixingsFilterBuilder.class);
+
+    protected Filter   filter;
+    protected Range    range;
+
+    protected Document document;
+
+    public FixingsFilterBuilder() {
+    }
+
+    public FixingsFilterBuilder(Document document) {
+        this.document = document;
+    }
+
+    public Filter getFilter() {
+        if (filter == null) {
+            filter = buildFilter();
+        }
+        return filter;
+    }
+
+    public Range getRange() {
+        if (range == null) {
+            range = buildRange();
+        }
+        return range;
+    }
+
+    public Document getDocument() {
+        return document;
+    }
+
+    protected Range buildRange() {
+
+        NodeList ranges = document.getElementsByTagName("range");
+
+        if (ranges.getLength() < 1) {
+            return FixingsOverview.FULL_EXTENT;
+        }
+
+        Element range = (Element)ranges.item(0);
+
+        String from = range.getAttribute("from").trim();
+        String to   = range.getAttribute("to"  ).trim();
+
+        double start = -Double.MAX_VALUE;
+        double end   =  Double.MAX_VALUE;
+
+        if (from.length() > 0) {
+            try {
+                start = Double.parseDouble(from);
+            }
+            catch (NumberFormatException nfe) {
+                log.warn("Invalid from value: " + from);
+            }
+        }
+
+        if (to.length() > 0) {
+            try {
+                end = Double.parseDouble(to);
+            }
+            catch (NumberFormatException nfe) {
+                log.warn("Invalid to value: " + to);
+            }
+        }
+
+        if (start > end) {
+            double t = start;
+            start = end;
+            end = t;
+        }
+
+        return new Range(start, end);
+    }
+
+    protected Filter buildFilter() {
+        NodeList filters = document.getElementsByTagName("filter");
+
+        return filters.getLength() < 1
+            ? FixingsOverview.ACCEPT
+            : buildFilter((Element)filters.item(0));
+    }
+
+    protected static Filter buildFilter(Element root) {
+        List<Filter> filters = buildRecursiveFilter(root);
+        switch (filters.size()) {
+            case  0: return FixingsOverview.ACCEPT;
+            case  1: return filters.get(0);
+            default: return new AndFilter(filters);
+        }
+    }
+
+    protected static final Date parseDate(String text) {
+        SimpleDateFormat format =
+            new SimpleDateFormat(FixingsOverview.DATE_FORMAT);
+        return format.parse(text, new ParsePosition(0));
+    }
+
+    protected static List<Filter> buildRecursiveFilter(Element root) {
+        List<Filter> filters = new ArrayList<Filter>();
+
+        NodeList children = root.getChildNodes();
+
+        for (int i = 0, N = children.getLength(); i < N; ++i) {
+            Node child = children.item(i);
+            if (child.getNodeType() != Node.ELEMENT_NODE) {
+                continue;
+            }
+
+            Element element = (Element)child;
+            String name = element.getLocalName();
+
+            if ("and".equals(name)) {
+                filters.add(new AndFilter(buildRecursiveFilter(element)));
+            }
+            else if ("or".equals(name)) {
+                filters.add(new OrFilter(buildRecursiveFilter(element)));
+            }
+            else if ("not".equals(name)) {
+                List<Filter> childrenFilters = buildRecursiveFilter(element);
+                if (!childrenFilters.isEmpty()) {
+                    filters.add(new NotFilter(childrenFilters.get(0)));
+                }
+            }
+            else if ("column".equals(name)) {
+                String cid = element.getAttribute("cid").trim();
+                if (cid.length() > 0) {
+                    try {
+                        filters.add(new IdFilter(Integer.parseInt(cid)));
+                    }
+                    catch (NumberFormatException nfe) {
+                        log.warn(nfe);
+                    }
+                }
+            }
+            else if ("columns".equals(name)) {
+                String cidsS = element.getAttribute("cids").trim();
+                String [] parts = cidsS.split("\\s+");
+                List<Integer> ids = new ArrayList<Integer>();
+                for (String part: parts) {
+                    try {
+                        ids.add(Integer.valueOf(part));
+                    }
+                    catch (NumberFormatException nfe) {
+                        log.warn(nfe);
+                    }
+                }
+                int [] cids = new int[ids.size()];
+                for (int j = 0; j < cids.length; ++j) {
+                    cids[j] = ids.get(j);
+                }
+                filters.add(new IdsFilter(cids));
+            }
+            else if ("date".equals(name)) {
+                String when = element.getAttribute("when").trim();
+                if (when.length() > 0) {
+                    Date date = parseDate(when);
+                    if (date != null) {
+                        filters.add(new DateFilter(date));
+                    }
+                }
+            }
+            else if ("date-range".equals(name)) {
+                String from = element.getAttribute("from").trim();
+                String to   = element.getAttribute("to"  ).trim();
+                if (from.length() > 0 && to.length() > 0) {
+                    Date start = parseDate(from);
+                    Date end   = parseDate(to);
+                    if (start != null && end != null) {
+                        filters.add(new DateRangeFilter(start, end));
+                    }
+                }
+            }
+            else if ("sector-range".equals(name)) {
+                String from = element.getAttribute("from").trim();
+                String to   = element.getAttribute("to"  ).trim();
+                if (from.length() > 0 && to.length() > 0) {
+                    try {
+                        filters.add(new SectorRangeFilter(
+                            Integer.parseInt(from),
+                            Integer.parseInt(to)));
+                    }
+                    catch (NumberFormatException nfe) {
+                        log.warn(nfe);
+                    }
+                }
+            }
+            else if ("sector".equals(name)) {
+                String value = element.getAttribute("value").trim();
+                if (value.length() > 0) {
+                    try {
+                        filters.add(new SectorFilter(Integer.parseInt(value)));
+                    }
+                    catch (NumberFormatException nfe) {
+                        log.warn(nfe);
+                    }
+                }
+            }
+            else if ("position".equals(name)) {
+                String km = element.getAttribute("km").trim();
+                if (km.length() > 0) {
+                    try {
+                        filters.add(new KmFilter(Double.parseDouble(km)));
+                    }
+                    catch (NumberFormatException nfe) {
+                        log.warn(nfe);
+                    }
+                }
+            }
+        }
+
+        return filters;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/FixingsOverview.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,808 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.io.Serializable;
+
+import java.text.SimpleDateFormat;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+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.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+
+/** Generate Fixings Table chart. */
+public class FixingsOverview
+implements   Serializable
+{
+    private static Logger log = Logger.getLogger(FixingsOverview.class);
+
+    public static final double EPSILON = 1e-2;
+
+    public static final String DATE_FORMAT = "dd.MM.yyyy";
+
+    public static final String SQL_RIVER_ID =
+        "SELECT" +
+        "    id AS river_id," +
+        "    km_up " +
+        "FROM rivers " +
+        "WHERE" +
+        "    name = :name";
+
+    /** All kind-2 wsts from given river. */
+    public static final String SQL_FIXINGS =
+        "SELECT" +
+        "    id AS wst_id," +
+        "    description " +
+        "FROM wsts " +
+        "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_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_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 class QRange extends Range {
+
+        protected double q;
+
+        public QRange() {
+        }
+
+        public QRange(double start, double end, double q) {
+            super(start, end);
+            this.q = q;
+        }
+    } // class QRange
+
+    public static class SectorRange extends Range {
+
+        protected int sector;
+
+        public SectorRange() {
+        }
+
+        public SectorRange(SectorRange other) {
+            start  = other.start;
+            end    = other.end;
+            sector = other.sector;
+        }
+
+        public SectorRange(Range range) {
+            super(range);
+        }
+
+        public SectorRange(double start, double end, int sector) {
+            super(start, end);
+            this.sector = sector;
+        }
+
+        public int getSector() {
+            return sector;
+        }
+
+        public void setSector(int sector) {
+            this.sector = sector;
+        }
+
+        public boolean enlarge(SectorRange other) {
+            if (sector == other.sector
+            && Math.abs(end-other.start) < FixingsOverview.EPSILON) {
+                end = other.end;
+                return true;
+            }
+            return false;
+        }
+    } // class SectorRange
+
+    public static class Fixing implements Serializable {
+
+        public static final Comparator<Column> DATE_CMP =
+            new Comparator<Column>() {
+                @Override
+                public int compare(Column a, Column b) {
+                    return a.startTime.compareTo(b.startTime);
+                }
+            };
+
+        public interface Filter {
+
+            boolean accept(Column column);
+
+        } // interface Filter
+
+        public class Column extends Range {
+
+            protected int    columnId;
+            protected Date   startTime;
+            protected String name;
+
+            protected List<SectorRange> sectors;
+
+            public Column() {
+            }
+
+            public Column(int columnId, Date startTime, String name) {
+                this.columnId  = columnId;
+                this.startTime = startTime;
+                this.name      = name;
+
+                sectors = new ArrayList<SectorRange>();
+            }
+
+            public int getId() {
+                return columnId;
+            }
+
+            public Fixing getFixing() {
+                return Fixing.this;
+            }
+
+            public Date getStartTime() {
+                return startTime;
+            }
+
+            public String getName() {
+                return name;
+            }
+
+            public String getDescription() {
+                return Fixing.this.description + "/" + name;
+            }
+
+            public List<SectorRange> getSectors() {
+                return sectors;
+            }
+
+            public boolean hasSectorsInRange(Range range) {
+                for (SectorRange sector: sectors) {
+                    if (sector.intersects(range)) {
+                        return true;
+                    }
+                }
+                return false;
+            }
+
+            public List<SectorRange> getSectors(Range range) {
+
+                List<SectorRange> result =
+                    new ArrayList<SectorRange>(sectors.size());
+
+                for (SectorRange src: sectors) {
+                    SectorRange dst = new SectorRange(src);
+                    if (range == null || dst.clip(range)) {
+                        result.add(dst);
+                    }
+                }
+
+                return result;
+            }
+
+            public int findQSector(double km) {
+                for (SectorRange sector: sectors) {
+                    if (sector.inside(km)) {
+                        return sector.getSector();
+                    }
+                }
+                return -1;
+            }
+
+            public void buildSectors(
+                GaugeFinder  gaugeFinder,
+                List<QRange> qRanges
+            ) {
+                for (QRange qRange: qRanges) {
+                    for (GaugeRange gRange: gaugeFinder.getGauges()) {
+                        SectorRange sector = new SectorRange(qRange);
+                        if (!sector.clip(gRange)) {
+                            continue;
+                        }
+                        sector.setSector(gRange.classify(qRange.q));
+
+                        if (sectors.isEmpty()
+                        || !sectors.get(sectors.size()-1).enlarge(sector)) {
+                            sectors.add(sector);
+                        }
+                    } // for all gauges
+                } // for all Q ranges
+            }
+
+            public void loadKmRange(SQLQuery query) {
+                query.setInteger("column_id", columnId);
+
+                List<Object []> kms = query.list();
+
+                if (kms.isEmpty()) {
+                    log.warn("No km range for column " + columnId + ".");
+                }
+                else {
+                    Object [] obj = kms.get(0);
+                    start = (Double)obj[0];
+                    end   = (Double)obj[1];
+                }
+            }
+
+            public void loadQRanges(
+                SQLQuery    query,
+                GaugeFinder gaugeFinder
+            ) {
+                query.setInteger("column_id", columnId);
+                List<Object []> list = query.list();
+
+                List<QRange> qRanges = new ArrayList<QRange>(list.size());
+
+                for (Object [] row: list) {
+                    double q     = (Double)row[0];
+                    double start = (Double)row[1];
+                    double end   = (Double)row[2];
+                    QRange qRange = new QRange(start, end, q);
+                    if (qRange.clip(this)) {
+                        qRanges.add(qRange);
+                    }
+                }
+
+                buildSectors(gaugeFinder, qRanges);
+            }
+        } // class Column
+
+        protected int          wstId;
+        protected String       description;
+        protected List<Column> columns;
+
+        public Fixing() {
+        }
+
+        public int getId() {
+            return wstId;
+        }
+
+        public String getDescription() {
+            return description;
+        }
+
+        public Fixing(int wstId, String description) {
+            this.wstId       = wstId;
+            this.description = description;
+            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 loadColumnsKmRange(SQLQuery query) {
+            for (Column column: columns) {
+                column.loadKmRange(query);
+            }
+        }
+
+        public void adjustExtent(Range extent) {
+            for (Column column: columns) {
+                extent.extend(column);
+            }
+        }
+
+        public void loadColumnsQRanges(
+            SQLQuery    query,
+            GaugeFinder gaugeFinder
+        ) {
+            for (Column column: columns) {
+                column.loadQRanges(query, gaugeFinder);
+            }
+        }
+
+        /**
+         * @param allColumns[out] Columns will be put here.
+         * @param range can be null.
+         * @param filter filter to apply.
+         */
+        public void addAllColumns(
+            List<Column> allColumns,
+            Range        range,
+            Filter       filter
+        ) {
+            for (Column column: columns) {
+                if ((range == null || column.hasSectorsInRange(range))
+                && (filter == null || filter.accept(column))) {
+                    allColumns.add(column);
+                }
+            }
+        }
+    } // class Fixing
+
+
+    protected String       riverName;
+    protected int          riverId;
+    protected boolean      isKmUp;
+    protected List<Fixing> fixings;
+    protected Range        extent;
+
+    public FixingsOverview() {
+        fixings = new ArrayList<Fixing>();
+        extent  = new Range(Double.MAX_VALUE, -Double.MAX_VALUE);
+    }
+
+    public FixingsOverview(String riverName) {
+        this();
+        this.riverName = riverName;
+    }
+
+    protected boolean loadRiver(Session session) {
+        SQLQuery query = session.createSQLQuery(SQL_RIVER_ID)
+            .addScalar("river_id", StandardBasicTypes.INTEGER)
+            .addScalar("km_up",    StandardBasicTypes.BOOLEAN);
+
+        query.setString("name", riverName);
+
+        List<Object []> list = query.list();
+
+        if (list.isEmpty()) {
+            log.warn("No river '" + riverName + "' found.");
+            return false;
+        }
+
+        Object [] row = list.get(0);
+
+        riverId = (Integer)row[0];
+        isKmUp  = (Boolean)row[1];
+
+        return true;
+    }
+
+    protected void loadFixings(Session session) {
+        SQLQuery query = session.createSQLQuery(SQL_FIXINGS)
+            .addScalar("wst_id",      StandardBasicTypes.INTEGER)
+            .addScalar("description", StandardBasicTypes.STRING);
+
+        query.setInteger("river_id", riverId);
+
+        List<Object []> list = query.list();
+
+        if (list.isEmpty()) {
+            log.warn("River " + riverId + " has no fixings.");
+            // Its pretty fine to have no fixings.
+        }
+
+        for (Object [] row: list) {
+            int    wstId       = (Integer)row[0];
+            String description = (String) row[1];
+            Fixing fixing = new Fixing(wstId, description);
+            fixings.add(fixing);
+        }
+    }
+
+    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);
+
+        for (Fixing fixing: fixings) {
+            fixing.loadColumns(query);
+        }
+    }
+
+    protected void loadFixingsColumnsKmRange(Session session) {
+        SQLQuery query = session.createSQLQuery(SQL_FIXING_COLUMN_KM_RANGE)
+            .addScalar("start_km", StandardBasicTypes.DOUBLE)
+            .addScalar("stop_km",  StandardBasicTypes.DOUBLE);
+
+        for (Fixing fixing: fixings) {
+            fixing.loadColumnsKmRange(query);
+        }
+    }
+
+    protected void loadFixingsColumnsQRanges(
+        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);
+
+        for (Fixing fixing: fixings) {
+            fixing.loadColumnsQRanges(query, gaugeFinder);
+        }
+    }
+
+    protected void adjustExtent() {
+        for (Fixing fixing: fixings) {
+            fixing.adjustExtent(extent);
+        }
+    }
+
+    public boolean load(Session session) {
+
+        if (!loadRiver(session)) {
+            return false;
+        }
+
+        GaugeFinderFactory gff = GaugeFinderFactory.getInstance();
+
+        GaugeFinder gaugeFinder = gff.getGaugeFinder(riverId, isKmUp);
+
+        if (gaugeFinder == null) {
+            return false;
+        }
+
+        loadFixings(session);
+        loadFixingsColumns(session);
+        loadFixingsColumnsKmRange(session);
+
+        adjustExtent();
+
+        loadFixingsColumnsQRanges(session, gaugeFinder);
+
+        return true;
+    }
+
+    public static final Range FULL_EXTENT =
+        new Range(-Double.MAX_VALUE, Double.MAX_VALUE);
+
+    public static final Fixing.Filter ACCEPT = new Fixing.Filter() {
+        @Override
+        public boolean accept(Fixing.Column column) {
+            return true;
+        }
+    };
+
+    public static class NotFilter implements Fixing.Filter {
+        protected Fixing.Filter child;
+
+        public NotFilter(Fixing.Filter child) {
+            this.child = child;
+        }
+
+        @Override
+        public boolean accept(Fixing.Column column) {
+            return !child.accept(column);
+        }
+    } // class NotFilter
+
+    public static abstract class ComponentFilter implements Fixing.Filter {
+        protected List<Fixing.Filter> children;
+
+        public ComponentFilter() {
+            children = new ArrayList<Fixing.Filter>();
+        }
+
+        public ComponentFilter(List<Fixing.Filter> children) {
+            this.children = children;
+        }
+
+        public ComponentFilter add(Fixing.Filter filter) {
+            children.add(filter);
+            return this;
+        }
+    } // class ComponentFilter
+
+    public static class OrFilter extends ComponentFilter {
+
+        public OrFilter() {
+        }
+
+        public OrFilter(List<Fixing.Filter> children) {
+            super(children);
+        }
+
+        @Override
+        public boolean accept(Fixing.Column column) {
+            for (Fixing.Filter child: children) {
+                if (child.accept(column)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    } // class OrFilter
+
+    public static class AndFilter extends ComponentFilter {
+
+        public AndFilter() {
+        }
+
+        public AndFilter(List<Fixing.Filter> children) {
+            super(children);
+        }
+
+        @Override
+        public boolean accept(Fixing.Column column) {
+            for (Fixing.Filter child: children) {
+                if (!child.accept(column)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+    } // class AndFilter
+
+    public static class IdFilter implements Fixing.Filter {
+
+        protected int columnId;
+
+        public IdFilter(int columnId) {
+            this.columnId = columnId;
+        }
+
+        @Override
+        public boolean accept(Fixing.Column column) {
+            return column.getId() == columnId;
+        }
+    } // class IdFilter
+
+    /** Accept Fixing columns whose id is in id list. */
+    public static class IdsFilter implements Fixing.Filter {
+
+        protected int [] columnIds;
+
+        public IdsFilter(int [] columnIds) {
+            this.columnIds = columnIds;
+        }
+
+        @Override
+        public boolean accept(Fixing.Column column) {
+            int cid = column.getId();
+            for (int i = columnIds.length-1; i >= 0; --i) {
+                if (columnIds[i] == cid) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    } // class IdFilter
+
+    public static class DateFilter implements Fixing.Filter {
+
+        protected Date date;
+
+        public DateFilter(Date date) {
+            this.date = date;
+        }
+
+        @Override
+        public boolean accept(Fixing.Column column) {
+            return date.equals(column.getStartTime());
+        }
+    } // class DateFilter
+
+    public static class DateRangeFilter implements Fixing.Filter {
+
+        protected Date start;
+        protected Date end;
+
+        public DateRangeFilter(Date start, Date end) {
+            this.start = start;
+            this.end   = end;
+        }
+
+        @Override
+        public boolean accept(Fixing.Column column) {
+            Date date = column.getStartTime();
+            return start.compareTo(date) <= 0 && end.compareTo(date) >= 0;
+        }
+    } // class DateRangeFilter
+
+    public static class SectorFilter implements Fixing.Filter {
+
+        protected int sector;
+
+        public SectorFilter(int sector) {
+            this.sector = sector;
+        }
+
+        @Override
+        public boolean accept(Fixing.Column column) {
+            for (SectorRange s: column.getSectors()) {
+                if (s.getSector() == sector) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    } // class SectorFilter
+
+    public static class SectorRangeFilter implements Fixing.Filter {
+
+        protected int min;
+        protected int max;
+
+        public SectorRangeFilter(int min, int max) {
+            this.min = Math.min(min, max);
+            this.max = Math.max(min, max);
+        }
+
+        @Override
+        public boolean accept(Fixing.Column column) {
+            for (SectorRange s: column.getSectors()) {
+                int v = s.getSector();
+                if (v < min || v > max) {
+                    return false;
+                }
+            }
+            return true;
+        }
+    } // class SectorRangeFilter
+
+    public static class KmFilter implements Fixing.Filter {
+
+        protected double km;
+
+        public KmFilter(double km) {
+            this.km = km;
+        }
+
+        @Override
+        public boolean accept(Fixing.Column column) {
+            for (SectorRange s: column.getSectors()) {
+                if (s.inside(km)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    } // class KmFilter
+
+    public void generateOverview(Document document) {
+        generateOverview(document, FULL_EXTENT, ACCEPT);
+    }
+
+    public List<Fixing.Column> filter(Range range, Fixing.Filter filter) {
+        List<Fixing.Column> allColumns = new ArrayList<Fixing.Column>();
+
+        for (Fixing fixing: fixings) {
+            fixing.addAllColumns(allColumns, range, filter);
+        }
+
+        Collections.sort(allColumns, Fixing.DATE_CMP);
+
+        return allColumns;
+    }
+
+    protected static Range realRange(List<Fixing.Column> columns) {
+        Range range = null;
+        for (Fixing.Column column: columns) {
+            if (range == null) {
+                range = new Range(column);
+            }
+            else {
+                range.extend(column);
+            }
+        }
+        return range;
+    }
+
+    protected Element intersectingGauges(Document document, Range range) {
+        Element gauges = document.createElement("gauges");
+
+        if (range == null) {
+            return gauges;
+        }
+
+        GaugeFinderFactory gff = GaugeFinderFactory.getInstance();
+
+        GaugeFinder gf = gff.getGaugeFinder(riverId, isKmUp);
+
+        if (gf == null) {
+            return gauges;
+        }
+
+        for (GaugeRange gr: gf.getGauges()) {
+            if (gr.intersects(range)) {
+                Element gauge = document.createElement("gauge");
+                gauge.setAttribute("from", String.valueOf(gr.getStart()));
+                gauge.setAttribute("to",   String.valueOf(gr.getEnd()));
+                gauge.setAttribute("name", gr.getName());
+                gauges.appendChild(gauge);
+            }
+        }
+
+        return gauges;
+    }
+
+    /** Populate document with fixings, filtered by range and filter. */
+    public void generateOverview(
+        Document      document,
+        Range         range,
+        Fixing.Filter filter
+    ) {
+        List<Fixing.Column> allColumns = filter(range, filter);
+
+        Element fixingsElement = document.createElement("fixings");
+
+        Element riverElement = document.createElement("river");
+
+        riverElement.setAttribute("from", String.valueOf(extent.start));
+        riverElement.setAttribute("to",   String.valueOf(extent.end));
+        riverElement.setAttribute("rid",  String.valueOf(riverId));
+        riverElement.setAttribute("name", riverName);
+
+        fixingsElement.appendChild(riverElement);
+
+        fixingsElement.appendChild(
+            intersectingGauges(
+                document,
+                realRange(allColumns)));
+
+        SimpleDateFormat df = new SimpleDateFormat(DATE_FORMAT);
+
+        Element esE = document.createElement("events");
+
+        for (Fixing.Column column: allColumns) {
+
+            List<SectorRange> sectors = column.getSectors(range);
+
+            if (!sectors.isEmpty()) {
+                Element eE = document.createElement("event");
+                eE.setAttribute("description",
+                    String.valueOf(column.getDescription()));
+                eE.setAttribute("cid", String.valueOf(column.columnId));
+                eE.setAttribute("date", df.format(column.startTime));
+
+                for (SectorRange sector: sectors) {
+                    Element sE = document.createElement("sector");
+
+                    sE.setAttribute("from",  String.valueOf(sector.start));
+                    sE.setAttribute("to",    String.valueOf(sector.end));
+                    sE.setAttribute("class", String.valueOf(sector.sector));
+
+                    eE.appendChild(sE);
+                }
+
+                esE.appendChild(eE);
+            }
+        }
+
+        fixingsElement.appendChild(esE);
+
+        document.appendChild(fixingsElement);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/FixingsOverviewFactory.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,73 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.dive4elements.river.artifacts.cache.CacheFactory;
+
+import org.dive4elements.river.backend.SessionHolder;
+
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.Element;
+
+import org.apache.log4j.Logger;
+
+import org.hibernate.Session;
+
+public class FixingsOverviewFactory
+{
+    private static Logger log = Logger.getLogger(FixingsOverviewFactory.class);
+
+    public static final String CACHE_NAME = "fixings-overviews";
+
+    private FixingsOverviewFactory() {
+    }
+
+
+    public static FixingsOverview getOverview(String river) {
+
+        boolean debug = log.isDebugEnabled();
+
+        if (debug) {
+            log.debug(
+                "Looking for fixings overview for river '" + river + "'");
+        }
+
+        Cache cache = CacheFactory.getCache(CACHE_NAME);
+
+        if (cache == null) {
+            if (debug) {
+                log.debug("Cache not configured.");
+            }
+            return getUncached(river);
+        }
+
+        String key = "fix-over-" + river;
+
+        Element element = cache.get(key);
+
+        if (element != null) {
+            if (debug) {
+                log.debug("Overview found in cache");
+            }
+            return (FixingsOverview)element.getValue();
+        }
+
+        FixingsOverview overview = getUncached(river);
+
+        if (overview != null) {
+            if (debug) {
+                log.debug("Store overview in cache.");
+            }
+            cache.put(new Element(key, overview));
+        }
+
+        return overview;
+    }
+
+    public static FixingsOverview getUncached(String river) {
+        FixingsOverview overview = new FixingsOverview(river);
+
+        Session session = SessionHolder.HOLDER.get();
+
+        return overview.load(session) ? overview : 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/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/FlowVelocityCalculation.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,166 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.dive4elements.artifacts.Artifact;
+
+import org.dive4elements.river.artifacts.access.FlowVelocityAccess;
+
+import org.dive4elements.river.model.DischargeZone;
+import org.dive4elements.river.model.FlowVelocityModel;
+import org.dive4elements.river.model.FlowVelocityModelValue;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+
+/** Calculate flow velocity. */
+public class FlowVelocityCalculation extends Calculation {
+
+    /** Own logger. */
+    private static final Logger logger =
+        Logger.getLogger(FlowVelocityCalculation.class);
+
+
+    public CalculationResult calculate(FlowVelocityAccess access) {
+        logger.info("FlowVelocityCalculation.calculate");
+
+        int[] mainIds  = access.getMainChannels();
+        int[] totalIds = access.getTotalChannels();
+
+        if (logger.isDebugEnabled()) {
+            Artifact a = access.getArtifact();
+            logger.debug("Artifact '" + a.identifier() + "' contains:");
+            if (mainIds != null) {
+                logger.debug("   " + mainIds.length + " main channel ids");
+            }
+
+            if (totalIds != null) {
+                logger.debug("   " + totalIds.length + " total channel ids");
+            }
+        }
+
+        List<DischargeZone>     zones  = getDischargeZones(mainIds, totalIds);
+        List<FlowVelocityModel> models = getFlowVelocityModels(access, zones);
+
+        return buildCalculationResult(access, models);
+    }
+
+
+    protected List<DischargeZone> getDischargeZones(
+        int[] mainIds,
+        int[] totalIds
+    ) {
+        List<DischargeZone> zones = new ArrayList<DischargeZone>();
+
+        if (mainIds != null) {
+            for (int id: mainIds) {
+                DischargeZone zone = DischargeZone.getDischargeZoneById(id);
+                zone.putType("main");
+
+                if (zone != null) {
+                    zones.add(zone);
+                }
+            }
+        }
+
+        if (totalIds != null) {
+            for (int id: totalIds) {
+                DischargeZone zone = DischargeZone.getDischargeZoneById(id);
+                if (zone != null) {
+                    int ndx = zones.indexOf(zone);
+                    if (zones.contains(zone) &&
+                        zones.get(ndx).fetchType().equals("main")) {
+                        zone.putType("main_total");
+                    }
+                    else {
+                        zone.putType("total");
+                        zones.add(zone);
+                    }
+                }
+            }
+        }
+
+        return zones;
+    }
+
+
+    protected List<FlowVelocityModel> getFlowVelocityModels(
+        FlowVelocityAccess  access,
+        List<DischargeZone> zones
+    ) {
+        String riverName = access.getRiver();
+        if (riverName == null) {
+            logger.warn("No river name found");
+            return Collections.<FlowVelocityModel>emptyList();
+        }
+
+        List<FlowVelocityModel> models = new ArrayList<FlowVelocityModel>();
+
+        for (DischargeZone zone: zones) {
+            List<FlowVelocityModel> model = FlowVelocityModel.getModels(zone);
+            models.addAll(model);
+        }
+
+        return models;
+    }
+
+
+    protected void prepareData(
+        FlowVelocityData  data,
+        FlowVelocityModel model,
+        double kmLo,
+        double kmHi
+    ) {
+        List<FlowVelocityModelValue> values =
+            FlowVelocityModelValue.getValues(model, kmLo, kmHi);
+
+        logger.debug("Found " + values.size() + " values for model.");
+
+        for (FlowVelocityModelValue value: values) {
+            data.addKM(value.getStation().doubleValue());
+            data.addQ(value.getQ().doubleValue());
+            data.addVTotal(value.getTotalChannel().doubleValue());
+            data.addVMain(value.getMainChannel().doubleValue());
+            data.addTauMain(value.getShearStress().doubleValue());
+        }
+
+        DischargeZone zone = model.getDischargeZone();
+        String        lo   = zone.getLowerDischarge();
+        String        hi   = zone.getUpperDischarge();
+
+        data.setType(zone.fetchType());
+        if (lo.equals(hi)) {
+            data.setZone(lo);
+        }
+        else {
+            data.setZone(lo + " - " + hi);
+        }
+    }
+
+
+    protected CalculationResult buildCalculationResult(
+        FlowVelocityAccess         access,
+        List<FlowVelocityModel> models
+    ) {
+        double kmLo = access.getLowerKM();
+        double kmHi = access.getUpperKM();
+
+        logger.debug("Prepare data for km range: " + kmLo + " - " + kmHi);
+
+        FlowVelocityData[] data = new FlowVelocityData[models.size()];
+        for (int i = 0, n = models.size(); i < n; i++) {
+            FlowVelocityData d = new FlowVelocityData();
+
+            prepareData(d, models.get(i), kmLo, kmHi);
+
+            data[i] = d;
+        }
+
+        logger.debug("Calculation contains " + data.length + " data items.");
+
+        return new CalculationResult(data, this);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/FlowVelocityData.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,135 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.io.Serializable;
+
+import gnu.trove.TDoubleArrayList;
+
+
+public class FlowVelocityData implements Serializable {
+
+    private TDoubleArrayList km;
+    private TDoubleArrayList vMain;
+    private TDoubleArrayList vTotal;
+    private TDoubleArrayList tauMain;
+    private TDoubleArrayList q;
+    private String zone;
+    private String type;
+
+    protected FlowVelocityData() {
+        this.km      = new TDoubleArrayList();
+        this.vMain   = new TDoubleArrayList();
+        this.vTotal  = new TDoubleArrayList();
+        this.tauMain = new TDoubleArrayList();
+        this.q       = new TDoubleArrayList();
+    }
+
+
+    public void addKM(double km) {
+        this.km.add(km);
+    }
+
+    public double getKM(int idx) {
+        return km.get(idx);
+    }
+
+    public void addVMain(double vMain) {
+        this.vMain.add(vMain);
+    }
+
+    public double getVMain(int idx) {
+        return vMain.get(idx);
+    }
+
+    public void addVTotal(double vTotal) {
+        this.vTotal.add(vTotal);
+    }
+
+    public double getVTotal(int idx) {
+        return vTotal.get(idx);
+    }
+
+    public void addTauMain(double tauMain) {
+        this.tauMain.add(tauMain);
+    }
+
+    public double getTauMain(int idx) {
+        return tauMain.get(idx);
+    }
+
+    public void addQ(double q) {
+        this.q.add(q);
+    }
+
+    public double getQ(int idx) {
+        return q.get(idx);
+    }
+
+    public void setZone(String zone) {
+        this.zone = zone;
+    }
+
+    public String getZone() {
+        return zone;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public String getType() {
+        return this.type;
+    }
+
+    public int size() {
+        return km.size();
+    }
+
+
+    public double[][] getMainChannelPoints() {
+        double[][] points = new double[2][size()];
+
+        for (int i = 0, n = size(); i < n; i++) {
+            points[0][i] = getKM(i);
+            points[1][i] = getVMain(i);
+        }
+
+        return points;
+    }
+
+
+    public double[][] getTotalChannelPoints() {
+        double[][] points = new double[2][size()];
+
+        for (int i = 0, n = size(); i < n; i++) {
+            points[0][i] = getKM(i);
+            points[1][i] = getVTotal(i);
+        }
+
+        return points;
+    }
+
+
+    public double[][] getQPoints() {
+        double[][] points = new double[2][size()];
+
+        for (int i = 0, n = size(); i < n; i++) {
+            points[0][i] = getKM(i);
+            points[1][i] = getQ(i);
+        }
+
+        return points;
+    }
+
+
+    public double[][] getTauPoints() {
+        double[][] points = new double[2][size()];
+
+        for (int i = 0, n = size(); i < n; i++) {
+            points[0][i] = getKM(i);
+            points[1][i] = getTauMain(i);
+        }
+
+        return points;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/FlowVelocityFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,63 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+import org.apache.log4j.Logger;
+
+/**
+ * Facet of a FlowVelocity curve.
+ */
+public class FlowVelocityFacet extends DataFacet {
+
+    private static Logger logger = Logger.getLogger(FlowVelocityFacet.class);
+
+    public FlowVelocityFacet() {
+        // required for clone operation deepCopy()
+    }
+
+
+    public FlowVelocityFacet(
+        int         idx,
+        String      name,
+        String      description,
+        ComputeType type,
+        String      stateId,
+        String      hash
+    ) {
+        super(idx, name, description, type, hash, stateId);
+    }
+
+
+    public Object getData(Artifact artifact, CallContext context) {
+        logger.debug("Get data for flow velocity at index: " + index);
+
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        CalculationResult res = (CalculationResult)
+            flys.compute(context, hash, stateId, type, false);
+
+        FlowVelocityData[] data = (FlowVelocityData[]) res.getData();
+
+        return data[index];
+    }
+
+
+    /** Copy deeply. */
+    @Override
+    public Facet deepCopy() {
+        FlowVelocityFacet copy = new FlowVelocityFacet();
+        copy.set(this);
+        copy.type    = type;
+        copy.hash    = hash;
+        copy.stateId = stateId;
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/FlowVelocityFilterFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,90 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+import org.dive4elements.river.artifacts.access.RiverAccess;
+import org.dive4elements.river.artifacts.context.FLYSContext;
+
+import org.dive4elements.river.artifacts.math.MovingAverage;
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+import org.apache.log4j.Logger;
+
+/**
+ * Facet of a FlowVelocity curve.
+ */
+public class FlowVelocityFilterFacet extends DataFacet {
+
+    private static Logger logger = Logger.getLogger(FlowVelocityFilterFacet.class);
+
+    public FlowVelocityFilterFacet() {
+        // required for clone operation deepCopy()
+    }
+
+
+    public FlowVelocityFilterFacet(
+        int         idx,
+        String      name,
+        String      description,
+        ComputeType type,
+        String      stateId,
+        String      hash
+    ) {
+        super(idx, name, description, type, hash, stateId);
+    }
+
+
+    public Object getData(Artifact artifact, CallContext context) {
+        logger.debug("Get data for flow velocity at index: " + index);
+
+        Double start = (Double)context.getContextValue("startkm");
+        Double end = (Double)context.getContextValue("endkm");
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        CalculationResult res = (CalculationResult)
+            flys.compute(context, hash, stateId, type, false);
+
+        FlowVelocityData[] data = (FlowVelocityData[]) res.getData();
+        if(start != null && end != null) {
+            FLYSContext fc = (FLYSContext)context.globalContext();
+            ZoomScale scales = (ZoomScale)fc.get("zoomscale");
+            RiverAccess access = new RiverAccess((FLYSArtifact)artifact);
+            String river = access.getRiver();
+
+            double radius = scales.getRadius(river, start, end);
+            FlowVelocityData oldData = data[index];
+            FlowVelocityData newData = new FlowVelocityData();
+            double[][] q = oldData.getQPoints();
+            double[][] totalV = MovingAverage.weighted(oldData.getTotalChannelPoints(), radius);
+            double[][] mainV = MovingAverage.weighted(oldData.getMainChannelPoints(), radius);
+            double[][] tau = MovingAverage.weighted(oldData.getTauPoints(), radius);
+            for(int j = 0; j < q[0].length; j++) {
+                newData.addKM(q[0][j]);
+                newData.addQ(q[1][j]);
+                newData.addTauMain(tau[1][j]);
+                newData.addVMain(mainV[1][j]);
+                newData.addVTotal(totalV[1][j]);
+            }
+            return newData;
+        }
+        return data[index];
+    }
+
+
+    /** Copy deeply. */
+    @Override
+    public Facet deepCopy() {
+        FlowVelocityFilterFacet copy = new FlowVelocityFilterFacet();
+        copy.set(this);
+        copy.type    = type;
+        copy.hash    = hash;
+        copy.stateId = stateId;
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/GaugeDischargeCurveFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,82 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.util.Arrays;
+import java.util.Map;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.artifactdatabase.state.DefaultFacet;
+import org.dive4elements.artifactdatabase.state.Facet;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+import org.dive4elements.river.model.Gauge;
+
+import org.dive4elements.river.utils.FLYSUtils;
+
+import org.apache.log4j.Logger;
+
+/**
+ * A Facet that returns discharge curve data at a gauge
+ *
+ * @author <a href="mailto:bjoern.ricks@intevation.de">Björn Ricks</a>
+ */
+public class GaugeDischargeCurveFacet
+extends      DefaultFacet
+implements FacetTypes
+{
+    private static final Logger log =
+        Logger.getLogger(GaugeDischargeCurveFacet.class);
+
+    public GaugeDischargeCurveFacet(String name, String description) {
+        super(0, name, description);
+    }
+
+    @Override
+    public Object getData(Artifact art, CallContext context) {
+        return getWQKms(art, context);
+    }
+
+    protected WQKms getWQKms(Artifact art, CallContext context) {
+        if (!(art instanceof FLYSArtifact)) {
+            log.warn("Invalid artifact type");
+            return null;
+        }
+
+        FLYSArtifact flys = (FLYSArtifact)art;
+
+        String river = flys.getDataAsString("river");
+
+        Gauge gauge = FLYSUtils.getReferenceGauge(flys);
+
+        if (river == null || gauge == null) {
+            log.warn("Unknown river or gauge");
+            return null;
+        }
+
+        String name = gauge.getName();
+
+        DischargeTables dt = new DischargeTables(river, name);
+
+        Map<String, double [][]> map = dt.getValues(
+                DischargeTables.MASTER_SCALE);
+
+        double [][] values = map.get(name);
+        if (values == null) {
+            return null;
+        }
+        double [] kms = new double[values[0].length];
+        Arrays.fill(kms, gauge.getStation().doubleValue());
+        return new WQKms(kms, values[0], values[1], name);
+    }
+
+    @Override
+    public Facet deepCopy() {
+        GaugeDischargeCurveFacet copy = new GaugeDischargeCurveFacet(
+                this.name,
+                this.description);
+        copy.set(this);
+        return copy;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/GaugeDischargeFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,86 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+
+/**
+ * Access Discharge Curve of a gauge.
+ */
+public class GaugeDischargeFacet extends DataFacet {
+
+    /** Private logger. */
+    private static final Logger logger =
+        Logger.getLogger(GaugeDischargeFacet.class);
+
+
+    public GaugeDischargeFacet() {
+    }
+
+
+    /**
+     * @param index Index translates to index of WQ-array.
+     * @param name Name of the facet.
+     * @param desc Description of the facet (visible in client).
+     */
+    public GaugeDischargeFacet(int index, String name, String desc) {
+        super(index, name, desc, ComputeType.ADVANCE, null,
+            "state.gaugedischarge.init");
+    }
+
+
+    public GaugeDischargeFacet(
+        int         index,
+        String      name,
+        String      description,
+        ComputeType type,
+        String      stateID,
+        String      hash
+
+    ) {
+        super(index, name, description, type, hash, stateID);
+    }
+
+
+    @Override
+    public Facet deepCopy() {
+        GaugeDischargeFacet copy = new GaugeDischargeFacet();
+        copy.set(this);
+        copy.type    = type;
+        copy.hash    = hash;
+        copy.stateId = stateId;
+        return copy;
+    }
+
+
+    /**
+     * @return wqkms corresponding to gauge of artifact and index of facet.
+     */
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        if (logger.isDebugEnabled()) {
+            logger.debug("Get data for discharge curves at index: " +
+                index + " / stateId: " + stateId);
+        }
+
+        if (stateId == null) {
+            logger.error("GaugeDischargeFacet.getData: stateId is null.");
+        }
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        CalculationResult res = (CalculationResult)
+            flys.compute(context, hash, stateId, type, true);
+
+        WQKms[] discharge = (WQKms[]) res.getData();
+
+        return discharge[index];
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/GaugeFinder.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,134 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.io.Serializable;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.hibernate.SQLQuery;
+import org.hibernate.Session;
+
+import org.hibernate.type.StandardBasicTypes;
+
+/** Find Gauges and respective Q main values. */
+public class GaugeFinder
+implements   Serializable
+{
+    private static Logger log = Logger.getLogger(GaugeFinder.class);
+
+    public static final String SQL_DISCHARGE_SECTORS =
+        "SELECT" +
+        "    g.id                            AS gauge_id," +
+        "    nmv.name                        AS name," +
+        "    CAST(mv.value AS NUMERIC(38,2)) AS value " +
+        "FROM gauges g" +
+        "    JOIN main_values       mv  ON g.id = mv.gauge_id" +
+        "    JOIN named_main_values nmv ON nmv.id = mv.named_value_id" +
+        "    JOIN main_value_types  mvt ON nmv.type_id = mvt.id " +
+        "WHERE" +
+        "    mvt.name = 'Q' AND (" +
+        "        nmv.name = 'MNQ'      OR" +
+        "        nmv.name LIKE 'MNQ(%' OR" +
+        "        nmv.name = 'MQ'       OR" +
+        "        nmv.name LIKE 'MQ(%'  OR" +
+        "        nmv.name = 'MHQ'      OR" +
+        "        nmv.name LIKE 'MHQ(%' OR" +
+        "        nmv.name = 'HQ5'      OR" +
+        "        nmv.name LIKE 'HQ5(%') AND" +
+        "    g.river_id = :river_id " +
+        "ORDER BY" +
+        "    g.id";
+
+    protected List<GaugeRange> gauges;
+    protected boolean          isKmUp;
+
+    public GaugeFinder(List<GaugeRange> gauges) {
+        this(gauges, true);
+    }
+
+    public GaugeFinder(
+        List<GaugeRange> gauges,
+        boolean          isKmUp
+    ) {
+        this.gauges = gauges;
+        this.isKmUp = isKmUp;
+    }
+
+    public boolean getIsKmUp() {
+        return isKmUp;
+    }
+
+    public void setIsKmUp(boolean isKmUp) {
+        this.isKmUp = isKmUp;
+    }
+
+
+    /** Find GaugeRange at kilometer. */
+    public GaugeRange find(double km) {
+        for (GaugeRange gauge: gauges) {
+            if (gauge.inside(km)) {
+                return gauge;
+            }
+        }
+        return null;
+    }
+
+    public GaugeRange find(Range range) {
+        return find(isKmUp ? range.start : range.end);
+    }
+
+    public GaugeRange find(int gaugeId) {
+        for (GaugeRange gauge: gauges) {
+            if (gauge.gaugeId == gaugeId) {
+                return gauge;
+            }
+        }
+        return null;
+    }
+
+    public List<GaugeRange> getGauges() {
+        return gauges;
+    }
+
+    public boolean loadDischargeSectors(Session session, int riverId) {
+
+        SQLQuery query = session.createSQLQuery(SQL_DISCHARGE_SECTORS)
+            .addScalar("gauge_id", StandardBasicTypes.INTEGER)
+            .addScalar("name",     StandardBasicTypes.STRING)
+            .addScalar("value",    StandardBasicTypes.DOUBLE);
+
+        query.setInteger("river_id", riverId);
+
+        List<Object []> list = query.list();
+
+        if (list.isEmpty()) {
+            log.warn("River " + riverId + " has no discharge sectors.");
+            return false;
+        }
+
+        GaugeRange gauge = null;
+
+        for (Object [] row: list) {
+            int    gaugeId = (Integer)row[0];
+            String label   = (String) row[1];
+            Double value   = (Double) row[2];
+
+            if (gauge == null || gauge.gaugeId != gaugeId) {
+                if ((gauge = find(gaugeId)) == null) {
+                    log.warn("Cannot find gauge for id " + gaugeId + ".");
+                    continue;
+                }
+            }
+
+            gauge.addMainValue(label, value);
+        }
+
+        for (GaugeRange g: gauges) {
+            g.buildClasses();
+        }
+
+        return true;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/GaugeFinderFactory.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,134 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.dive4elements.river.artifacts.cache.CacheFactory;
+
+import org.dive4elements.river.backend.SessionHolder;
+
+import org.dive4elements.river.model.River;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.Element;
+
+import org.apache.log4j.Logger;
+
+import org.hibernate.SQLQuery;
+import org.hibernate.Session;
+
+import org.hibernate.type.StandardBasicTypes;
+
+/** Get GaugeFinders. */
+public class GaugeFinderFactory
+{
+    private static Logger log = Logger.getLogger(GaugeFinderFactory.class);
+
+    public static final String CACHE_NAME = "gauge-finders";
+
+    public static final String SQL_GAUGES =
+        "SELECT" +
+        "    g.id AS gauge_id," +
+        "    g.name AS name," +
+        "    r.a  AS a," +
+        "    r.b  AS b " +
+        "FROM gauges g" +
+        "    JOIN ranges r ON g.range_id = r.id " +
+        "WHERE" +
+        "    g.river_id = :river_id " +
+        "ORDER BY r.a";
+
+    private static GaugeFinderFactory INSTANCE;
+
+    protected GaugeFinderFactory() {
+    }
+
+    public static synchronized GaugeFinderFactory getInstance() {
+        if (INSTANCE == null) {
+            INSTANCE = new GaugeFinderFactory();
+        }
+
+        return INSTANCE;
+    }
+
+    public GaugeFinder getGaugeFinder(String riverName) {
+        River river = RiverFactory.getRiver(riverName);
+        return river != null
+            ? getGaugeFinder(river.getId(), river.getKmUp())
+            : null;
+    }
+
+    public synchronized GaugeFinder getGaugeFinder(
+        int     riverId,
+        boolean isKmUp
+    ) {
+        Cache cache = CacheFactory.getCache(CACHE_NAME);
+
+        if (cache == null) {
+            return getUncached(riverId, isKmUp);
+        }
+
+        String cacheKey = riverId + "-" + isKmUp;
+        Element element = cache.get(cacheKey);
+
+        if (element != null) {
+            return (GaugeFinder)element.getValue();
+        }
+
+        GaugeFinder finder = getUncached(riverId, isKmUp);
+
+        if (finder != null) {
+            cache.put(new Element(cacheKey, finder));
+        }
+
+        return finder;
+    }
+
+    protected GaugeFinder loadGauges(
+        Session session,
+        int     riverId,
+        boolean isKmUp
+    ) {
+        SQLQuery query = session.createSQLQuery(SQL_GAUGES)
+            .addScalar("gauge_id", StandardBasicTypes.INTEGER)
+            .addScalar("name",     StandardBasicTypes.STRING)
+            .addScalar("a",        StandardBasicTypes.DOUBLE)
+            .addScalar("b",        StandardBasicTypes.DOUBLE);
+
+        query.setInteger("river_id", riverId);
+
+        List<Object []> list = query.list();
+
+        if (list.isEmpty()) {
+            log.warn("River " + riverId + " has no gauges.");
+            return null;
+        }
+
+        List<GaugeRange> gauges = new ArrayList<GaugeRange>();
+
+        for (Object [] row: list) {
+            int    gaugeId = (Integer)row[0];
+            String name    = (String) row[1];
+            double start   = (Double) row[2];
+            double end     = (Double) row[3];
+            GaugeRange gauge = new GaugeRange(start, end, name, gaugeId);
+            gauges.add(gauge);
+        }
+
+        return new GaugeFinder(gauges, isKmUp);
+    }
+
+    protected GaugeFinder getUncached(int riverId, boolean isKmUp) {
+        Session session = SessionHolder.HOLDER.get();
+
+        GaugeFinder finder = loadGauges(session, riverId, isKmUp);
+
+        if (finder == null
+        || !finder.loadDischargeSectors(session, riverId)) {
+            return null;
+        }
+
+        return finder;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/GaugeRange.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,153 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.io.Serializable;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+
+/**
+ * Gauge, km-range, main values.
+ */
+public class GaugeRange
+extends      Range
+{
+    private static Logger log = Logger.getLogger(GaugeRange.class);
+
+    private static final class Sector implements Serializable {
+
+        int    sector;
+        double value;
+
+        Sector(int sector, double value) {
+            this.sector = sector;
+            this.value  = value;
+        }
+    } // class Sector
+
+    protected String name;
+
+    protected int gaugeId;
+
+    /** Certain main value. */
+    protected Map<String, Double> mainValues;
+
+    protected List<Sector> sectors;
+
+
+    public GaugeRange() {
+    }
+
+
+    public GaugeRange(double start, double end, int gaugeId) {
+        this(start, end, null, gaugeId);
+    }
+
+
+    public GaugeRange(
+        double start,
+        double end,
+        String name,
+        int    gaugeId
+    ) {
+        super(start, end);
+        this.name = name;
+        this.gaugeId = gaugeId;
+        mainValues = new HashMap<String, Double>();
+        sectors = new ArrayList<Sector>(3);
+    }
+
+
+    public void addMainValue(String label, Double value) {
+        int idx = label.indexOf('(');
+        if (idx >= 0) {
+            label = label.substring(0, idx);
+        }
+        mainValues.put(label, value);
+    }
+
+
+    protected Double getMainValue(String label) {
+        Double v = mainValues.get(label);
+        if (v == null) {
+            log.warn("Missing main value '"
+                + label + "' for gauge " + gaugeId);
+        }
+        return v;
+    }
+
+
+    public Map<String, Double> getMainValues() {
+        return mainValues;
+    }
+
+
+    public void buildClasses() {
+        Double mnq = getMainValue("MNQ");
+        Double mq  = getMainValue("MQ");
+        Double mhq = getMainValue("MHQ");
+        Double hq5 = getMainValue("HQ5");
+
+        Double [][] pairs = {
+            { mnq,  mq },
+            {  mq, mhq },
+            { hq5, hq5 } };
+
+        for (int c = 0; c < pairs.length; ++c) {
+            Double [] pair = pairs[c];
+            if (pair[0] != null && pair[1] != null) {
+                double value = 0.5*(pair[0] + pair[1]);
+                sectors.add(new Sector(c, value));
+            }
+        }
+    }
+
+
+    public double getSectorBorder(int sector) {
+        for (Sector s: sectors) {
+            if (s.sector == sector) {
+                return s.value;
+            }
+        }
+        return Double.NaN;
+    }
+
+
+    public int classify(double value) {
+        for (Sector sector: sectors) {
+            if (value < sector.value) {
+                return sector.sector;
+            }
+        }
+        return sectors.size();
+    }
+
+
+    public String getName() {
+        return name;
+    }
+
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+
+    public String toString() {
+        StringBuilder sb = new StringBuilder("sectors: [");
+
+        for (int i = 0, S = sectors.size(); i < S; ++i) {
+            if (i > 0) sb.append(", ");
+            Sector s = sectors.get(i);
+            sb.append(s.sector).append(": ").append(s.value);;
+        }
+
+        sb.append("] mainvalues: ").append(mainValues);
+
+        return sb.toString();
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/GaugesFactory.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,70 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.util.List;
+import java.util.ArrayList;
+
+import org.dive4elements.river.backend.SessionHolder;
+import org.dive4elements.river.model.River;
+import org.dive4elements.river.model.Gauge;
+import org.dive4elements.river.model.Range;
+
+import org.hibernate.Session;
+import org.hibernate.Query;
+
+public class GaugesFactory
+{
+    public static List<Gauge> getGauges(River river) {
+        return getGauges(river.getName());
+    }
+
+
+    public static Gauge getGauge(String gaugeName) {
+        Session session = SessionHolder.HOLDER.get();
+        Query query = session.createQuery(
+            "from Gauge where name=:name");
+        query.setParameter("name", gaugeName);
+
+        List<Gauge> res = query.list();
+
+        return res.isEmpty() ? null : res.get(0);
+    }
+
+
+    public static List<Gauge> getGauges(String river) {
+        Session session = SessionHolder.HOLDER.get();
+        Query query = session.createQuery(
+            "from Gauge where river.name=:name");
+        query.setParameter("name", river);
+        return query.list();
+    }
+
+    public static List<Gauge> filterRanges(
+        List<Gauge>     gauges,
+        List<double []> ranges
+    ) {
+        // XXX: Inefficent!
+        ArrayList<Range> rs = new ArrayList<Range>();
+        for (double [] range: ranges) {
+            double a = range[0];
+            double b = range[1];
+            rs.add(new Range(Math.min(a, b), Math.max(a, b), null));
+        }
+        return filter(gauges, rs);
+    }
+
+    public static List<Gauge> filter(List<Gauge> gauges, List<Range> ranges) {
+        // TODO: Make it an HQL filter!
+        ArrayList<Gauge> out = new ArrayList<Gauge>();
+        for (Gauge gauge: gauges) {
+            Range range = gauge.getRange();
+            for (Range cmp: ranges) {
+                if (range.intersects(cmp)) {
+                    out.add(gauge);
+                    break;
+                }
+            }
+        }
+        return out;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/HYKFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,78 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.DataProvider;
+import org.dive4elements.river.artifacts.HYKArtifact;
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+import org.dive4elements.river.model.FastCrossSectionLine;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+
+/**
+ * Trival Facet for HYKs
+ */
+public class HYKFacet
+extends      DataFacet
+implements   FacetTypes {
+
+    /** House logger. */
+    private static Logger logger = Logger.getLogger(HYKFacet.class);
+
+    /** Trivial constructor, set (maybe localized) description. */
+    public HYKFacet(int idx, String description) {
+        super(idx, HYK, description, ComputeType.FEED, null, null);
+    }
+
+
+    /**
+     * Set km from cross section- master to HYKArtifact, then fire up
+     * computation.
+     *
+     * @param art artifact to get data from.
+     * @param context ignored
+     */
+     @Override
+    public Object getData(Artifact art, CallContext context) {
+        logger.debug("HYKFacet.getData");
+
+        String dataKey = CrossSectionFacet.BLACKBOARD_CS_MASTER_DATA;
+
+        List<DataProvider> providers = context.getDataProvider(dataKey);
+        if (providers.size() < 1) {
+            logger.warn("Could not find Cross-Section data provider to get master cs km.");
+            return null;
+        }
+
+        FastCrossSectionLine crossSection = (FastCrossSectionLine) providers.get(0)
+            .provideData(dataKey, null, context);
+
+        if(crossSection == null) {
+            logger.debug("getData: crossSection is null");
+            return null;
+        }
+
+        double km = crossSection.getKm();
+        logger.debug("HYKFacet.getData: Master Cross Section is at km: " + km);
+
+        // Set this km at hyk artifact to be evaluated.
+        HYKArtifact hyk = (HYKArtifact) art;
+        hyk.setKm(km);
+
+        return hyk.compute(context, hash, stateId, type, false);
+    }
+
+
+    /** Do a deep copy. */
+    @Override
+    public Facet deepCopy() {
+        HYKFacet copy = new HYKFacet(this.index, this.description);
+        copy.set(this);
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/HYKFactory.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,211 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.dive4elements.river.artifacts.cache.CacheFactory;
+import org.dive4elements.river.backend.SessionHolder;
+import org.dive4elements.river.model.HYK;
+import org.dive4elements.river.model.HYKFlowZone;
+import org.dive4elements.river.model.HYKFormation;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.Element;
+
+import org.apache.log4j.Logger;
+import org.hibernate.Query;
+import org.hibernate.SQLQuery;
+import org.hibernate.Session;
+import org.hibernate.type.StandardBasicTypes;
+
+
+/**
+ * Factory to access HYKs (hydrographic values).
+ */
+public class HYKFactory
+{
+    private static Logger logger = Logger.getLogger(HYKFactory.class);
+
+    public static String HYK_CACHE_NAME = "hykache";
+
+
+    /** Do not instantiate a HYKFactory. */
+    private HYKFactory() {
+    }
+
+
+    /**
+     * Get List of Zones for given river and km.
+     */
+    public static Object getHYKs(int hykid, double km) {
+        logger.debug("HYKFactory.getHYKs");
+
+        Cache cache = CacheFactory.getCache(HYK_CACHE_NAME);
+
+        String cacheKey;
+
+        if (cache != null) {
+            cacheKey = "" + hykid + "_" + km;
+            Element element = cache.get(cacheKey);
+            if (element != null) {
+                logger.debug("Got hyk from cache");
+                return element.getValue();
+            }
+        }
+        else {
+            cacheKey = null;
+        }
+
+        List<Zone> zones = getZonesUncached(hykid, km);
+
+        if (zones != null && cacheKey != null) {
+            logger.debug("Store hykzones in cache.");
+            Element element = new Element(cacheKey, zones);
+            cache.put(element);
+        }
+
+        return zones;
+    }
+
+
+    /** Return name for hyk with given id. */
+    public static String getHykName(int hykid) {
+        logger.debug("HYKFactory.getHykName " + hykid);
+
+        Session session = SessionHolder.HOLDER.get();
+
+        Query query = session.createQuery(
+            "select description from HYK where id = :hykid ");
+        query.setParameter("hykid", hykid);
+
+        return (String) query.uniqueResult();
+    }
+
+
+    /**
+     * Ask DB for hyk zones.
+     * @param hykid ID of the 'main' HYK to query.
+     * @param km for which to get the hyk-zones.
+     * @return according zones.
+     */
+    public static List<Zone> getZonesUncached(int hykid, double km) {
+        if (logger.isDebugEnabled()) {
+            logger.debug("HYKFactory.getZoneUncached " + hykid + " km " + km);
+        }
+
+        Session session = SessionHolder.HOLDER.get();
+
+        // Find out flow-direction of river.
+        // OPTIMIZE: 1) query kmUp directly 2) merge queries.
+        Query rQuery = session.createQuery("from HYK where id = :hykid");
+        rQuery.setParameter("hykid", hykid);
+        rQuery.setMaxResults(1);
+        HYK hyk = (HYK) rQuery.uniqueResult();
+
+        double flowDir = hyk.getRiver().getKmUp() ? 1 : -1;
+
+        List<HYKFormation> forms = getHYKFormations(hykid, km, flowDir);
+        List<Zone>         zones = new ArrayList<Zone>();
+
+        // Take the first one.
+        if (forms.size() >= 1) {
+            HYKFormation form = forms.get(0);
+            // Create respective zones.
+            for (HYKFlowZone flowZone: form.getZones()) {
+                Zone z = new Zone(flowZone.getA().doubleValue(),
+                    flowZone.getB().doubleValue(),
+                    flowZone.getType().getName());
+                zones.add(z);
+            }
+        }
+
+        return zones;
+    }
+
+
+    protected static List<HYKFormation> getHYKFormations(
+        int        hykid,
+        double     km,
+        double     flowDir
+    ) {
+        Session session = SessionHolder.HOLDER.get();
+
+        String SQL = "SELECT " +
+            "   f.id          AS FID, " +
+            "   f.distance_vl AS DIST, " +
+            "   e.hyk_id      AS HID, " +
+            "   e.km          AS KM " +
+            " FROM hyk_formations f INNER JOIN hyk_entries e " +
+            "   ON e.id = f.hyk_entry_id " +
+            " WHERE e.hyk_id = :hykid " +
+            "   AND :km between " +
+            "     LEAST(e.km, e.km + :flowDir*(f.distance_vl/1000.0+0.001)) " +
+            "   AND " +
+            "     GREATEST(e.km, e.km + :flowDir*(f.distance_vl/1000.0+0.001))";
+
+        SQLQuery sqlQuery = session.createSQLQuery(SQL)
+            .addScalar("FID", StandardBasicTypes.INTEGER)
+            .addScalar("DIST", StandardBasicTypes.DOUBLE)
+            .addScalar("HID", StandardBasicTypes.INTEGER)
+            .addScalar("KM", StandardBasicTypes.DOUBLE);
+
+        sqlQuery.setInteger("hykid", hykid);
+        sqlQuery.setDouble("flowDir", flowDir);
+        sqlQuery.setDouble("km", km);
+
+        logger.debug("HYK SQL: " + sqlQuery.getQueryString());
+
+        List<Object[]> results = sqlQuery.list();
+
+        logger.debug("Found " + results.size() + " HYKFormation IDs in DB.");
+
+        if (results == null || results.isEmpty()) {
+            logger.debug("No HYK found for ID " + hykid + " at km " + km);
+            return new ArrayList<HYKFormation>();
+        }
+
+        Object[] resultSet      = results.get(0);
+        Integer  hykFormationId = (Integer) resultSet[0];
+
+        Query query = session.createQuery("from HYKFormation where id = :id");
+        query.setParameter("id", hykFormationId);
+        query.setMaxResults(1);
+
+        return query.list();
+    }
+
+
+    /** Labeled section. */
+    public static class Zone implements Serializable {
+        /** Lower end of segment. */
+        protected double  from;
+        /** Upper end of segment. */
+        protected double  to;
+        /** The label. */
+        protected String name;
+
+        /** Constructor for labelled section. */
+        public Zone (double from, double to, String name) {
+            this.from = from;
+            this.to   = to;
+            this.name = name;
+        }
+
+        /** Get upper value. */
+        public double getTo() {
+            return to;
+        }
+
+        /** Get lower value. */
+        public double getFrom() {
+            return from;
+        }
+
+        /** Get name (type). */
+        public String getName() {
+            return name;
+        }
+    } // public static class Zone
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/HistoricalDischargeCurveFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,48 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class HistoricalDischargeCurveFacet extends DataFacet {
+
+    public HistoricalDischargeCurveFacet() {
+    }
+
+    public HistoricalDischargeCurveFacet(int index, String name,
+        String description, ComputeType type, String stateID, String hash
+
+    ) {
+        super(index, name, description, type, hash, stateID);
+    }
+
+    @Override
+    public Facet deepCopy() {
+        WaterlevelFacet copy = new WaterlevelFacet();
+        copy.set(this);
+        copy.type = type;
+        copy.hash = hash;
+        copy.stateId = stateId;
+        return copy;
+    }
+
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        CalculationResult res = (CalculationResult) flys.compute(context, hash,
+            stateId, type, false);
+
+        HistoricalDischargeData data = (HistoricalDischargeData) res.getData();
+        WQKms[] wqkms = (WQKms[]) data.getWQs();
+
+        return wqkms[index];
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/HistoricalDischargeData.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,27 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.io.Serializable;
+
+
+public class HistoricalDischargeData implements Serializable {
+
+    private WQKms[] wqs;
+    private WQTimerange[] wqTimeranges;
+
+    public HistoricalDischargeData() {
+    }
+
+    public HistoricalDischargeData(WQTimerange[] wqTimeranges, WQKms[] wqs) {
+        this.wqTimeranges = wqTimeranges;
+        this.wqs = wqs;
+    }
+
+    public WQTimerange[] getWQTimeranges() {
+        return wqTimeranges;
+    }
+
+    public WQ[] getWQs() {
+        return wqs;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/HistoricalDischargeDifferenceFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,63 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+
+/**
+ * Difference of historical discharge curve to ...
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class HistoricalDischargeDifferenceFacet
+extends      HistoricalDischargeFacet
+{
+    private static final Logger logger =
+        Logger.getLogger(HistoricalDischargeDifferenceFacet.class);
+
+
+    public HistoricalDischargeDifferenceFacet(
+        int    index,
+        String name,
+        String desc
+    ) {
+        super(index, name, desc, ComputeType.ADVANCE, null, null);
+    }
+
+
+    public HistoricalDischargeDifferenceFacet(
+        int         index,
+        String      name,
+        String      description,
+        ComputeType type,
+        String      stateID,
+        String      hash
+
+    ) {
+        super(index, name, description, type, hash, stateID);
+    }
+
+
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        if (logger.isDebugEnabled()) {
+            logger.debug("Get data for historical discharge difference curves" +
+                " at index: " + index + " / stateId: " + stateId);
+        }
+
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        CalculationResult res = (CalculationResult)
+            flys.compute(context, hash, stateId, type,  false);
+
+        HistoricalDischargeData data = (HistoricalDischargeData) res.getData();
+        WQTimerange[] wqts = (WQTimerange[]) data.getWQTimeranges();
+
+        return wqts[index];
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/HistoricalDischargeFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,74 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class HistoricalDischargeFacet extends DataFacet {
+
+    private static final Logger logger =
+        Logger.getLogger(HistoricalDischargeFacet.class);
+
+
+    public HistoricalDischargeFacet() {
+    }
+
+
+    public HistoricalDischargeFacet(int index, String name, String desc) {
+        super(index, name, desc, ComputeType.ADVANCE, null, null);
+    }
+
+
+    public HistoricalDischargeFacet(
+        int         index,
+        String      name,
+        String      description,
+        ComputeType type,
+        String      stateID,
+        String      hash
+
+    ) {
+        super(index, name, description, type, hash, stateID);
+    }
+
+
+    @Override
+    public Facet deepCopy() {
+        WaterlevelFacet copy = new WaterlevelFacet();
+        copy.set(this);
+        copy.type    = type;
+        copy.hash    = hash;
+        copy.stateId = stateId;
+        return copy;
+    }
+
+
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        if (logger.isDebugEnabled()) {
+            logger.debug("Get data for historical discharge curves at index: " +
+                index + " / stateId: " + stateId);
+        }
+
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        CalculationResult res = (CalculationResult)
+            flys.compute(context, hash, stateId, type,  false);
+
+        HistoricalDischargeData data = (HistoricalDischargeData) res.getData();
+        WQTimerange[] wqts = (WQTimerange[]) data.getWQTimeranges();
+
+        return wqts[index];
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/HistoricalDischargeWQFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,40 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class HistoricalDischargeWQFacet extends DataFacet {
+
+    private double value;
+
+    public HistoricalDischargeWQFacet() {
+    }
+
+    public HistoricalDischargeWQFacet(int index, String name,
+        String description, ComputeType type, String hash, String stateId,
+        double value) {
+
+        super(index, name, description, type, hash, stateId);
+        this.value = value;
+    }
+
+    @Override
+    public Facet deepCopy() {
+        HistoricalDischargeWQFacet copy = new HistoricalDischargeWQFacet();
+        copy.set(this);
+        copy.value = value;
+        return copy;
+    }
+
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        return value;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/HistoricalWQKms.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,23 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.dive4elements.river.model.TimeInterval;
+
+
+public class HistoricalWQKms extends WQKms {
+
+    private TimeInterval timeInterval;
+
+    public HistoricalWQKms() {
+    }
+
+    public HistoricalWQKms(double[] kms, double[] qs, double[] ws, String name,
+        TimeInterval timeInterval) {
+        super(kms, qs, ws, name);
+        this.timeInterval = timeInterval;
+    }
+
+    public TimeInterval getTimeInterval() {
+        return timeInterval;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/HistoricalWQTimerange.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,86 @@
+package org.dive4elements.river.artifacts.model;
+
+import gnu.trove.TDoubleArrayList;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+
+/**
+ * A subclass of WQTimerange that stores besides W, Q and Timerange values
+ * another double value (difference to something).
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class HistoricalWQTimerange extends WQTimerange {
+
+    public static class HistoricalTimerangeItem extends TimerangeItem {
+        public double diff;
+
+        public HistoricalTimerangeItem (Timerange timerange, double q, double w, double diff) {
+            super(timerange, q, w);
+            this.diff = diff;
+        }
+
+        public double[] get(double[] wq) {
+            if (wq.length >= 3) {
+                wq[0] = w;
+                wq[1] = q;
+                wq[2] = diff;
+            }
+            else if (wq.length >= 2) {
+                return super.get(wq);
+            }
+
+            return wq;
+        }
+    }
+
+    protected TDoubleArrayList diffs;
+
+
+    public HistoricalWQTimerange(String name) {
+        super(name);
+
+        diffs = new TDoubleArrayList();
+    }
+
+
+    public void add(double w, double q, double diff, Timerange t) {
+        ws.add(w);
+        qs.add(q);
+        timeranges.add(t);
+        diffs.add(diff);
+    }
+
+
+    /**
+     * This method requires a 3dim double array for <i>res</i>!
+     */
+    @Override
+    public double[] get(int idx, double[] res) {
+        res[0] = ws.getQuick(idx);
+        res[1] = qs.getQuick(idx);
+        res[2] = diffs.getQuick(idx);
+
+        return res;
+    }
+
+
+    public double[] getDiffs() {
+        return diffs.toNativeArray();
+    }
+
+    @Override
+    public List<TimerangeItem> sort() {
+        ArrayList<TimerangeItem> items = new ArrayList<TimerangeItem>(timeranges.size());
+        for (int i = 0, n = size(); i < n; i++) {
+            items.add(new HistoricalTimerangeItem(getTimerange(i), getQ(i), getW(i), diffs.get(i)));
+        }
+
+        Collections.sort(items);
+        return items;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/InfoldingColumns.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,78 @@
+package org.dive4elements.river.artifacts.model;
+
+/** A pretty naive pointwise algorithm to find out the columns
+ *  of a WSTValueTable which imfold ("umhuellen") a set of WQKMs
+ *  in terms of Q.
+ *  A better implemention would exploit the fact that the
+ *  Qs normally are constant for a while along km. This would
+ *  reduce the runtime complexity to only a few Q spans instead
+ *  of the pointwise evaluation.
+ */
+public class InfoldingColumns
+{
+    private QRangeTree.QuickQFinder [] qFinders;
+
+    private boolean [] infoldingColumns;
+
+    public InfoldingColumns() {
+    }
+
+    public InfoldingColumns(WstValueTable.Column [] columns) {
+
+        qFinders = new QRangeTree.QuickQFinder[columns.length];
+        for (int i = 0; i < qFinders.length; ++i) {
+            qFinders[i] = columns[i].getQRangeTree().new QuickQFinder();
+        }
+
+        infoldingColumns = new boolean[columns.length];
+    }
+
+    public boolean [] getInfoldingColumns() {
+        return infoldingColumns;
+    }
+
+    public void markInfoldingColumns(QKms [] qkms) {
+        for (QKms qk: qkms) {
+            markInfoldingColumns(qk);
+        }
+    }
+
+    public void markInfoldingColumns(QKms qkms) {
+        int N = qkms.size();
+        int C = qFinders.length-1;
+        for (int i = 0; i < N; ++i) {
+            double km       = qkms.getKm(i);
+            double q        = qkms.getQ(i);
+            double above    =  Double.MAX_VALUE;
+            double below    = -Double.MAX_VALUE;
+            int    aboveIdx = -1;
+            int    belowIdx = -1;
+
+            for (int j = C; j >= 0; --j) {
+                double qc = qFinders[j].findQ(km);
+                if (Double.isNaN(qc)) {
+                    continue;
+                }
+                if (qc <= q) {
+                    if (qc > below) {
+                        below    = qc;
+                        belowIdx = j;
+                    }
+                }
+                else if (qc < above) { // qc > q
+                    above    = qc;
+                    aboveIdx = j;
+                }
+            }
+
+            if (aboveIdx != -1) {
+                infoldingColumns[aboveIdx] = true;
+            }
+
+            if (belowIdx != -1) {
+                infoldingColumns[belowIdx] = true;
+            }
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/LayerInfo.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,163 @@
+package org.dive4elements.river.artifacts.model;
+
+
+public class LayerInfo {
+
+    protected String name;
+    protected String type;
+    protected String directory;
+    protected String data;
+    protected String connection;
+    protected String connectionType;
+    protected String extent;
+    protected String srid;
+    protected String group;
+    protected String groupTitle;
+    protected String title;
+    protected String style;
+    protected String filter;
+    protected String labelItem;
+
+
+    public LayerInfo() {
+    }
+
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+
+    public String getName() {
+        return name;
+    }
+
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+
+    public String getType() {
+        return type;
+    }
+
+
+    public void setDirectory(String directory) {
+        this.directory = directory;
+    }
+
+
+    public String getDirectory() {
+        return directory;
+    }
+
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+
+    public String getData() {
+        return data;
+    }
+
+
+    public void setConnection(String connection) {
+        this.connection = connection;
+    }
+
+
+    public String getConnection() {
+        return connection;
+    }
+
+
+    public void setConnectionType(String connectionType) {
+        this.connectionType = connectionType;
+    }
+
+
+    public String getConnectionType() {
+        return connectionType;
+    }
+
+
+    public void setGroup(String group) {
+        this.group = group;
+    }
+
+
+    public String getGroup() {
+        return group;
+    }
+
+
+    public void setGroupTitle(String groupTitle) {
+        this.groupTitle = groupTitle;
+    }
+
+
+    public String getGroupTitle() {
+        return groupTitle;
+    }
+
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+
+    public String getTitle() {
+        return title;
+    }
+
+
+    public void setExtent(String extent) {
+        this.extent = extent;
+    }
+
+
+    public String getExtent() {
+        return extent;
+    }
+
+
+    public void setSrid(String srid) {
+        this.srid = srid;
+    }
+
+
+    public String getSrid() {
+        return srid;
+    }
+
+
+    public void setStyle(String style) {
+        this.style = style;
+    }
+
+
+    public String getStyle() {
+        return style;
+    }
+
+
+    public void setFilter(String filter) {
+        this.filter = filter;
+    }
+
+
+    public String getFilter() {
+        return filter;
+    }
+
+    public void setLabelItem(String labelItem) {
+        this.labelItem = labelItem;
+    }
+
+    public String getLabelItem() {
+        return labelItem;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/LocationProvider.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,103 @@
+package org.dive4elements.river.artifacts.model;
+
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.Element;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.river.model.Annotation;
+import org.dive4elements.river.model.FastAnnotations;
+
+import org.dive4elements.river.artifacts.cache.CacheFactory;
+
+
+/** Make FastAnnotations (db unbound) available. */
+public class LocationProvider {
+
+    private static final Logger log =
+        Logger.getLogger(LocationProvider.class);
+
+
+    public static final String CACHE_KEY = "location-provider";
+
+    public static final String PREFIX = "lp-";
+
+
+    private LocationProvider() {
+    }
+
+    public static String getLocation(String river, double km) {
+
+        FastAnnotations fas = getAnnotations(river, km);
+
+        FastAnnotations.Annotation an = fas.findByKm(km);
+
+        return an != null ? an.getPosition() : null;
+    }
+
+    public static FastAnnotations getAnnotations(String river) {
+        return getAnnotations(river, Double.MAX_VALUE);
+    }
+
+    protected static FastAnnotations getAnnotations(String river, double km) {
+        // TODO issue880: Make annotations available _per type_
+
+        Cache cache = CacheFactory.getCache(CACHE_KEY);
+
+        if (cache == null) {
+            return uncachedAnnotations(river, km);
+        }
+
+        String key = PREFIX + river;
+
+        Element element = cache.get(key);
+
+        if (element != null) {
+            return (FastAnnotations)element.getValue();
+        }
+
+        FastAnnotations fas = uncachedAnnotations(river, Double.MAX_VALUE);
+
+        cache.put(new Element(key, fas));
+
+        return fas;
+    }
+
+    protected static FastAnnotations uncachedAnnotations(
+        String river,
+        double km
+    ) {
+        if (km != Double.MAX_VALUE) {
+            // XXX Fake it by using a standard Annotation.
+
+            Annotation annotation =
+                AnnotationsFactory.getAnnotation(river, km);
+
+            if (annotation != null) {
+                FastAnnotations.Annotation fa =
+                    new FastAnnotations.Annotation(
+                        km, Double.NaN,
+                        annotation.getPosition().getValue(), null, null,
+                        Double.NaN, Double.NaN);
+                return new FastAnnotations(
+                    new FastAnnotations.Annotation [] { fa });
+            }
+
+            return new FastAnnotations(new FastAnnotations.Annotation[0]);
+        }
+
+        long startTime = System.currentTimeMillis();
+
+        FastAnnotations fas = new FastAnnotations(river);
+
+        long stopTime = System.currentTimeMillis();
+
+        if (log.isDebugEnabled()) {
+            log.debug("Loading locations took " +
+                (stopTime-startTime)/1000f + " secs.");
+        }
+
+        return fas;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/MainValuesQFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,154 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.DataProvider;
+
+import org.dive4elements.artifactdatabase.state.DefaultFacet;
+
+import org.dive4elements.river.artifacts.MainValuesArtifact;
+import org.dive4elements.river.artifacts.math.Linear;
+import org.dive4elements.river.jfree.FLYSAnnotation;
+import org.dive4elements.river.jfree.StickyAxisAnnotation;
+
+import org.dive4elements.river.exports.DurationCurveGenerator;
+
+
+/**
+ * Facet to show Main Q Values.
+ * TODO Join with W implementation.
+ */
+public class MainValuesQFacet
+extends      DefaultFacet
+implements   FacetTypes {
+
+    /** Own logger. */
+    private static Logger logger = Logger.getLogger(MainValuesQFacet.class);
+
+    /** Do we want MainValues at Gauge (not interpolated)? */
+    protected boolean isAtGauge;
+
+
+    /** Trivial Constructor. */
+    public MainValuesQFacet(String name, String description, boolean atGauge) {
+        this.description = description;
+        this.name        = name;
+        this.index       = 0;
+        this.isAtGauge   = atGauge;
+    }
+
+
+    /**
+     * Set the hit-point in Q where a line drawn from the axis would hit the
+     * curve in WQDay (if hit).
+     * Employ linear interpolation.
+     */
+    protected static void setHitPoint(WQDay wqday, StickyAxisAnnotation annotation) {
+        int idx = 0;
+        float q = annotation.getPos();
+        boolean qIncreases = wqday.getQ(0) < wqday.getQ(wqday.size()-1);
+        if (qIncreases) {
+            while (idx < wqday.size() && wqday.getQ(idx) < q) {
+                idx++;
+            }
+        }
+        else {
+            idx = wqday.size() -1;
+            while (idx > 0 && wqday.getQ(idx) > q) {
+                idx--;
+            }
+        }
+
+        double day = 0d;
+        int mod = (qIncreases) ? -1 : +1;
+        if (idx != 0 && idx <= wqday.size()-1) {
+            day = Linear.linear(q, wqday.getQ(idx +mod), wqday.getQ(idx),
+                wqday.getDay(idx+mod), wqday.getDay(idx));
+            annotation.setHitPoint((float)day);
+        }
+        else {
+            logger.debug("StickyAnnotation does not hit wqday curve");
+        }
+    }
+
+
+    /**
+     * Returns the data this facet requires.
+     *
+     * @param artifact the owner artifact.
+     * @param context  the CallContext (ignored).
+     *
+     * @return the data.
+     */
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        MainValuesArtifact mvArtifact = (MainValuesArtifact) artifact;
+
+        List<NamedDouble>          qs = mvArtifact.getMainValuesQ(isAtGauge);
+        List<StickyAxisAnnotation> xy = new ArrayList<StickyAxisAnnotation>();
+
+        WQDay wqdays = null;
+        List<DataProvider> providers = context.
+            getDataProvider(DurationCurveFacet.BB_DURATIONCURVE);
+        if (providers.size() < 1) {
+            logger.warn("Could not find durationcurve data provider.");
+        }
+        else {
+            wqdays = (WQDay) providers.get(0).provideData(
+                DurationCurveFacet.BB_DURATIONCURVE,
+                null,
+                context);
+        }
+
+        // Rather specific case, Q-Annotations at a maybe second yaxis.
+        StickyAxisAnnotation annotation = null;
+        if (this.name.equals(DURATION_MAINVALUES_Q)) {
+            for (NamedDouble q: qs) {
+                annotation =
+                    new StickyAxisAnnotation(
+                        q.getName(),
+                        (float) q.getValue(),
+                        StickyAxisAnnotation.SimpleAxis.Y_AXIS,
+                        DurationCurveGenerator.YAXIS.Q.idx);
+                xy.add(annotation);
+                if (wqdays != null) {
+                    setHitPoint(wqdays, annotation);
+                }
+            }
+        }
+        else {
+            for (NamedDouble q: qs) {
+                annotation =
+                    new StickyAxisAnnotation(
+                        q.getName(),
+                        (float) q.getValue(),
+                        StickyAxisAnnotation.SimpleAxis.X_AXIS);
+                xy.add(annotation);
+                if (wqdays != null) {
+                    setHitPoint(wqdays, annotation);
+                }
+            }
+        }
+
+        return new FLYSAnnotation(description, xy);
+    }
+
+
+    /**
+     * Create a deep copy of this Facet.
+     * @return a deep copy.
+     */
+    @Override
+    public MainValuesQFacet deepCopy() {
+        MainValuesQFacet copy = new MainValuesQFacet(this.name,
+            description, this.isAtGauge);
+        copy.set(this);
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/MainValuesWFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,133 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.DataProvider;
+
+import org.dive4elements.artifactdatabase.state.DefaultFacet;
+
+import org.dive4elements.river.artifacts.MainValuesArtifact;
+import org.dive4elements.river.artifacts.math.Linear;
+import org.dive4elements.river.jfree.FLYSAnnotation;
+import org.dive4elements.river.jfree.StickyAxisAnnotation;
+
+
+/**
+ * Facet to show Main W Values.
+ */
+public class MainValuesWFacet
+extends      DefaultFacet
+implements   FacetTypes {
+
+    /** Own logger. */
+    private static Logger logger = Logger.getLogger(MainValuesWFacet.class);
+
+    /** Do we want MainValues at Gauge (not interpolated)? */
+    protected boolean isAtGauge;
+
+    /** Trivial Constructor. */
+    public MainValuesWFacet(String name, String description, boolean atGauge) {
+        this.description = description;
+        this.name = name;
+        this.index = 0;
+        this.isAtGauge = atGauge;
+    }
+
+
+    /**
+     * Set the hit-point in W where a line drawn from the axis would hit the
+     * curve in WQDay (if hit).
+     * Employ linear interpolation.
+     */
+    protected static void setHitPoint(WQDay wqday, StickyAxisAnnotation annotation) {
+        int idx = 0;
+        float w = annotation.getPos();
+        boolean wIncreases = wqday.getW(0) < wqday.getW(wqday.size()-1);
+        if (wIncreases) {
+            while (idx < wqday.size() && wqday.getW(idx) < w) {
+                idx++;
+            }
+        }
+        else {
+            idx = wqday.size() -1;
+            while (idx > 0 && wqday.getW(idx) > w) {
+                idx--;
+            }
+        }
+
+        double day = 0d;
+        int mod = (wIncreases) ? -1 : +1;
+        if (idx != 0 && idx < wqday.size()-1-mod) {
+            day = Linear.linear(w, wqday.getW(idx +mod), wqday.getW(idx),
+                wqday.getDay(idx+mod), wqday.getDay(idx));
+            annotation.setHitPoint((float)day);
+        }
+        else {
+            logger.debug("StickyAnnotation does not hit wqday curve");
+        }
+    }
+
+
+    /**
+     * Returns the data this facet provides.
+     *
+     * @param artifact the owner artifact.
+     * @param context  the CallContext (ignored).
+     *
+     * @return the data.
+     */
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        MainValuesArtifact mvArtifact = (MainValuesArtifact) artifact;
+
+        List<NamedDouble> ws = mvArtifact.getMainValuesW(isAtGauge);
+        List<StickyAxisAnnotation> xy = new ArrayList<StickyAxisAnnotation>();
+
+        // Find whether a duration curve is on the blackboard.
+        WQDay wqdays = null;
+        List<DataProvider> providers = context.
+            getDataProvider(DurationCurveFacet.BB_DURATIONCURVE);
+        if (providers.size() < 1) {
+            logger.warn("Could not find durationcurve data provider.");
+        }
+        else {
+            wqdays = (WQDay) providers.get(0).provideData(
+                DurationCurveFacet.BB_DURATIONCURVE,
+                null,
+                context);
+        }
+
+        for (NamedDouble w: ws) {
+            StickyAxisAnnotation annotation =
+                new StickyAxisAnnotation(
+                    w.getName(),
+                    (float) w.getValue(),
+                    StickyAxisAnnotation.SimpleAxis.Y_AXIS);
+            xy.add(annotation);
+            if (wqdays != null) {
+                setHitPoint(wqdays, annotation);
+            }
+        }
+
+        return new FLYSAnnotation(description, xy);
+    }
+
+
+    /**
+     * Create a deep copy of this Facet.
+     * @return a deep copy.
+     */
+    @Override
+    public MainValuesWFacet deepCopy() {
+        MainValuesWFacet copy = new MainValuesWFacet(this.name,
+            description, this.isAtGauge);
+        copy.set(this);
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/ManagedDomFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,174 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import org.dive4elements.artifacts.ArtifactNamespaceContext;
+
+
+/**
+ * Use an Element (DOM) to store the information about a facet.
+ * The intent of this facet type is to represent a facet
+ * stored in an Collection attribute. Different facets can have different
+ * attributes that we need to parse, but the only thing ManagedFacets need
+ * to do, is to adjust the attributes "active" and "position". So, those
+ * values are set directly on the Element, the other attributes aren't
+ * touched.
+ */
+public class ManagedDomFacet extends ManagedFacet {
+
+    protected Element facet;
+
+    public ManagedDomFacet(Element facet) {
+        super(null, -1, null, null, -1, -1, -1);
+
+        this.facet = facet;
+    }
+
+
+    @Override
+    public int getIndex() {
+        if (this.index < 0) {
+            String index = facet.getAttributeNS(
+                ArtifactNamespaceContext.NAMESPACE_URI, "index");
+
+            if (index != null && index.length() > 0) {
+                this.index = Integer.parseInt(index);
+            }
+        }
+
+        return this.index;
+    }
+
+
+    @Override
+    public String getName() {
+        if (this.name == null || this.name.length() == 0) {
+            String name = facet.getAttributeNS(
+                ArtifactNamespaceContext.NAMESPACE_URI, "facet");
+
+            this.name = name;
+        }
+
+        return this.name;
+    }
+
+
+    @Override
+    public String getDescription() {
+        if (this.description == null || this.description.length() == 0) {
+            String description = facet.getAttributeNS(
+                ArtifactNamespaceContext.NAMESPACE_URI, "description");
+
+            this.description = description;
+        }
+
+        return this.description;
+    }
+
+
+    @Override
+    public int getPosition() {
+        if (this.position < 0) {
+            String position = facet.getAttributeNS(
+                ArtifactNamespaceContext.NAMESPACE_URI,
+                "pos");
+
+            if (position != null && position.length() > 0) {
+                this.position = Integer.parseInt(position);
+            }
+        }
+
+        return this.position;
+    }
+
+
+    @Override
+    public void setPosition(int position) {
+        this.position = position;
+
+        // TODO Evaluate whether other set/getAttributes also need
+        // to use the NAMESPACE_PREFIX.
+        facet.setAttributeNS(
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX + ":" + "pos",
+            String.valueOf(position));
+    }
+
+
+    @Override
+    public int getActive() {
+        if (this.active < 0) {
+            String active = facet.getAttributeNS(
+                ArtifactNamespaceContext.NAMESPACE_URI, "active");
+
+            if (active != null && active.length() > 0) {
+                this.active = Integer.parseInt(active);
+            }
+        }
+
+        return this.active;
+    }
+
+
+    @Override
+    public void setActive(int active) {
+        this.active = active;
+
+        facet.setAttributeNS(
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            "art:active",
+            String.valueOf(active));
+    }
+
+
+    @Override
+    public int getVisible() {
+        if (this.visible < 0) {
+            String visible = facet.getAttributeNS(
+                ArtifactNamespaceContext.NAMESPACE_URI, "visible");
+
+            if (visible != null && visible.length() > 0) {
+                this.visible = Integer.parseInt(visible);
+            }
+        }
+
+        return this.visible;
+    }
+
+
+    @Override
+    public void setVisible(int visible) {
+        this.visible = visible;
+
+        facet.setAttributeNS(
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            "visible",
+            String.valueOf(getVisible()));
+    }
+
+
+    @Override
+    public String getArtifact() {
+        if (this.uuid == null || this.uuid.length() == 0) {
+            String uuid = facet.getAttributeNS(
+                ArtifactNamespaceContext.NAMESPACE_URI, "artifact");
+
+            this.uuid = uuid;
+        }
+
+        return this.uuid;
+    }
+
+
+    /**
+     * Import into document.
+     * @param doc Document to be imported to.
+     */
+    @Override
+    public Node toXML(Document doc) {
+        return doc.importNode(facet, true);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/ManagedFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,130 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import org.dive4elements.artifactdatabase.state.DefaultFacet;
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifacts.ArtifactNamespaceContext;
+import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator;
+
+
+/**
+ * Facet with user-supplied theme-control-information (pos in list,
+ * active/disabled etc) attached.
+ */
+public class ManagedFacet extends DefaultFacet implements Comparable {
+
+    /** The uuid of the owner artifact. */
+    protected String uuid;
+
+    /** A property that determines the position of this facet. */
+    protected int position;
+
+    /** A property that determines if this facet is active or not. */
+    protected int active;
+
+    /** A property that determines if this facet is visible or not. */
+    protected int visible;
+
+    public ManagedFacet() {
+    }
+
+    public ManagedFacet(String name, int index, String desc, String uuid,
+        int pos, int active, int visible) {
+        super(index, name, desc);
+
+        this.uuid = uuid;
+        this.position = pos;
+        this.active = active;
+        this.visible = visible;
+    }
+
+    /**
+     * Sets position (will be merged to position in ThemeList).
+     */
+    public void setPosition(int pos) {
+        this.position = pos;
+    }
+
+    public int getPosition() {
+        return position;
+    }
+
+    public void setActive(int active) {
+        this.active = active;
+    }
+
+    public int getActive() {
+        return active;
+    }
+
+    public void setVisible(int visible) {
+        this.visible = visible;
+    }
+
+    public int getVisible() {
+        return visible;
+    }
+
+    /**
+     * Get uuid of related artifact.
+     *
+     * @return uuid of related artifact.
+     */
+    public String getArtifact() {
+        return uuid;
+    }
+
+    public Node toXML(Document doc) {
+        ElementCreator ec = new ElementCreator(doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element facet = ec.create("theme");
+        ec.addAttr(facet, "artifact", getArtifact(), true);
+        ec.addAttr(facet, "facet", getName(), true);
+        ec.addAttr(facet, "pos", String.valueOf(getPosition()), true);
+        ec.addAttr(facet, "active", String.valueOf(getActive()), true);
+        ec.addAttr(facet, "index", String.valueOf(getIndex()), true);
+        ec.addAttr(facet, "description", getDescription(), true);
+        ec.addAttr(facet, "visible", String.valueOf(getVisible()), true);
+
+        return facet;
+    }
+
+    public void set(ManagedFacet other) {
+        uuid = other.uuid;
+        position = other.position;
+        active = other.active;
+    }
+
+    @Override
+    public Facet deepCopy() {
+        ManagedFacet copy = new ManagedFacet();
+        copy.set((DefaultFacet) this);
+        copy.set((ManagedFacet) this);
+        return copy;
+    }
+
+    @Override
+    public int compareTo(Object o) {
+        if (!(o instanceof ManagedFacet)) {
+            return -1;
+        }
+
+        ManagedFacet other = (ManagedFacet) o;
+
+        if (position < other.position) {
+            return -1;
+        }
+        else if (position > other.position) {
+            return 1;
+        }
+        else {
+            return 0;
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/ManagedFacetAdapter.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,73 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import org.dive4elements.artifacts.ArtifactNamespaceContext;
+
+import org.dive4elements.artifactdatabase.state.DefaultFacet;
+import org.dive4elements.artifactdatabase.state.Facet;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator;
+
+
+public class ManagedFacetAdapter extends ManagedFacet {
+
+    protected Facet facet;
+
+    public ManagedFacetAdapter() {
+    }
+
+
+    protected Logger logger = Logger.getLogger(ManagedFacetAdapter.class);
+
+    public ManagedFacetAdapter(
+        Facet   facet,
+        String  uuid,
+        int     pos,
+        int     active,
+        int     visible
+    ) {
+        super(
+            facet.getName(),
+            facet.getIndex(),
+            facet.getDescription(),
+            uuid,
+            pos,
+            active,
+            visible);
+
+        this.facet = facet;
+    }
+
+
+    @Override
+    public Node toXML(Document doc) {
+        ElementCreator ec = new ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element e = (Element) facet.toXML(doc);
+        ec.addAttr(e, "artifact", getArtifact(), true);
+        ec.addAttr(e, "facet", getName(), true);
+        ec.addAttr(e, "pos", String.valueOf(getPosition()), true);
+        ec.addAttr(e, "active", String.valueOf(getActive()), true);
+        ec.addAttr(e, "visible", String.valueOf(getVisible()), true);
+
+        return e;
+    }
+
+    @Override
+    public Facet deepCopy() {
+        ManagedFacetAdapter copy = new ManagedFacetAdapter();
+        copy.set((DefaultFacet)this);
+        copy.set((ManagedFacet)this);
+        copy.facet = facet.deepCopy();
+        return facet;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/ManualPointsFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,64 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.ManualPointsArtifact;
+
+import org.dive4elements.artifactdatabase.state.DefaultFacet;
+import org.dive4elements.artifactdatabase.state.Facet;
+
+
+/**
+ * Facet to access ManualPoints that where added by user.
+ */
+public class ManualPointsFacet
+extends      DefaultFacet
+{
+    /** Logger for this class. */
+    private static final Logger logger = Logger.getLogger(ManualPointsFacet.class);
+
+
+    /**
+     * Trivial Constructor.
+     */
+    public ManualPointsFacet() {
+    }
+
+
+    /**
+     * Trivial Constructor for a ManualPointsFacet.
+     *
+     * @param index       Database-Index to use.
+     * @param name        Name (~type) of Facet.
+     * @param description Description of Facet.
+     */
+    public ManualPointsFacet(int index, String name, String description) {
+        super(index, name, description);
+    }
+
+
+    /**
+     * Get List of ManualPoints for river from Artifact.
+     *
+     * @param artifact (ManualPoints-)Artifact to query for list of ManualPoints.
+     * @param context  Ignored.
+     */
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        ManualPointsArtifact pointsArtifact = (ManualPointsArtifact) artifact;
+        return pointsArtifact.getPointsData(this.name);
+    }
+
+
+    /** Do a deep copy. */
+    @Override
+    public Facet deepCopy() {
+        ManualPointsFacet copy = new ManualPointsFacet();
+        copy.set(this);
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/MapserverStyle.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,152 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class MapserverStyle {
+
+    public static class Clazz {
+        protected List<ClazzItem> items;
+        protected String    name;
+
+        public Clazz(String name) {
+            this.name  = name;
+            this.items = new ArrayList<ClazzItem>();
+        }
+
+        public void addItem(ClazzItem item) {
+            if (item != null) {
+                items.add(item);
+            }
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append("CLASS\n");
+            sb.append("NAME \"" + name + "\"\n");
+
+            for (ClazzItem item: items) {
+                item.toString(sb);
+            }
+
+            sb.append("END\n");
+
+            return sb.toString();
+        }
+    }
+
+    public interface ClazzItem {
+        void toString(StringBuilder sb);
+    }
+
+    public static class Style implements ClazzItem {
+        protected String color;
+        protected String outlinecolor;
+        protected String symbol;
+        protected int    size;
+
+        public void setColor(String color) {
+            this.color = color;
+        }
+
+        public void setOutlineColor(String outlinecolor) {
+            this.outlinecolor = outlinecolor;
+        }
+
+        public void setSize(int size) {
+            this.size = size;
+        }
+
+        public void setSymbol(String symbol) {
+            if (symbol != null && symbol.length() > 0) {
+                this.symbol = symbol;
+            }
+        }
+
+        public void toString(StringBuilder sb) {
+            sb.append("STYLE\n");
+            sb.append("WIDTH " + String.valueOf(size) + "\n");
+
+            if (outlinecolor != null) {
+                sb.append("OUTLINECOLOR " + outlinecolor + "\n");
+            }
+
+            if (color != null) {
+                sb.append("COLOR " + color + "\n");
+            }
+
+            if (symbol != null) {
+                sb.append("SYMBOL '" + symbol + "'\n");
+            }
+
+            sb.append("END\n");
+        }
+    } // end of Style
+
+    public static class Label implements ClazzItem {
+        protected String color;
+        protected int    size;
+
+        public void setColor(String color) {
+            this.color = color;
+        }
+
+        public void setSize(int size) {
+            this.size = size;
+        }
+
+        @Override
+        public void toString(StringBuilder sb) {
+            sb.append("LABEL\n");
+            sb.append("ANGLE auto\n");
+            sb.append("SIZE " + String.valueOf(size) + "\n");
+            sb.append("COLOR " + color + "\n");
+            sb.append("TYPE truetype\n");
+            sb.append("FONT DefaultFont\n");
+            sb.append("POSITION ur\n");
+            sb.append("OFFSET 2 2\n");
+            sb.append("END\n");
+        }
+    }
+
+    public static class Expression implements ClazzItem {
+        protected String value;
+
+        public Expression(String value) {
+            this.value = value;
+        }
+
+        @Override
+        public void toString(StringBuilder sb) {
+            sb.append("EXPRESSION " + value);
+            sb.append("\n");
+        }
+    }
+
+
+    protected List<Clazz> classes;
+
+
+    public MapserverStyle() {
+        classes = new ArrayList<Clazz>();
+    }
+
+    public void addClazz(Clazz clazz) {
+        if (clazz != null) {
+            classes.add(clazz);
+        }
+    }
+
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+
+        for (Clazz clazz: classes) {
+            sb.append(clazz.toString());
+        }
+
+        return sb.toString();
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/MiddleBedHeightCalculation.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,212 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.common.utils.DateUtils;
+
+import org.dive4elements.river.model.BedHeightEpoch;
+import org.dive4elements.river.model.BedHeightEpochValue;
+import org.dive4elements.river.model.BedHeightSingle;
+import org.dive4elements.river.model.BedHeightSingleValue;
+import org.dive4elements.river.model.TimeInterval;
+
+import org.dive4elements.river.artifacts.access.BedHeightAccess;
+
+
+public class MiddleBedHeightCalculation extends Calculation {
+
+    private static final Logger logger =
+        Logger.getLogger(MiddleBedHeightCalculation.class);
+
+
+    public CalculationResult calculate(BedHeightAccess access) {
+        logger.info("MiddleBedHeightCalculation.calculate");
+
+        int[] singleIds = access.getBedHeightSingleIDs();
+        int[] epochIds  = access.getBedHeightEpochIDs();
+
+
+        if (logger.isDebugEnabled()) {
+            Artifact artifact = access.getArtifact();
+
+            logger.debug("Artifact '" + artifact.identifier() + "' contains:");
+            if (singleIds != null) {
+                logger.debug("   " + singleIds.length + " single bedheight ids");
+            }
+
+            if (epochIds != null) {
+                logger.debug("   " + epochIds.length + " epoch bedheight ids");
+            }
+        }
+
+        List<BedHeightSingle> singles = getSingles(access, singleIds);
+        List<BedHeightEpoch>  epochs  = getEpochs(access, epochIds);
+
+        return buildCalculationResult(access, singles, epochs);
+    }
+
+
+    protected List<BedHeightSingle> getSingles(
+        BedHeightAccess access,
+        int[] ids
+    ) {
+        List<BedHeightSingle> singles = new ArrayList<BedHeightSingle>();
+
+        for (int id: ids) {
+            BedHeightSingle s = BedHeightSingle.getBedHeightSingleById(id);
+
+            if (s != null) {
+                singles.add(s);
+            }
+            else {
+                logger.warn("Cannot find Single by id: " + id);
+                // TODO ADD WARNING
+            }
+        }
+
+        return singles;
+    }
+
+
+    protected List<BedHeightEpoch> getEpochs(
+        BedHeightAccess access,
+        int[] ids
+    ) {
+        List<BedHeightEpoch> epochs = new ArrayList<BedHeightEpoch>();
+
+        for (int id: ids) {
+            BedHeightEpoch e = BedHeightEpoch.getBedHeightEpochById(id);
+
+            if (e != null) {
+                epochs.add(e);
+            }
+            else {
+                logger.warn("Cannot find Epoch by id: " + id);
+                // TODO ADD WARNING
+            }
+        }
+
+        return epochs;
+    }
+
+
+    protected CalculationResult buildCalculationResult(
+        BedHeightAccess       access,
+        List<BedHeightSingle> singles,
+        List<BedHeightEpoch>  epochs
+    ) {
+        logger.info("MiddleBedHeightCalculation.buildCalculationResult");
+
+        double kmLo = access.getLowerKM();
+        double kmHi = access.getUpperKM();
+
+        List<MiddleBedHeightData> data = new ArrayList<MiddleBedHeightData>();
+
+        for (BedHeightSingle single: singles) {
+            MiddleBedHeightData d = prepareSingleData(single, kmLo, kmHi);
+
+            if (d != null) {
+                data.add(d);
+            }
+        }
+
+        for (BedHeightEpoch epoch: epochs) {
+            MiddleBedHeightData d = prepareEpochData(epoch, kmLo, kmHi);
+
+            if (d != null) {
+                data.add(d);
+            }
+        }
+
+        logger.debug("Calculation results in " + data.size() + " data objects.");
+
+        return new CalculationResult((MiddleBedHeightData[])
+            data.toArray(new MiddleBedHeightData[data.size()]), this);
+    }
+
+
+    protected MiddleBedHeightData prepareSingleData(
+        BedHeightSingle single,
+        double kmLo,
+        double kmHi
+    ) {
+        logger.debug("Prepare data for single: " + single.getDescription());
+
+        List<BedHeightSingleValue> values =
+            BedHeightSingleValue.getBedHeightSingleValues(single, kmLo, kmHi);
+
+        MiddleBedHeightData data = new MiddleBedHeightData(
+            single.getYear(),
+            single.getYear(),
+            single.getEvaluationBy(),
+            single.getDescription());
+
+        for (BedHeightSingleValue value: values) {
+            if (value.getHeight() != null) {
+                data.addAll(value.getStation().doubleValue(),
+                    value.getHeight().doubleValue(),
+                    value.getUncertainty().doubleValue(),
+                    value.getSoundingWidth().doubleValue(),
+                    value.getDataGap().doubleValue(),
+                    value.getWidth().doubleValue(),
+                    false);
+             }
+            else {
+                data.addAll(value.getStation().doubleValue(),
+                    0,
+                    0,
+                    0,
+                    0,
+                    0,
+                    true);
+            }
+        }
+
+        logger.debug("Single contains " + values.size() + " values");
+
+        return data;
+    }
+
+
+    /** Create MiddleBedHeightData to return. */
+    protected MiddleBedHeightData prepareEpochData(
+        BedHeightEpoch epoch,
+        double kmLo,
+        double kmHi
+    ) {
+        logger.debug("Prepare data for epoch: " + epoch.getDescription());
+
+        TimeInterval ti = epoch.getTimeInterval();
+
+        List<BedHeightEpochValue> values =
+            BedHeightEpochValue.getBedHeightEpochValues(epoch, kmLo, kmHi);
+
+        MiddleBedHeightData data = new MiddleBedHeightData(
+            DateUtils.getYearFromDate(ti.getStartTime()),
+            DateUtils.getYearFromDate(ti.getStopTime()),
+            epoch.getEvaluationBy(),
+            epoch.getDescription()
+        );
+
+        for (BedHeightEpochValue value: values) {
+            data.addKM(value.getStation().doubleValue());
+            if (value.getHeight() != null) {
+                data.addMiddleHeight(value.getHeight().doubleValue());
+                data.addIsEmpty(false);
+            }
+            else {
+                data.addMiddleHeight(Double.NaN);
+                data.addIsEmpty(true);
+            }
+        }
+
+        logger.debug("Epoch contains " + values.size() + " values");
+
+        return data;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/MiddleBedHeightData.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,185 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.io.Serializable;
+
+import java.util.ArrayList;
+
+import gnu.trove.TDoubleArrayList;
+
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.resources.Resources;
+
+import org.apache.log4j.Logger;
+
+
+public class MiddleBedHeightData implements Serializable {
+
+    /** Very private logger. */
+    private static final Logger logger = Logger.getLogger(MiddleBedHeightData.class);
+
+    public static final String I18N_SINGLE_NAME = "facet.bedheight_middle.single";
+    public static final String I18N_EPOCH_NAME  = "facet.bedheight_middle.epoch";
+
+    private int    startYear;
+    private int    endYear;
+    private String evaluatedBy;
+    private String description;
+
+    private TDoubleArrayList km;
+    private TDoubleArrayList middleHeight;
+    private TDoubleArrayList uncertainty;
+    private TDoubleArrayList soundingWidth;
+    private TDoubleArrayList dataGap;
+    private TDoubleArrayList width;
+    private ArrayList empty;
+
+
+    protected MiddleBedHeightData(int start, int end, String eval, String desc) {
+        this.startYear   = start;
+        this.endYear     = end;
+        this.evaluatedBy = eval;
+        this.description = desc;
+
+        this.km            = new TDoubleArrayList();
+        this.middleHeight  = new TDoubleArrayList();
+        this.uncertainty   = new TDoubleArrayList();
+        this.soundingWidth = new TDoubleArrayList();
+        this.dataGap       = new TDoubleArrayList();
+        this.width         = new TDoubleArrayList();
+        this.empty         = new ArrayList();
+    }
+
+    public void addAll(double station, double height, double uncertainty,
+        double soundingWidth, double dataGap, double width, boolean isEmpty) {
+        addKM(station);
+        addMiddleHeight(height);
+        addUncertainty(uncertainty);
+        addSoundingWidth(soundingWidth);
+        addDataGap(dataGap);
+        addWidth(width);
+        addIsEmpty(isEmpty);
+    }
+
+
+    public int getStartYear() {
+        return startYear;
+    }
+
+    public int getEndYear() {
+        return endYear;
+    }
+
+    public String getEvaluatedBy() {
+        return evaluatedBy;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+
+    protected void addKM(double km) {
+        this.km.add(km);
+    }
+
+    public double getKM(int idx) {
+        return km.get(idx);
+    }
+
+    protected void addMiddleHeight(double middleHeight) {
+        this.middleHeight.add(middleHeight);
+    }
+
+    public double getMiddleHeight(int idx) {
+        return middleHeight.get(idx);
+    }
+
+    protected void addUncertainty(double uncertainty) {
+        this.uncertainty.add(uncertainty);
+    }
+
+    public double getUncertainty(int idx) {
+        return uncertainty.get(idx);
+    }
+
+    protected void addSoundingWidth(double soundingWidth) {
+        this.soundingWidth.add(soundingWidth);
+    }
+
+    public double getSoundingWidth(int idx) {
+        return soundingWidth.get(idx);
+    }
+
+    protected void addDataGap(double gap) {
+        this.dataGap.add(gap);
+    }
+
+    public double getDataGap(int idx) {
+        return dataGap.get(idx);
+    }
+
+    protected void addIsEmpty(boolean empty) {
+        this.empty.add(empty);
+    }
+
+    public boolean isEmpty(int idx) {
+        return (Boolean) empty.get(idx);
+    }
+
+
+    protected void addWidth(double width) {
+        this.width.add(width);
+    }
+
+    public double getWidth(int idx) {
+        return width.get(idx);
+    }
+
+    public int size() {
+        return km.size();
+    }
+
+
+    /**
+     * Get the points, ready to be drawn
+     * @return [[km1, km2,...],[height1,height2,...]]
+     */
+    public double[][] getMiddleHeightsPoints() {
+        double[][] points = new double[2][size()];
+
+        for (int i = 0, n = size(); i < n; i++) {
+            if (isEmpty(i)) {
+                points[0][i] = getKM(i);
+                points[1][i] = Double.NaN;
+            }
+            else {
+                points[0][i] = getKM(i);
+                points[1][i] = getMiddleHeight(i);
+            }
+        }
+
+        return points;
+    }
+
+
+    public String getSoundingName(CallContext context) {
+        if (getStartYear() == getEndYear()) {
+            return Resources.getMsg(
+                context.getMeta(),
+                I18N_SINGLE_NAME,
+                I18N_SINGLE_NAME,
+                new Object[] { getStartYear() }
+            );
+        }
+        else {
+            return Resources.getMsg(
+                context.getMeta(),
+                I18N_EPOCH_NAME,
+                I18N_EPOCH_NAME,
+                new Object[] { getStartYear(), getEndYear() }
+            );
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/MiddleBedHeightFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,65 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+import org.apache.log4j.Logger;
+
+
+/**
+ * Facet of a MiddleBedHeight curve.
+ */
+public class MiddleBedHeightFacet extends DataFacet {
+
+    private static Logger logger = Logger.getLogger(MiddleBedHeightFacet.class);
+
+
+    public MiddleBedHeightFacet() {
+        // required for clone operation deepCopy()
+    }
+
+
+    public MiddleBedHeightFacet(
+        int         idx,
+        String      name,
+        String      description,
+        ComputeType type,
+        String      stateId,
+        String      hash
+    ) {
+        super(idx, name, description, type, hash, stateId);
+    }
+
+
+    public Object getData(Artifact artifact, CallContext context) {
+        logger.debug("Get data for middle bed height at index: " + index);
+
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        CalculationResult res = (CalculationResult)
+            flys.compute(context, hash, stateId, type, false);
+
+        MiddleBedHeightData[] data = (MiddleBedHeightData[]) res.getData();
+
+        return data[index];
+    }
+
+
+    /** Copy deeply. */
+    @Override
+    public Facet deepCopy() {
+        MiddleBedHeightFacet copy = new MiddleBedHeightFacet();
+        copy.set(this);
+        copy.type    = type;
+        copy.hash    = hash;
+        copy.stateId = stateId;
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/Module.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,25 @@
+package org.dive4elements.river.artifacts.model;
+
+/**
+ * Represents a Module as is is loaded from the config
+ */
+public class Module {
+
+    private String name;
+    private boolean selected;
+
+    public Module(String name, boolean selected) {
+        this.name = name;
+        this.selected = selected;
+    }
+
+    public String getName() {
+        return this.name;
+    }
+
+    public boolean isSelected() {
+        return this.selected;
+    }
+}
+
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 tw=80:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/NamedDouble.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,30 @@
+package org.dive4elements.river.artifacts.model;
+
+/**
+ * Implementation of a <String,double> pair.
+ */
+public class NamedDouble
+extends      NamedObjectImpl
+{
+    protected double value;
+
+
+    /**
+     * @param name  name for the given value.
+     * @param value value.
+     */
+    public NamedDouble(String name, double value) {
+        super(name);
+        this.value = value;
+    }
+
+
+    /**
+     * Get the value.
+     * @return the value.
+     */
+    public double getValue() {
+        return this.value;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/NamedObject.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,19 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.io.Serializable;
+
+
+/**
+ * This class represents an object that has a name. The default case would be to
+ * inherit from this class.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface NamedObject
+extends          Serializable
+{
+    void setName(String name);
+
+    String 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/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/NamedObjectImpl.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,28 @@
+package org.dive4elements.river.artifacts.model;
+
+public class NamedObjectImpl
+implements   NamedObject
+{
+    /** The name of this object.*/
+    protected String name;
+
+    public NamedObjectImpl() {
+    }
+
+    public NamedObjectImpl(String name) {
+        this.name = name;
+    }
+
+
+    @Override
+    public void setName(String name) {
+        this.name = name;
+    }
+
+
+    @Override
+    public String getName() {
+        return name;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/Parameters.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,336 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.dive4elements.river.artifacts.math.Linear;
+
+import org.dive4elements.river.utils.DoubleUtil;
+
+import gnu.trove.TDoubleArrayList;
+
+import java.io.Serializable;
+
+import org.apache.log4j.Logger;
+
+public class Parameters
+implements   Serializable
+{
+    private static Logger log = Logger.getLogger(Parameters.class);
+
+    public interface Visitor {
+
+        void visit(double [] row);
+
+    } // interface Visitor
+
+    public static final double EPSILON = 1e-4;
+
+    protected String []           columnNames;
+    protected TDoubleArrayList [] columns;
+
+    public Parameters() {
+    }
+
+    public Parameters(String [] columnNames) {
+        if (columnNames == null || columnNames.length < 1) {
+            throw new IllegalArgumentException("columnNames too short.");
+        }
+        this.columnNames = columnNames;
+        columns = new TDoubleArrayList[columnNames.length];
+        for (int i = 0; i < columns.length; ++i) {
+            columns[i] = new TDoubleArrayList();
+        }
+    }
+
+    public int columnIndex(String name) {
+        for (int i = 0; i < columnNames.length; ++i) {
+            if (columnNames[i].equals(name)) {
+                return i;
+            }
+        }
+        if (log.isDebugEnabled()) {
+            log.debug("columnIndex: " + name + " not found in columnNames");
+        }
+        return -1;
+    }
+
+    public int newRow() {
+
+        int N = columns[0].size();
+
+        for (int i = 0; i < columns.length; ++i) {
+            columns[i].add(Double.NaN);
+        }
+
+        return N;
+    }
+
+    public double get(int row, int index) {
+        return columns[index].getQuick(row);
+    }
+
+    public double get(int i, String columnName) {
+        int index = columnIndex(columnName);
+        return index >= 0
+            ? columns[index].getQuick(i)
+            : Double.NaN;
+    }
+
+    public void set(int row, int index, double value) {
+        columns[index].setQuick(row, value);
+    }
+
+    public void set(int i, String columnName, double value) {
+        int idx = columnIndex(columnName);
+        if (idx >= 0) {
+            columns[idx].setQuick(i, value);
+        }
+    }
+
+    public boolean set(int row, int [] indices, double [] values) {
+        boolean invalid = false;
+        for (int i = 0; i < indices.length; ++i) {
+            double v = values[i];
+            if (Double.isNaN(v)) {
+                invalid = true;
+            }
+            else {
+                columns[indices[i]].setQuick(row, v);
+            }
+        }
+        return invalid;
+    }
+
+    public boolean set(int row, String [] names, double [] values) {
+        boolean success = true;
+        for (int i = 0; i < names.length; ++i) {
+            int idx = columnIndex(names[i]);
+            if (idx >= 0) {
+                columns[idx].setQuick(row, values[i]);
+            }
+            else {
+                success = false;
+            }
+        }
+        return success;
+    }
+
+    public int size() {
+        return columns[0].size();
+    }
+
+    public int getNumberColumns() {
+        return columnNames.length;
+    }
+
+    public String [] getColumnNames() {
+        return columnNames;
+    }
+
+    public void removeNaNs() {
+        DoubleUtil.removeNaNs(columns);
+    }
+
+    public int [] columnIndices(String [] columns) {
+        int [] indices = new int[columns.length];
+        for (int i = 0; i < columns.length; ++i) {
+            indices[i] = columnIndex(columns[i]);
+        }
+        return indices;
+    }
+
+    public double getValue(int row, String column) {
+        int idx = columnIndex(column);
+        return idx >= 0
+            ? columns[idx].getQuick(row)
+            : Double.NaN;
+    }
+
+    public double [] get(int row, String [] columns) {
+        return get(row, columns, new double[columns.length]);
+    }
+
+    public double [] get(int row, String [] columns, double [] values) {
+        for (int i = 0; i < columns.length; ++i) {
+            int idx = columnIndex(columns[i]);
+            values[i] = idx < 0
+                ? Double.NaN
+                : this.columns[idx].getQuick(row);
+        }
+
+        return values;
+    }
+
+    public void get(int row, int [] columnIndices, double [] values) {
+        for (int i = 0; i < columnIndices.length; ++i) {
+            int index = columnIndices[i];
+            values[i] = index >= 0 && index < columns.length
+                ? columns[index].getQuick(row)
+                : Double.NaN;
+        }
+    }
+
+    public int binarySearch(String columnName, double value) {
+        return binarySearch(columnIndex(columnName), value);
+    }
+
+    /**
+     * Performes a binary search in the column identified by its
+     * index.
+     * @return Index of found element or negative insertion point (shifted by one)
+     */
+    public int binarySearch(int columnIndex, double value) {
+        TDoubleArrayList column = columns[columnIndex];
+        return column.binarySearch(value);
+    }
+
+    public int binarySearch(String columnName, double value, double epsilon) {
+        return binarySearch(columnIndex(columnName), value, epsilon);
+    }
+
+    public int binarySearch(int columnIndex, double value, double epsilon) {
+        if (epsilon < 0d) epsilon = -epsilon;
+        double vl = value - epsilon;
+        double vh = value + epsilon;
+
+        TDoubleArrayList column = columns[columnIndex];
+        int lo = 0, hi = column.size()-1;
+        while (hi >= lo) {
+            int mid = (lo + hi) >> 1;
+            double v = column.getQuick(mid);
+            if      (v < vl) lo = mid + 1;
+            else if (v > vh) hi = mid - 1;
+            else             return mid;
+        }
+
+        return -(lo + 1);
+    }
+
+    public double [] interpolate(int columnIndex, double key) {
+        return interpolate(columnIndex, key, new double[columns.length]);
+    }
+
+    public double [] interpolate(String columnName, double key) {
+        return interpolate(
+            columnIndex(columnName), key, new double[columns.length]);
+    }
+
+    public double [] interpolate(
+        String    columnName,
+        double    key,
+        double [] values
+    ) {
+        return interpolate(columnIndex(columnName), key, values);
+    }
+
+    public double [] interpolate(
+        int       columnIndex,
+        double    key,
+        double [] values
+    ) {
+        int row = binarySearch(columnIndex, key, EPSILON);
+
+        if (row >= 0) { // direct hit
+            for (int i = 0; i < values.length; ++i) {
+                values[i] = columns[i].getQuick(row);
+            }
+        }
+        else {
+            row = -row - 1;
+            if (row < 1 || row >= size()) {
+                return null;
+            }
+            double v1 = columns[columnIndex].getQuick(row-1);
+            double v2 = columns[columnIndex].getQuick(row);
+            double factor = Linear.factor(key, v1, v2);
+            for (int i = 0; i < values.length; ++i) {
+                values[i] = Linear.weight(
+                    factor,
+                    columns[i].getQuick(row-1),
+                    columns[i].getQuick(row));
+            }
+        }
+        return values;
+    }
+
+
+    public double [] interpolate(
+        String    keyName,
+        double    key,
+        String [] columnNames
+    ) {
+        int keyIndex = columnIndex(keyName);
+        return keyIndex < 0
+            ? null
+            : interpolate(keyIndex, key, columnNames);
+    }
+
+    public double [] interpolate(
+        int       keyIndex,
+        double    key,
+        String [] columnNames
+    ) {
+        int row = binarySearch(keyIndex, key, EPSILON);
+
+        if (row >= 0) { // direct match
+            double [] values = new double[columnNames.length];
+            for (int i = 0; i < values.length; ++i) {
+                int ci = columnIndex(columnNames[i]);
+                values[i] = ci < 0
+                    ? Double.NaN
+                    : columns[ci].getQuick(row);
+            }
+            return values;
+        }
+
+        row = -row - 1;
+        if (row < 1 || row >= size()) {
+            log.debug("interpolate: row is out of bounds");
+            return null;
+        }
+
+        double v1 = columns[keyIndex].getQuick(row-1);
+        double v2 = columns[keyIndex].getQuick(row);
+        double factor = Linear.factor(key, v1, v2);
+
+        double [] values = new double[columnNames.length];
+
+        for (int i = 0; i < values.length; ++i) {
+            int ci = columnIndex(columnNames[i]);
+            values[i] = ci < 0
+                ? Double.NaN
+                : Linear.weight(
+                    factor,
+                    columns[ci].getQuick(row-1),
+                    columns[ci].getQuick(row));
+        }
+
+        return values;
+    }
+
+    public boolean isSorted(String columnName) {
+        return isSorted(columnIndex(columnName));
+    }
+
+    public boolean isSorted(int columnIndex) {
+        TDoubleArrayList column = columns[columnIndex];
+        for (int i = 1, N = column.size(); i < N; ++i) {
+            if (column.getQuick(i-1) > column.getQuick(i)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public void visit(Visitor visitor) {
+        visit(visitor, new double[columns.length]);
+    }
+
+    public void visit(Visitor visitor, double [] data) {
+        for (int i = 0, R = size(); i < R; ++i) {
+            for (int j = 0; j < data.length; ++j) {
+                data[j] = columns[j].getQuick(i);
+            }
+            visitor.visit(data);
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/QKms.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,18 @@
+package org.dive4elements.river.artifacts.model;
+
+import gnu.trove.TDoubleArrayList;
+
+public interface QKms
+extends          NamedObject
+{
+    int size();
+
+    double getKm(int index);
+
+    double getQ(int index);
+
+    TDoubleArrayList allKms();
+
+    TDoubleArrayList allQs();
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/QKmsImpl.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,82 @@
+package org.dive4elements.river.artifacts.model;
+
+import gnu.trove.TDoubleArrayList;
+
+import org.dive4elements.river.utils.DoubleUtil;
+
+public class QKmsImpl
+extends      NamedObjectImpl
+implements   QKms
+{
+    protected TDoubleArrayList kms;
+    protected TDoubleArrayList qs;
+
+    public QKmsImpl() {
+        super("");
+        kms = new TDoubleArrayList();
+        qs  = new TDoubleArrayList();
+    }
+
+    public QKmsImpl(String name) {
+        super(name);
+        kms = new TDoubleArrayList();
+        qs  = new TDoubleArrayList();
+    }
+
+
+    public QKmsImpl(int capacity) {
+        super("");
+        kms = new TDoubleArrayList(capacity);
+        qs  = new TDoubleArrayList(capacity);
+    }
+
+
+    public QKmsImpl(TDoubleArrayList kms, TDoubleArrayList qs) {
+        this(kms, qs, "");
+    }
+
+    public QKmsImpl(
+        TDoubleArrayList kms,
+        TDoubleArrayList qs,
+        String           name
+    ) {
+        super(name);
+        this.kms = kms;
+        this.qs  = qs;
+    }
+
+    public void add(double km, double q) {
+        kms.add(km);
+        qs .add(q);
+    }
+
+    @Override
+    public double getQ(int index) {
+        return qs.getQuick(index);
+    }
+
+
+    @Override
+    public double getKm(int index) {
+        return kms.getQuick(index);
+    }
+
+    @Override
+    public int size() {
+        return kms.size();
+    }
+
+    @Override
+    public TDoubleArrayList allKms() {
+        return kms;
+    }
+
+    @Override
+    public TDoubleArrayList allQs() {
+        return qs;
+    }
+
+    public void removeNaNs() {
+        DoubleUtil.removeNaNs(new TDoubleArrayList [] { kms, qs });
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/QRangeTree.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,399 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.io.Serializable;
+
+import java.util.List;
+import java.util.ArrayList;
+
+import org.apache.log4j.Logger;
+
+public class QRangeTree
+implements   Serializable
+{
+    private static Logger log = Logger.getLogger(QRangeTree.class);
+
+    public static final double EPSILON = 1e-4;
+
+    public static class Node
+    implements          Serializable
+    {
+        Node left;
+        Node right;
+        Node prev;
+        Node next;
+
+        double a;
+        double b;
+        double q;
+
+        public Node() {
+        }
+
+        public Node(double a, double b, double q) {
+            this.a = a;
+            this.b = b;
+            this.q = q;
+        }
+
+        protected final double interpolatePrev(double pos) {
+            /*
+            f(prev.b) = prev.q
+            f(a)      = q
+
+            prev.q = m*prev.b + n
+            q      = m*a      + n <=> n = q - m*a
+
+            q - prev.q = m*(a - prev.b)
+
+            m = (q - prev.q)/(a - prev.b) # a != prev.b
+            */
+
+            if (a == prev.b) {
+                return 0.5*(q + prev.q);
+            }
+            double m = (q - prev.q)/(a - prev.b);
+            double n = q - m*a;
+            return m*pos + n;
+        }
+
+        protected final double interpolateNext(double pos) {
+            /*
+            f(next.a) = next.q
+            f(b)      = q
+
+            next.q = m*next.a + n
+            q      = m*b      + n <=> n = q - m*b
+
+            q - next.q = m*(b - next.a)
+            m = (q - next.q)/(b - next.a) # b != next.a
+            */
+
+            if (b == next.a) {
+                return 0.5*(q + next.q);
+            }
+            double m = (q - next.q)/(b - next.a);
+            double n = q - m*b;
+            return m*pos + n;
+        }
+
+        public double findQ(double pos) {
+
+            Node current = this;
+            for (;;) {
+                if (pos < current.a) {
+                    if (current.left != null) {
+                        current = current.left;
+                        continue;
+                    }
+                    return current.prev != null
+                        ? current.interpolatePrev(pos)
+                        : Double.NaN;
+                }
+                if (pos > current.b) {
+                    if (current.right != null) {
+                        current = current.right;
+                        continue;
+                    }
+                    return current.next != null
+                        ? current.interpolateNext(pos)
+                        : Double.NaN;
+                }
+                return current.q;
+            }
+        }
+
+        public Node findNode(double pos) {
+            Node current = this;
+            while (current != null) {
+                if (pos < current.a) {
+                    current = current.left;
+                }
+                else if (pos > current.b) {
+                    current = current.right;
+                }
+                return current;
+            }
+            return null;
+        }
+
+        public boolean contains(double c) {
+            return c >= a && c <= b;
+        }
+    } // class Node
+
+    /** Class to cache the last found tree leaf in a search for Q.
+     *  Its likely that a neighbored pos search
+     *  results in using the same leaf node. So
+     *  caching this leaf will minimize expensive
+     *  tree traversals.
+     *  Modeled as inner class because the QRangeTree
+     *  itself is a shared data structure.
+     *  Using this class omits interpolation between
+     *  leaves.
+     */
+    public final class QuickQFinder {
+
+        private Node last;
+
+        public QuickQFinder() {
+        }
+
+        public double findQ(double pos) {
+            if (last != null && last.contains(pos)) {
+                return last.q;
+            }
+            last = QRangeTree.this.findNode(pos);
+            return last != null ? last.q : Double.NaN;
+        }
+
+        public double [] findQs(double [] kms, Calculation report) {
+            return findQs(kms, new double[kms.length], report);
+        }
+
+        public double [] findQs(
+            double []    kms,
+            double []   qs,
+            Calculation report
+        ) {
+            for (int i = 0; i < kms.length; ++i) {
+                if (Double.isNaN(qs[i] = findQ(kms[i]))) {
+                    report.addProblem(kms[i], "cannot.find.q");
+                }
+            }
+            return qs;
+        }
+    } // class QuickQFinder
+
+    protected Node root;
+
+    public QRangeTree() {
+    }
+
+    public static final class AccessQAB {
+        private int startIndex;
+
+        public AccessQAB(int startIndex) {
+            this.startIndex = startIndex;
+        }
+
+        public Double getQ(Object [] row) {
+            return (Double)row[startIndex];
+        }
+
+        public Double getA(Object [] row) {
+            return (Double)row[startIndex+1];
+        }
+
+        public Double getB(Object [] row) {
+            return (Double)row[startIndex+2];
+        }
+    }
+
+    public static final AccessQAB WITH_COLUMN    = new AccessQAB(1);
+    public static final AccessQAB WITHOUT_COLUMN = new AccessQAB(0);
+
+    /** wstQRanges need to be sorted by range.a */
+    public QRangeTree(List<Object []> qRanges, int start, int stop) {
+        this(qRanges, WITH_COLUMN, start, stop);
+    }
+
+    public QRangeTree(
+        List<Object []> qRanges,
+        AccessQAB       accessQAB,
+        int             start,
+        int             stop
+    ) {
+        if (stop <= start) {
+            return;
+        }
+
+        int N = stop-start;
+
+        List<Node> nodes = new ArrayList<Node>(N);
+
+        Node last = null;
+
+        for (int i = 0; i < N; ++i) {
+            Object [] qRange = qRanges.get(start + i);
+            Double q = accessQAB.getQ(qRange);
+            Double a = accessQAB.getA(qRange);
+            Double b = accessQAB.getB(qRange);
+
+            double av = a != null ? a.doubleValue() : -Double.MAX_VALUE;
+            double bv = b != null ? b.doubleValue() :  Double.MAX_VALUE;
+            double qv = q.doubleValue();
+
+            // If nodes are directly neighbored and Qs are the same
+            // join them.
+            if (last != null
+            && Math.abs(last.b - av) < EPSILON
+            && Math.abs(last.q - qv) < EPSILON) {
+                last.b = bv;
+            }
+            else {
+                nodes.add(last = new Node(av, bv, qv));
+            }
+        }
+
+        if (log.isDebugEnabled()) {
+            log.debug("Before/after nodes join: " +
+                N + "/" + nodes.size());
+        }
+
+        root = wireTree(nodes);
+    }
+
+    protected static Node wireTree(List<Node> nodes) {
+        int N = nodes.size();
+        for (int i = 0; i < N; ++i) {
+            Node node = nodes.get(i);
+            if (i > 0  ) node.prev = nodes.get(i-1);
+            if (i < N-1) node.next = nodes.get(i+1);
+        }
+
+        return buildTree(nodes, 0, N-1);
+    }
+
+    protected static Node buildTree(List<Node> nodes, int lo, int hi) {
+
+        if (lo > hi) {
+            return null;
+        }
+
+        int mid = (lo + hi) >> 1;
+        Node parent = nodes.get(mid);
+
+        parent.left  = buildTree(nodes, lo, mid-1);
+        parent.right = buildTree(nodes, mid+1, hi);
+
+        return parent;
+    }
+
+    public double averageQ() {
+        double sum = 0d;
+        int n = 0;
+        for (Node node = head(); node != null; node = node.next) {
+            sum += node.q;
+            ++n;
+        }
+        return sum/n;
+    }
+
+    public double maxQ() {
+        double max = -Double.MAX_VALUE;
+        for (Node node = head(); node != null; node = node.next) {
+            if (node.q > max) {
+                max = node.q;
+            }
+        }
+        return max;
+    }
+
+    public double findQ(double pos) {
+        return root != null ? root.findQ(pos) : Double.NaN;
+    }
+
+    public Node findNode(double pos) {
+        return root != null ? root.findNode(pos) : null;
+    }
+
+    protected Node head() {
+        Node head = root;
+        while (head.left != null) {
+            head = head.left;
+        }
+        return head;
+    }
+
+    public boolean intersectsQRange(double qMin, double qMax) {
+        if (qMin > qMax) {
+            double t = qMin;
+            qMin = qMax;
+            qMax = t;
+        }
+        for (Node curr = head(); curr != null; curr = curr.next) {
+            if (curr.q >= qMin || curr.q <= qMax) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public List<Range> findSegments(double a, double b) {
+        if (a > b) { double t = a; a = b; b = t; }
+        return findSegments(new Range(a, b));
+    }
+
+    public List<Range> findSegments(Range range) {
+        List<Range> segments = new ArrayList<Range>();
+
+        // Linear scan should be good enough here.
+        for (Node curr = head(); curr != null; curr = curr.next) {
+            if (!range.disjoint(curr.a, curr.b)) {
+                Range r = new Range(curr.a, curr.b);
+                if (r.clip(range)) {
+                    segments.add(r);
+                }
+            }
+        }
+
+        return segments;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        inorder(root, sb);
+        return sb.toString();
+    }
+
+    protected static void inorder(Node node, StringBuilder sb) {
+        if (node != null) {
+            inorder(node.left, sb);
+            sb.append('[')
+              .append(node.a)
+              .append(", ")
+              .append(node.b)
+              .append(": ")
+              .append(node.q)
+              .append(']');
+            inorder(node.right, sb);
+        }
+    }
+
+    private static final String name(Object o) {
+        return String.valueOf(System.identityHashCode(o) & 0xffffffffL);
+    }
+
+    public String toGraph() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("subgraph c");
+        sb.append(name(this));
+        sb.append(" {\n");
+        if (root != null) {
+            java.util.Deque<Node> stack = new java.util.ArrayDeque<Node>();
+            stack.push(root);
+            while (!stack.isEmpty()) {
+                Node current = stack.pop();
+                String name = "n" + name(current);
+                sb.append(name);
+                sb.append(" [label=\"");
+                sb.append(current.a).append(", ").append(current.b);
+                sb.append(": ").append(current.q).append("\"]\n");
+                if (current.left != null) {
+                    String leftName = name(current.left);
+                    sb.append(name).append(" -- n").append(leftName).append("\n");
+                    stack.push(current.left);
+                }
+                if (current.right != null) {
+                    String rightName = name(current.right);
+                    sb.append(name).append(" -- n").append(rightName).append("\n");
+                    stack.push(current.right);
+                }
+            }
+        }
+        sb.append("}\n");
+        return sb.toString();
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/QSectorFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,71 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.QSectorArtifact;
+
+import org.dive4elements.artifactdatabase.state.DefaultFacet;
+import org.dive4elements.artifactdatabase.state.Facet;
+
+
+/**
+ * Facet to access QSector that where added by user.
+ */
+public class QSectorFacet
+extends      DefaultFacet
+{
+    /** Logger for this class. */
+    private static final Logger logger = Logger.getLogger(QSectorFacet.class);
+
+
+    /**
+     * Trivial Constructor.
+     */
+    public QSectorFacet() {
+    }
+
+
+    /**
+     * Trivial Constructor for a QSectorFacet.
+     *
+     * @param index       Database-Index to use.
+     * @param name        Name (~type) of Facet.
+     * @param description Description of Facet.
+     */
+    public QSectorFacet(int index, String name, String description) {
+        super(index, name, description);
+    }
+
+
+    /**
+     * Get List of QSector for river from Artifact.
+     *
+     * @param artifact (QSector-)Artifact to query for list of QSector.
+     * @param context  Ignored.
+     */
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        QSectorArtifact qsectorArtifact = (QSectorArtifact) artifact;
+        if (qsectorArtifact == null || context == null ||
+            context.getContextValue("currentKm") == null) {
+            logger.error("No artifact, context or currentKm in QSectorFacet");
+            return null;
+        }
+        double currentKm =
+            ((Double)context.getContextValue("currentKm")).doubleValue();
+        return qsectorArtifact.getQSectors(currentKm, context);
+    }
+
+
+    /** Do a deep copy. */
+    @Override
+    public Facet deepCopy() {
+        QSectorFacet copy = new QSectorFacet();
+        copy.set(this);
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/QW.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,35 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.io.Serializable;
+
+public class QW
+implements   Serializable
+{
+    protected double q;
+    protected double w;
+
+    public QW() {
+    }
+
+    public QW(double q, double w) {
+        this.q = q;
+        this.w = w;
+    }
+
+    public double getQ() {
+        return q;
+    }
+
+    public void setQ(double q) {
+        this.q = q;
+    }
+
+    public double getW() {
+        return w;
+    }
+
+    public void setW(double w) {
+        this.w = w;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/QWDDateRange.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,24 @@
+package org.dive4elements.river.artifacts.model;
+
+
+import java.io.Serializable;
+
+import org.dive4elements.river.artifacts.model.fixings.QWD;
+
+public class QWDDateRange
+implements   Serializable
+{
+
+    public QWD qwd;
+    public DateRange dateRange;
+
+    public QWDDateRange(QWD qwd, DateRange dr) {
+        this.qwd = qwd;
+        this.dateRange = dr;
+    }
+
+    public QWD getQWD() {
+        return qwd;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/Range.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,65 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.io.Serializable;
+
+/** A range from ... to .*/
+public class Range implements Serializable {
+
+    public static final double EPSILON = 1e-5;
+
+    protected double start;
+    protected double end;
+
+    public Range() {
+    }
+
+    public Range(Range other) {
+        start = other.start;
+        end   = other.end;
+    }
+
+    public Range(double start, double end) {
+        this.start = start;
+        this.end   = end;
+    }
+
+    public double getStart() {
+        return start;
+    }
+
+    public double getEnd() {
+        return end;
+    }
+
+    public boolean disjoint(double ostart, double oend) {
+        return start > oend || ostart > end;
+    }
+
+    public boolean disjoint(Range other) {
+        return start > other.end || other.start > end;
+    }
+
+    public boolean intersects(Range other) {
+        return !disjoint(other);
+    }
+
+    public void extend(Range other) {
+        if (other.start < start) start = other.start;
+        if (other.end   > end  ) end   = other.end;
+    }
+
+    public boolean clip(Range other) {
+        if (disjoint(other)) return false;
+
+        if (other.start > start) start = other.start;
+        if (other.end   < end  ) end   = other.end;
+
+        return true;
+    }
+
+    /** True if start>x<end (+ some epsilon) . */
+    public boolean inside(double x) {
+        return x > start-EPSILON && x < end+EPSILON;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/RangeWithValues.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,35 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.util.Arrays;
+
+/**
+ * A range (from -> to) with associated double array.
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class RangeWithValues extends Range {
+
+    protected double[] values;
+
+    public RangeWithValues() {
+    }
+
+    public RangeWithValues(double lower, double upper, double[] values) {
+        super(lower, upper);
+        this.values = values;
+    }
+
+
+    /** Returns the values. */
+    public double[] getValues() {
+        return values;
+    }
+
+    @Override
+    public String toString() {
+        return new StringBuilder("start=").append(start)
+            .append(" end=" ).append(end)
+            .append(" values=[").append(Arrays.toString(values)).append(']')
+            .toString();
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/ReferenceCurveFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,150 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Collections;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.WINFOArtifact;
+
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+import org.apache.log4j.Logger;
+
+
+/** Facet for W-over-Ws. */
+public class ReferenceCurveFacet
+extends      DataFacet
+{
+    private static Logger log = Logger.getLogger(ReferenceCurveFacet.class);
+
+    public static final String CONTEXT_KEY = "reference.curve.axis.scale";
+
+    /** Blackboard data provider key for reference curves start km. */
+    public static final String BB_REFERENCECURVE_STARTKM =
+        "reference_curve.startkm";
+
+    /** Blackboard data provider key for reference curves end kms. */
+    public static final String BB_REFERENCECURVE_ENDKMS =
+        "reference_curve.endkms";
+
+
+    public ReferenceCurveFacet() {
+    }
+
+
+    public ReferenceCurveFacet(int index, String name, String description) {
+        super(index, name, description, ComputeType.ADVANCE, null, null);
+    }
+
+
+    public ReferenceCurveFacet(
+        int         index,
+        String      name,
+        String      description,
+        ComputeType type,
+        String      stateID,
+        String      hash
+    ) {
+        super(index, name, description, type, hash, stateID);
+    }
+
+
+    public Object getData(Artifact artifact, CallContext context) {
+
+        if (log.isDebugEnabled()) {
+            log.debug("Get data for reference curve at index: " + index +
+                " /stateId: " + stateId);
+        }
+
+        return getWWQQ(artifact, context);
+    }
+
+
+   /**
+     * Can provide parameters of reference curve
+     * @param key      will respond on BB_REFERENCECURVE START/ENDKMS
+     * @param param    ignored
+     * @param context  ignored
+     * @return whatever parameters for reference curve
+     */
+    @Override
+    public Object provideBlackboardData(Artifact artifact,
+        Object key,
+        Object param,
+        CallContext context
+    ) {
+        WINFOArtifact winfo = (WINFOArtifact) artifact;
+        if (key.equals(BB_REFERENCECURVE_STARTKM)) {
+            return winfo.getReferenceStartKm();
+        }
+        else if (key.equals(BB_REFERENCECURVE_ENDKMS)) {
+            return winfo.getReferenceEndKms();
+        }
+        else {
+            return null;
+        }
+    }
+
+
+    protected WWQQ getWWQQ(Artifact artifact, CallContext context) {
+        FLYSArtifact winfo = (FLYSArtifact)artifact;
+
+        CalculationResult res = (CalculationResult)
+            winfo.compute(context, hash, stateId, type, false);
+
+        return ((WWQQ [])res.getData())[index];
+    }
+
+
+    @Override
+    public void set(Facet other) {
+        super.set(other);
+        ReferenceCurveFacet o = (ReferenceCurveFacet)other;
+        type                  = o.type;
+        hash                  = o.hash;
+        stateId               = o.stateId;
+    }
+
+
+    /** Copy deeply. */
+    @Override
+    public Facet deepCopy() {
+        ReferenceCurveFacet copy = new ReferenceCurveFacet();
+        copy.set(this);
+        return copy;
+    }
+
+
+    @Override
+    public List getStaticDataProviderKeys(Artifact art) {
+        List list = new ArrayList();
+        list.add(BB_REFERENCECURVE_STARTKM);
+        list.add(BB_REFERENCECURVE_ENDKMS);
+        return list;
+    }
+
+
+    @Override
+    public List getDataProviderKeys(Artifact art, CallContext context) {
+
+        // compute / get data
+        Object obj = context.getContextValue(CONTEXT_KEY);
+
+        if (!(obj instanceof WWAxisTypes)) {
+            obj = new WWAxisTypes(getWWQQ(art, context));
+            context.putContextValue(CONTEXT_KEY, obj);
+        }
+        else {
+            ((WWAxisTypes)obj).classify(getWWQQ(art, context));
+        }
+
+        return getStaticDataProviderKeys(art);//Collections.emptyList();
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/RelativePointFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,193 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.util.List;
+import java.awt.geom.Point2D;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.DataProvider;
+
+import org.dive4elements.river.artifacts.StaticWKmsArtifact;
+import org.dive4elements.river.artifacts.StaticWQKmsArtifact;
+import org.dive4elements.river.artifacts.math.Linear;
+
+/**
+ * Facet to access a point.
+ */
+public class RelativePointFacet
+extends      BlackboardDataFacet
+implements   FacetTypes {
+
+    /** Own logger. */
+    private static Logger logger = Logger.getLogger(RelativePointFacet.class);
+
+    /** Trivial Constructor. */
+    public RelativePointFacet(String description) {
+        this(RELATIVE_POINT, description);
+    }
+
+
+    public RelativePointFacet(String name, String description) {
+        this.name        = name;
+        this.description = description;
+        this.index       = 0;
+    }
+
+
+    protected Point2D calculateDurationCurvePoint(CallContext context,
+        WKms wKms)
+    {
+        // TODO here and in reference curve calc: Do warn if more than 1
+        // provider found or (way better) handle it.
+        Object wqdays = null;
+        double km     = 0d;
+        List<DataProvider> providers = context.
+            getDataProvider(DurationCurveFacet.BB_DURATIONCURVE);
+        if (providers.size() < 1) {
+            logger.warn("Could not find durationcurve data provider.");
+        }
+        else {
+            wqdays = providers.get(0).provideData(
+                DurationCurveFacet.BB_DURATIONCURVE,
+                null,
+                context);
+        }
+        List<DataProvider> kmproviders = context.
+            getDataProvider(DurationCurveFacet.BB_DURATIONCURVE_KM);
+        if (kmproviders.size() < 1) {
+            logger.warn("Could not find durationcurve.km data provider.");
+        }
+        else {
+            logger.debug("Found durationcurve.km data provider.");
+            String dckm = providers.get(0).provideData(
+                DurationCurveFacet.BB_DURATIONCURVE_KM,
+                null,
+                context).toString();
+            km = Double.valueOf(dckm);
+        }
+
+        if (wqdays != null) {
+            // Which W at this km?
+            double w = StaticWKmsArtifact.getWAtKmLin(wKms, km);
+            if (w == -1) {
+                logger.warn("w is -1, already bad sign!");
+            }
+            // Where is this W passed by in the wq-curve?
+            WQDay wqday = (WQDay) wqdays;
+            // Doing a linear Day Of KM.
+            int idx = 0;
+            boolean wIncreases = wqday.getW(0) < wqday.getW(wqday.size()-1);
+            if (wIncreases) {
+                while (idx < wqday.size() && wqday.getW(idx) < w) {
+                    idx++;
+                }
+            }
+            else {
+                idx = wqday.size() -1;
+                while (idx > 0 && wqday.getW(idx) > w) {
+                    idx--;
+                }
+            }
+
+            double day = 0d;
+            int mod = (wIncreases) ? -1 : +1;
+            if (idx != 0 && idx <= wqday.size()-1) {
+                day = Linear.linear(w, wqday.getW(idx+mod), wqday.getW(idx),
+                    wqday.getDay(idx+mod), wqday.getDay(idx));
+            }
+
+            return new Point2D.Double((double) day, w);
+        }
+        logger.warn("not wqkms / w / day found");
+        // TODO better signal failure.
+        return new Point2D.Double(0d, 0d);
+    }
+
+
+    /**
+     * Calculate a reference curve point, that is, a point made of
+     * the Ws from start and end km param of the reference curve.
+     */
+    public Point2D calculateReferenceCurvePoint(CallContext context,
+        WKms wKms) {
+
+        List<DataProvider> providers = context.
+            getDataProvider(ReferenceCurveFacet.BB_REFERENCECURVE_STARTKM);
+        if (providers.size() < 1) {
+            logger.warn("Could not find reference curve startkm data provider.");
+        }
+
+        Double start = (Double) providers.get(0).
+            provideData(ReferenceCurveFacet.BB_REFERENCECURVE_STARTKM, null, context);
+
+        providers = context.
+            getDataProvider(ReferenceCurveFacet.BB_REFERENCECURVE_ENDKMS);
+        if (providers.size() < 1) {
+            logger.warn("Could not find reference curve endkms data provider.");
+        }
+        double[] ends = (double[]) providers.get(0).
+            provideData(ReferenceCurveFacet.BB_REFERENCECURVE_ENDKMS, null, context);
+
+        logger.debug("Got s " + start + " e " + ends);
+
+        double startW = StaticWKmsArtifact.getWAtKmLin(wKms, start.doubleValue());
+        // TODO handle multiple ends.
+        double endW = StaticWKmsArtifact.getWAtKmLin(wKms, ends[0]);
+        logger.debug("Gotw s " + startW + " e " + endW);
+        return new Point2D.Double(startW, endW);
+    }
+
+
+    /**
+     * Returns the data this facet requires.
+     *
+     * @param artifact the owner artifact.
+     * @param context  the CallContext (ignored).
+     *
+     * @return the data.
+     */
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        WKms wKms = null;
+        if (artifact instanceof StaticWKmsArtifact) {
+            wKms = ((StaticWKmsArtifact) artifact).getWKms(0);
+        }
+        else if (artifact instanceof StaticWQKmsArtifact) {
+            wKms = ((StaticWQKmsArtifact) artifact).getWQKms();
+        }
+        else {
+            logger.error("Cannot handle Artifact to create relative point.");
+            return null;
+        }
+
+        // Find out whether we live in a duration curve context, there we would
+        // provide only a single point.
+
+        if (context.getDataProvider(
+            DurationCurveFacet.BB_DURATIONCURVE_KM).size() > 0) {
+            return calculateDurationCurvePoint(context, wKms);
+        }
+        else if (context.getDataProvider(
+            ReferenceCurveFacet.BB_REFERENCECURVE_STARTKM).size() > 0) {
+            return calculateReferenceCurvePoint(context, wKms);
+        }
+
+        // TODO better signal failure.
+        return new Point2D.Double(0d, 0d);
+    }
+
+
+    /**
+     * Create a deep copy of this Facet.
+     * @return a deep copy.
+     */
+    @Override
+    public RelativePointFacet deepCopy() {
+        RelativePointFacet copy = new RelativePointFacet(description);
+        copy.set(this);
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/ReportFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,64 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.artifactdatabase.state.DefaultFacet;
+import org.dive4elements.artifactdatabase.state.Facet;
+
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+
+import org.apache.log4j.Logger;
+
+public class ReportFacet
+extends      DefaultFacet
+implements   FacetTypes
+{
+    private static Logger logger = Logger.getLogger(ReportFacet.class);
+
+    protected ComputeType type;
+    protected String      hash;
+    protected String      stateId;
+
+    public ReportFacet() {
+        this(ComputeType.ADVANCE);
+    }
+
+    public ReportFacet(ComputeType type) {
+        super(0, REPORT, "report");
+        this.type = type;
+    }
+
+
+    public ReportFacet(ComputeType type, String hash, String stateId) {
+        super(0, REPORT, "report");
+        this.type    = type;
+        this.hash    = hash;
+        this.stateId = stateId;
+    }
+
+    public Object getData(Artifact artifact, CallContext context) {
+        logger.debug("get report data");
+
+        FLYSArtifact flys = (FLYSArtifact)artifact;
+
+        CalculationResult cr = (CalculationResult)flys.compute(
+            context, hash, stateId, type, false);
+
+        return cr.getReport();
+    }
+
+    @Override
+    public Facet deepCopy() {
+        ReportFacet copy = new ReportFacet();
+        copy.set(this);
+        copy.type    = type;
+        copy.hash    = hash;
+        copy.stateId = stateId;
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/RiverFactory.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,71 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.util.List;
+
+import org.dive4elements.river.backend.SessionHolder;
+import org.dive4elements.river.model.River;
+
+import org.hibernate.Query;
+import org.hibernate.Session;
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class RiverFactory {
+
+    /** We don't need to instantiate concrete objects of this class. */
+    private RiverFactory() {
+    }
+
+
+    /**
+     * Returns all rivers that were found in the backend.
+     *
+     * @return all rivers.
+     */
+    public static List<River> getRivers() {
+        Session session = SessionHolder.HOLDER.get();
+
+        return session.createQuery("from River order by name").list();
+    }
+
+
+    /**
+     * Returns a River object fetched from database based on its id.
+     *
+     * @param river_id The id of the desired river.
+     *
+     * @return the river.
+     */
+    public static River getRiver(int river_id) {
+        Session session = SessionHolder.HOLDER.get();
+
+        Query query = session.createQuery("from River where id=:river_id");
+        query.setParameter("river_id", river_id);
+
+        List<River> rivers = query.list();
+
+        return rivers.isEmpty() ? null : rivers.get(0);
+    }
+
+
+    /**
+     * Returns a River object fetched from database based on its name.
+     *
+     * @param river The name of a river.
+     *
+     * @return the River object.
+     */
+    public static River getRiver(String river) {
+        Session session = SessionHolder.HOLDER.get();
+
+        Query query = session.createQuery(
+            "from River where name =:name");
+        query.setParameter("name", river);
+
+        List<River> rivers = query.list();
+
+        return rivers.isEmpty() ? null : rivers.get(0);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/SQOverview.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,209 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.io.Serializable;
+
+import java.text.SimpleDateFormat;
+
+import java.util.ArrayList;
+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.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import org.dive4elements.river.utils.KMIndex;
+
+public class SQOverview
+implements   Serializable
+{
+    private static Logger log = Logger.getLogger(SQOverview.class);
+
+    /**
+     * Serial version UId.
+     */
+    private static final long serialVersionUID = -8934372438968398508L;
+
+    public interface Filter {
+
+        boolean accept(KMIndex<List<Date>> entry);
+
+    } // interface Filter
+
+
+    public static final Filter ACCEPT = new Filter() {
+        @Override
+        public boolean accept(KMIndex<List<Date>> entry) {
+            return true;
+        }
+    };
+
+    public static class KmFilter implements Filter {
+
+        protected double km;
+
+        public KmFilter (double km) {
+            this.km = km;
+        }
+        @Override
+        public boolean accept(KMIndex<List<Date>> list) {
+            for (KMIndex.Entry<List<Date>> e: list){
+                if (e.getKm() == km) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    };
+
+    public static class DateFilter implements Filter {
+
+        protected Date date;
+
+        public DateFilter (Date date) {
+            this.date = date;
+        }
+        @Override
+        public boolean accept(KMIndex<List<Date>> list) {
+            for (KMIndex.Entry<List<Date>> e: list){
+                if (e.getValue().equals(this.date)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    };
+
+    public static final double EPSILON = 1e-4;
+
+    public static final String DATE_FORMAT = "dd.MM.yyyy";
+
+    public static final String SQL_SQ =
+        "SELECT" +
+        "    s.km    AS km," +
+        "    m.datum AS datum " +
+        "FROM messung m " +
+        "    JOIN station s" +
+        "       ON m.stationid = s.stationid " +
+        "    JOIN gewaesser g " +
+        "       ON s.gewaesserid = g.gewaesserid " +
+        "WHERE" +
+        "    m.q_bpegel IS NOT NULL AND" +
+        "    g.name = :name " +
+        "ORDER by" +
+        "    s.km, m.datum";
+
+    protected String       riverName;
+
+    protected KMIndex<List<Date>> entries;
+
+    public SQOverview() {
+        entries = new KMIndex<List<Date>>();
+    }
+
+    public SQOverview(String riverName) {
+        this();
+        this.riverName = riverName;
+    }
+
+    private static final boolean epsilonEquals(double a, double b) {
+        return Math.abs(a - b) < EPSILON;
+    }
+
+    protected void loadData(Session session) {
+        SQLQuery query = session.createSQLQuery(SQL_SQ)
+            .addScalar("km",    StandardBasicTypes.DOUBLE)
+            .addScalar("datum", StandardBasicTypes.DATE);
+
+        query.setString("name", riverName);
+
+        List<Object []> list = query.list();
+
+        if (list.isEmpty()) {
+            log.warn("No river '" + riverName + "' found.");
+        }
+
+        Double prevKm = -Double.MAX_VALUE;
+        List<Date> dates = new ArrayList<Date>();
+
+        for (Object [] row: list) {
+            Double km = (Double)row[0];
+            if (!epsilonEquals(km, prevKm) && !dates.isEmpty()) {
+                entries.add(prevKm, dates);
+                dates = new ArrayList<Date>();
+            }
+            dates.add((Date)row[1]);
+            prevKm = km;
+        }
+
+        if (!dates.isEmpty()) {
+            entries.add(prevKm, dates);
+        }
+    }
+
+    public boolean load(Session session) {
+
+        loadData(session);
+
+        return true;
+    }
+
+
+    public void generateOverview(Document document) {
+        generateOverview(document, ACCEPT);
+    }
+
+    public KMIndex<List<Date>> filter(Filter f) {
+        // TODO: Apply filter
+        return entries;
+    }
+
+    public void generateOverview(
+        Document document,
+        Filter   filter
+    ) {
+        KMIndex<List<Date>> filtered = filter(ACCEPT);
+
+        Element sqElement = document.createElement("sq");
+
+        Element riverElement = document.createElement("river");
+
+        riverElement.setAttribute("name", riverName);
+
+        sqElement.appendChild(riverElement);
+
+        SimpleDateFormat df = new SimpleDateFormat(DATE_FORMAT);
+
+        Element kmE = document.createElement("km");
+
+        for (KMIndex.Entry<List<Date>> e: filtered) {
+
+            List<Date> dates = e.getValue();
+
+            if (!dates.isEmpty()) {
+                Element dEs = document.createElement("dates");
+
+                for (Date d: dates) {
+                    Element dE = document.createElement("date");
+
+                    dE.setAttribute("value", df.format(d));
+
+                    dEs.appendChild(dE);
+                }
+
+                kmE.appendChild(dEs);
+            }
+        }
+
+        sqElement.appendChild(kmE);
+
+        document.appendChild(sqElement);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/SQOverviewFactory.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,73 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.dive4elements.river.artifacts.cache.CacheFactory;
+
+import org.dive4elements.river.backend.SedDBSessionHolder;
+
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.Element;
+
+import org.apache.log4j.Logger;
+
+import org.hibernate.Session;
+
+public class SQOverviewFactory {
+
+    private static Logger log = Logger.getLogger(SQOverviewFactory.class);
+
+    public static final String CACHE_NAME = "sq-overviews";
+
+    private SQOverviewFactory() {
+    }
+
+
+    public static SQOverview getOverview(String river) {
+
+        boolean debug = log.isDebugEnabled();
+
+        if (debug) {
+            log.debug(
+                "Looking for sq overview for river '" + river + "'");
+        }
+
+        Cache cache = CacheFactory.getCache(CACHE_NAME);
+
+        if (cache == null) {
+            if (debug) {
+                log.debug("Cache not configured.");
+            }
+            return getUncached(river);
+        }
+
+        String key = "sq-over-" + river;
+
+        Element element = cache.get(key);
+
+        if (element != null) {
+            if (debug) {
+                log.debug("Overview found in cache");
+            }
+            return (SQOverview)element.getValue();
+        }
+
+        SQOverview overview = getUncached(river);
+
+        if (overview != null) {
+            if (debug) {
+                log.debug("Store overview in cache.");
+            }
+            cache.put(new Element(key, overview));
+        }
+
+        return overview;
+    }
+
+    public static SQOverview getUncached(String river) {
+        SQOverview overview = new SQOverview(river);
+
+        Session session = SedDBSessionHolder.HOLDER.get();
+
+        return overview.load(session) ? overview : 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/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/Segment.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,216 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.dive4elements.river.model.DischargeTable;
+import org.dive4elements.river.model.Gauge;
+import org.dive4elements.river.model.River;
+
+import org.dive4elements.river.utils.DoubleUtil;
+
+import java.io.Serializable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+/** A Range with values and a reference point. */
+public class Segment
+implements   Serializable
+{
+    private static Logger log = Logger.getLogger(Segment.class);
+
+    public static final Comparator<Segment> REF_CMP =
+        new Comparator<Segment>() {
+            @Override
+            public int compare(Segment a, Segment b) {
+                double d = a.referencePoint - b.referencePoint;
+                if (d < 0d) return -1;
+                return d > 0d ? +1 : 0;
+            }
+        };
+
+    protected double    from;
+    protected double    to;
+    protected double [] values;
+    protected double [] backup;
+    protected double    referencePoint;
+
+    public Segment() {
+    }
+
+    public Segment(double referencePoint) {
+        this.referencePoint = referencePoint;
+    }
+
+    public Segment(double from, double to, double [] values) {
+        this.from   = from;
+        this.to     = to;
+        this.values = values;
+    }
+
+    public boolean isUp() {
+        return from < to;
+    }
+
+    /** Checks whether given km lies inside the to/from bounds of this segment. */
+    public boolean inside(double km) {
+        return from < to
+            ? km >= from && km <= to
+            : km >= to   && km <= from;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder("Segment: [");
+        sb.append("from: ").append(from).append("; to: ")
+          .append(to)
+          .append("; ref: ").append(referencePoint)
+          .append("; values: (");
+        for (int i = 0; i < values.length; ++i) {
+            if (i > 0) sb.append(", ");
+            sb.append(values[i]);
+        }
+        sb.append(")]");
+        return sb.toString();
+    }
+
+    public void setFrom(double from) {
+        this.from = from;
+    }
+
+    public void backup() {
+        backup = values != null
+            ? (double [])values.clone()
+            : null;
+    }
+
+    public double [] getBackup() {
+        return backup;
+    }
+
+    public double getFrom() {
+        return from;
+    }
+
+    public void setTo(double to) {
+        this.to = to;
+    }
+
+    public double getTo() {
+        return to;
+    }
+
+    public void setValues(double [] values) {
+        this.values = values;
+    }
+
+    public double [] getValues() {
+        return values;
+    }
+
+    public int numValues() {
+        return values.length;
+    }
+
+    public void setReferencePoint(double referencePoint) {
+        this.referencePoint = referencePoint;
+    }
+
+    public double getReferencePoint() {
+        return referencePoint;
+    }
+
+    /** Use DoubleUtil to parse Segments. */
+    public static List<Segment> parseSegments(String input) {
+
+        final List<Segment> segments = new ArrayList<Segment>();
+
+        DoubleUtil.parseSegments(input, new DoubleUtil.SegmentCallback() {
+            @Override
+            public void newSegment(double from, double to, double [] values) {
+                segments.add(new Segment(from, to, values));
+            }
+        });
+
+        return segments;
+    }
+
+    public static boolean setReferencePointConvertQ(
+        List<Segment> segments,
+        River         river,
+        boolean       isQ,
+        Calculation   report
+    ) {
+        int numResults = -1;
+
+        boolean success = true;
+
+        // assign reference points
+        for (Segment segment: segments) {
+            Gauge gauge = river.maxOverlap(segment.getFrom(), segment.getTo());
+
+            if (gauge == null) {
+                log.warn("no gauge found. Defaults to mid point.");
+                segment.setReferencePoint(
+                    0.5*(segment.getFrom()+segment.getTo()));
+            }
+            else {
+                double ref = gauge.getStation().doubleValue();
+                log.debug(
+                    "reference gauge: " + gauge.getName() +
+                    " (km " + ref + ")");
+                segment.setReferencePoint(ref);
+            }
+
+            double [] values = segment.values;
+
+            if (numResults == -1) {
+                numResults = values.length;
+            }
+            else if (numResults != values.length) {
+                log.warn("wrong length of values");
+                return false;
+            }
+
+            // convert to Q if needed
+            if (!isQ && gauge != null) {
+
+                DischargeTable dt = gauge.fetchMasterDischargeTable();
+
+                //TODO: Change scale from 100 to 1 immediately after
+                //      discharge table import changed to cm!
+                double [][] table =
+                    DischargeTables.loadDischargeTableValues(dt, 100);
+
+                // need the original values for naming
+                segment.backup();
+
+                for (int i = 0; i < values.length; ++i) {
+                    //TODO: s.o.
+                    double w = values[i]; /* / 100.0; */
+                    double [] qs = DischargeTables.getQsForW(table, w);
+                    if (qs.length == 0) {
+                        log.warn("No Qs found for W = " + values[i]);
+                        report.addProblem("cannot.find.q.for.w", values[i]);
+                        values[i] = Double.NaN;
+                        success = false;
+                    }
+                    else {
+                        values[i] = qs[0];
+                        if (qs.length > 1) {
+                            log.warn(
+                                "More than one Q found for W = " + values[i]);
+                        }
+                    }
+                }
+            }
+        } // for all segments
+
+        Collections.sort(segments, Segment.REF_CMP);
+
+        return success;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/StaticBedHeightCacheKey.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,28 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.io.Serializable;
+
+public class StaticBedHeightCacheKey implements Serializable {
+    public static final String CACHE_NAME = "bedheight-value-table-static";
+
+    private int time;
+    private int height_id;
+
+    public StaticBedHeightCacheKey(int column, int wst_id) {
+        this.height_id  = wst_id;
+        this.time  = column;
+    }
+
+    public int hashCode() {
+        return (height_id << 8) | time;
+    }
+
+    public boolean equals(Object other) {
+        if (!(other instanceof StaticBedHeightCacheKey)) {
+            return false;
+        }
+        StaticBedHeightCacheKey o = (StaticBedHeightCacheKey) other;
+        return this.height_id == o.height_id && this.time == o.time;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/StaticMorphoWidthCacheKey.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,29 @@
+package org.dive4elements.river.artifacts.model;
+
+
+public class StaticMorphoWidthCacheKey
+{
+   public static final String CACHE_NAME = "morpho-width-table-static";
+
+    private int width_id;
+
+    public StaticMorphoWidthCacheKey(
+        int width_id
+    ) {
+        this.width_id = width_id;
+    }
+
+    @Override
+    public int hashCode() {
+        return (String.valueOf(width_id)).hashCode();
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof StaticMorphoWidthCacheKey)) {
+            return false;
+        }
+        StaticMorphoWidthCacheKey o = (StaticMorphoWidthCacheKey) other;
+        return this.width_id == o.width_id;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/StaticSedimentLoadCacheKey.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,53 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.apache.commons.lang.builder.HashCodeBuilder;
+
+
+public class StaticSedimentLoadCacheKey
+{
+    public static final String CACHE_NAME = "sedimentload-value-table-static";
+
+    private String river;
+    private double startKm;
+    private double endKm;
+    private int sYear;
+    private int eYear;
+
+    public StaticSedimentLoadCacheKey(
+        String river,
+        double startKm,
+        double endKm,
+        int sYear,
+        int eYear
+    ) {
+        this.river = river;
+        this.startKm = startKm;
+        this.endKm = endKm;
+        this.sYear = sYear;
+        this.eYear = eYear;
+    }
+
+    @Override
+    public int hashCode() {
+        HashCodeBuilder builder = new HashCodeBuilder();
+        builder.append(river);
+        builder.append(startKm);
+        builder.append(endKm);
+        builder.append(sYear);
+        builder.append(eYear);
+        return builder.toHashCode();
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof StaticBedHeightCacheKey)) {
+            return false;
+        }
+        StaticSedimentLoadCacheKey o = (StaticSedimentLoadCacheKey) other;
+        return this.river == o.river &&
+            this.startKm == o.startKm &&
+            this.endKm == o.endKm &&
+            this.sYear == o.sYear &&
+            this.eYear == o.eYear;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/StaticWKmsCacheKey.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,33 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.io.Serializable;
+
+/**
+ * Caching-Key object for 'static' wst- data.
+ */
+public final class StaticWKmsCacheKey
+implements         Serializable
+{
+    public static final String CACHE_NAME = "wst-value-table-static";
+
+    private int column;
+    private int wst_id;
+
+    public StaticWKmsCacheKey(int column, int wst_id) {
+        this.wst_id  = wst_id;
+        this.column  = column;
+    }
+
+    public int hashCode() {
+        return (wst_id << 8) | column;
+    }
+
+    public boolean equals(Object other) {
+        if (!(other instanceof StaticWKmsCacheKey)) {
+            return false;
+        }
+        StaticWKmsCacheKey o = (StaticWKmsCacheKey) other;
+        return wst_id == o.wst_id && this.column == o.column;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/StaticWQKmsCacheKey.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,33 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.io.Serializable;
+
+/**
+ * Caching-Key object for 'static' wst- data.
+ */
+public final class StaticWQKmsCacheKey
+implements         Serializable
+{
+    public static final String CACHE_NAME = "wst-wq-value-table-static";
+
+    private int column;
+    private int wst_id;
+
+    public StaticWQKmsCacheKey(int column, int wst_id) {
+        this.wst_id  = wst_id;
+        this.column  = column;
+    }
+
+    public int hashCode() {
+        return (wst_id << 8) | column;
+    }
+
+    public boolean equals(Object other) {
+        if (!(other instanceof StaticWQKmsCacheKey)) {
+            return false;
+        }
+        StaticWQKmsCacheKey o = (StaticWQKmsCacheKey) other;
+        return this.wst_id == o.wst_id && this.column == o.column;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/Timerange.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,41 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.io.Serializable;
+
+import java.util.Date;
+
+
+/**
+ * This class represents time ranges specified by start and end time. Start and
+ * end times are stored as long (number of milliseconds since january 1, 1970).
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class Timerange implements Serializable {
+
+    private long start;
+    private long end;
+
+
+    public Timerange(long start, long end) {
+        this.start = start;
+        this.end   = end;
+    }
+
+
+    public Timerange(Date start, Date stop) {
+        this.start = start.getTime();
+        this.end   = stop != null ? stop.getTime() : System.currentTimeMillis();
+    }
+
+
+    public long getStart() {
+        return start;
+    }
+
+
+    public long getEnd() {
+        return end;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/W.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,124 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.dive4elements.river.utils.DataUtil;
+import org.dive4elements.river.utils.DoubleUtil;
+
+import gnu.trove.TDoubleArrayList;
+
+import org.apache.log4j.Logger;
+
+public class W
+extends      NamedObjectImpl
+{
+    private static Logger log = Logger.getLogger(W.class);
+
+    protected TDoubleArrayList ws;
+
+    public W() {
+        ws = new TDoubleArrayList();
+    }
+
+    public W(String name) {
+        super(name);
+        ws = new TDoubleArrayList();
+    }
+
+    public W(int capacity) {
+        this(capacity, "");
+    }
+
+    public W(int capacity, String name) {
+        super(name);
+        ws = new TDoubleArrayList(capacity);
+    }
+
+    public void add(double value) {
+        ws.add(value);
+    }
+
+    public int size() {
+        return ws.size();
+    }
+
+    public double getW(int idx) {
+        return ws.getQuick(idx);
+    }
+
+    public double [] getWs() {
+        return ws.toNativeArray();
+    }
+
+    public double [] get(int idx) {
+        return get(idx, new double [1]);
+    }
+
+    public double [] get(int idx, double [] dst) {
+        dst[0] = ws.getQuick(idx);
+        return dst;
+    }
+
+    public double minWs() {
+        return ws.min();
+    }
+
+    public void removeNaNs() {
+        DoubleUtil.removeNaNs(new TDoubleArrayList [] { ws });
+    }
+
+    public boolean guessWaterIncreasing() {
+        return guessWaterIncreasing(0.05f);
+    }
+
+    public boolean guessWaterIncreasing(float factor) {
+        return DataUtil.guessWaterIncreasing(ws, factor);
+    }
+
+    public int [] longestIncreasingWRangeIndices() {
+        return longestIncreasingWRangeIndices(new int[2]);
+    }
+
+    public int [] longestIncreasingWRangeIndices(int [] bounds) {
+
+        int N = size();
+        int start = 0;
+        int stop  = 0;
+
+        double lastW = Double.MAX_VALUE;
+
+        for (int i = 0; i < N; ++i) {
+            double v = ws.getQuick(i);
+            if (v <= lastW) {
+                if (stop-start > bounds[1]-bounds[0]) {
+                    bounds[0] = start;
+                    bounds[1] = stop;
+                    if (log.isDebugEnabled()) {
+                        log.debug("new range: " +
+                            bounds[0] + " - " + bounds[1] + " (" +
+                            ws.getQuick(bounds[0]) + ", " +
+                            ws.getQuick(bounds[1]) + ")");
+
+                    }
+                }
+                start = stop = i;
+            }
+            else {
+                stop = i;
+            }
+            lastW = v;
+        }
+
+        if (stop-start > bounds[1]-bounds[0]) {
+            bounds[0] = start;
+            bounds[1] = stop;
+            if (log.isDebugEnabled()) {
+                log.debug("new range @end: " +
+                    bounds[0] + " - " + bounds[1] + " (" +
+                    ws.getQuick(bounds[0]) + ", " +
+                    ws.getQuick(bounds[1]) + ")");
+            }
+        }
+
+        return bounds;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WKms.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,20 @@
+package org.dive4elements.river.artifacts.model;
+
+import gnu.trove.TDoubleArrayList;
+
+public interface WKms
+extends          NamedObject
+{
+    int size();
+
+    double getKm(int index);
+
+    double getW(int index);
+
+    TDoubleArrayList allKms();
+
+    TDoubleArrayList allWs();
+
+    boolean guessWaterIncreasing();
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WKmsFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,54 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.StaticWKmsArtifact;
+
+/**
+ * Facet to show W|km Values.
+ */
+public class WKmsFacet
+extends      BlackboardDataFacet
+implements   FacetTypes {
+
+    /** Trivial Constructor. */
+    public WKmsFacet(String description) {
+        this(STATIC_WKMS, description);
+    }
+
+    public WKmsFacet(String name, String description) {
+        this.name        = name;
+        this.description = description;
+        this.index       = 0;
+    }
+
+
+    /**
+     * Returns the data this facet requires.
+     *
+     * @param artifact the owner artifact.
+     * @param context  the CallContext (ignored).
+     *
+     * @return the data.
+     */
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        StaticWKmsArtifact staticData =
+            (StaticWKmsArtifact) artifact;
+        return staticData.getWKms(0);
+    }
+
+
+    /**
+     * Create a deep copy of this Facet.
+     * @return a deep copy.
+     */
+    @Override
+    public WKmsFacet deepCopy() {
+        WKmsFacet copy = new WKmsFacet(description);
+        copy.set(this);
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WKmsFactory.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,152 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.util.List;
+
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.Element;
+
+import org.apache.log4j.Logger;
+
+import org.hibernate.Session;
+
+import org.hibernate.SQLQuery;
+import org.hibernate.type.StandardBasicTypes;
+
+import org.dive4elements.river.artifacts.cache.CacheFactory;
+
+import org.dive4elements.river.backend.SessionHolder;
+
+
+/**
+ * Factory to access ready-made WKms for other (than computed) 'kinds' of
+ * WST-data.
+ */
+public class WKmsFactory
+{
+    /** Private logger to use here. */
+    private static Logger log = Logger.getLogger(WKmsFactory.class);
+
+    /** Query to get km and ws for wst_id and column_pos. */
+    public static final String SQL_SELECT_WS =
+        "SELECT km, w FROM wst_w_values " +
+        "WHERE wst_id = :wst_id AND column_pos = :column_pos";
+
+    /** Query to get name for wst_id and column_pos. */
+    public static final String SQL_SELECT_NAME =
+        "SELECT name " +
+        "FROM wst_columns "+
+        "WHERE wst_id = :wst_id AND position = :column_pos";
+
+    /** Query to get name (description) for wst_id. */
+    public static final String SQL_SELECT_WST_NAME =
+        "SELECT description from wsts "+
+        "WHERE id = :wst_id";
+
+
+    private WKmsFactory() {
+    }
+
+
+    /**
+     * Get WKms for given column and wst_id, caring about the cache.
+     */
+    public static WKms getWKms(int column, int wst_id) {
+        log.debug("WKmsFactory.getWKms");
+        Cache cache = CacheFactory.getCache(StaticWKmsCacheKey.CACHE_NAME);
+
+        StaticWKmsCacheKey cacheKey;
+
+        if (cache != null) {
+            cacheKey = new StaticWKmsCacheKey(wst_id, column);
+            Element element = cache.get(cacheKey);
+            if (element != null) {
+                log.debug("Got static wst values from cache");
+                return (WKms)element.getValue();
+            }
+        }
+        else {
+            cacheKey = null;
+        }
+
+        WKms values = getWKmsUncached(column, wst_id);
+
+        if (values != null && cacheKey != null) {
+            log.debug("Store static wst values in cache.");
+            Element element = new Element(cacheKey, values);
+            cache.put(element);
+        }
+        return values;
+    }
+
+    /** Get name for a WKms. */
+    public static String getWKmsName(int wst_id) {
+        log.debug("WKmsFactory.getWKmsName wst_id/" + wst_id);
+
+        String name = null;
+        Session session = SessionHolder.HOLDER.get();
+
+        SQLQuery nameQuery = session.createSQLQuery(SQL_SELECT_WST_NAME)
+            .addScalar("description", StandardBasicTypes.STRING);
+        nameQuery.setInteger("wst_id",     wst_id);
+
+        List<String> names = nameQuery.list();
+        if (names.size() >= 1) {
+            name = names.get(0);
+        }
+
+        return name;
+    }
+
+    /** Get name for a WKms. */
+    public static String getWKmsName(int column, int wst_id) {
+        log.debug("WKmsFactory.getWKmsName c/" + column + ", wst_id/" + wst_id);
+
+        String name = null;
+        Session session = SessionHolder.HOLDER.get();
+
+        SQLQuery nameQuery = session.createSQLQuery(SQL_SELECT_NAME)
+            .addScalar("name", StandardBasicTypes.STRING);
+        nameQuery.setInteger("wst_id",     wst_id);
+        nameQuery.setInteger("column_pos", column);
+
+        List<String> names = nameQuery.list();
+        if (names.size() >= 1) {
+            name = names.get(0);
+        }
+
+        return name;
+    }
+
+
+    /**
+     * Get WKms from db.
+     * @param column the position columns value
+     * @param wst_id database id of the wst
+     * @return according WKms.
+     */
+    public static WKms getWKmsUncached(int column, int wst_id) {
+
+        if (log.isDebugEnabled()) {
+            log.debug("WKmsFactory.getWKmsUncached c/" + column + ", wst_id/" + wst_id);
+        }
+
+        WKmsImpl wkms = new WKmsImpl(getWKmsName(column, wst_id));
+
+        Session session = SessionHolder.HOLDER.get();
+        SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_WS)
+            .addScalar("km", StandardBasicTypes.DOUBLE)
+            .addScalar("w",  StandardBasicTypes.DOUBLE);
+        sqlQuery.setInteger("wst_id",     wst_id);
+        sqlQuery.setInteger("column_pos", column);
+
+        List<Object []> results = sqlQuery.list();
+
+        for (int i = 0, N = results.size(); i < N; i++) {
+            Object[] row = results.get(i);
+            wkms.add((Double) row[0], (Double) row[1]);
+        }
+
+        return wkms;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WKmsImpl.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,99 @@
+package org.dive4elements.river.artifacts.model;
+
+import gnu.trove.TDoubleArrayList;
+
+import org.dive4elements.river.utils.DataUtil;
+
+
+public class WKmsImpl
+extends      NamedObjectImpl
+implements   WKms
+{
+    protected TDoubleArrayList kms;
+    protected TDoubleArrayList ws;
+
+    public WKmsImpl() {
+        super("");
+        kms = new TDoubleArrayList();
+        ws  = new TDoubleArrayList();
+    }
+
+
+    /**
+     * Create named, empty WKms.
+     */
+    public WKmsImpl(String name) {
+        super(name);
+        kms = new TDoubleArrayList();
+        ws  = new TDoubleArrayList();
+    }
+
+
+    public WKmsImpl(int capacity) {
+        super("");
+        kms = new TDoubleArrayList(capacity);
+        ws  = new TDoubleArrayList(capacity);
+    }
+
+
+    public WKmsImpl(TDoubleArrayList kms, TDoubleArrayList ws) {
+        this(kms, ws, "");
+    }
+
+
+    public WKmsImpl(
+        TDoubleArrayList kms,
+        TDoubleArrayList ws,
+        String           name
+    ) {
+        super(name);
+        this.kms = kms;
+        this.ws  = ws;
+    }
+
+
+    /**
+     * Add a W (in NN+m) for a km (in km).
+     */
+    public void add(double km, double w) {
+        kms.add(km);
+        ws .add(w);
+    }
+
+
+    @Override
+    public double getW(int index) {
+        return ws.getQuick(index);
+    }
+
+
+    @Override
+    public double getKm(int index) {
+        return kms.getQuick(index);
+    }
+
+    @Override
+    public boolean guessWaterIncreasing() {
+        return guessWaterIncreasing(0.05f);
+    }
+
+    protected boolean guessWaterIncreasing(float factor) {
+        return DataUtil.guessWaterIncreasing(ws, factor);
+    }
+
+    @Override
+    public int size() {
+        return kms.size();
+    }
+
+    @Override
+    public TDoubleArrayList allKms() {
+        return kms;
+    }
+
+    @Override
+    public TDoubleArrayList allWs() {
+        return ws;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WKmsJRDataSource.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,115 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import org.apache.log4j.Logger;
+
+import net.sf.jasperreports.engine.JRDataSource;
+import net.sf.jasperreports.engine.JRException;
+import net.sf.jasperreports.engine.JRField;
+
+
+/**
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class WKmsJRDataSource implements JRDataSource
+{
+    /** The logger used in this exporter.*/
+    private static Logger logger = Logger.getLogger(WKmsJRDataSource.class);
+
+    /**
+     *
+     */
+    private ArrayList<String[]> data;
+    private HashMap<String, String> metaData;
+
+    private int index = -1;
+
+    /**
+     *
+     */
+    public WKmsJRDataSource()
+    {
+        data = new ArrayList<String[]>();
+        metaData = new HashMap<String, String>();
+    }
+
+
+    /**
+     *
+     */
+    public void addData(String[] data) {
+        this.data.add(data);
+    }
+
+
+    /**
+     *
+     */
+    public void addMetaData(String key, String value) {
+        this.metaData.put(key, value);
+    }
+
+
+    /**
+     *
+     */
+    public boolean next() throws JRException
+    {
+        index++;
+
+        return (index < data.size());
+    }
+
+
+    /**
+     *
+     */
+    public Object getFieldValue(JRField field) throws JRException
+    {
+        Object value = "";
+        String fieldName = field.getName();
+        if ("river".equals(fieldName)) {
+            value = metaData.get("river");
+        }
+        else if ("date".equals(fieldName)) {
+            value = metaData.get("date");
+        }
+        else if ("range".equals(fieldName)) {
+            value = metaData.get("range");
+        }
+        else if ("gauge".equals(fieldName)) {
+            value = metaData.get("gauge");
+        }
+        else if ("calculation".equals(fieldName)) {
+            value = metaData.get("calculation");
+        }
+        else if ("differences".equals(fieldName)) {
+            value = metaData.get("differences");
+        }
+        else if ("km".equals(fieldName)) {
+            value = data.get(index)[0];
+        }
+        else if ("W".equals(fieldName)) {
+            value = data.get(index)[1];
+        }
+        else if ("Q".equals(fieldName)) {
+            value = data.get(index)[2];
+        }
+        else if ("description".equals(fieldName)) {
+            value = data.get(index)[3];
+        }
+        else if ("location".equals(fieldName)) {
+            value = data.get(index)[4];
+        }
+        else if ("gaugename".equals(fieldName)) {
+            value = data.get(index)[5];
+        }
+        else if ("day".equals(fieldName)) {
+            value = data.get(index)[6];
+        }
+        return value;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WQ.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,104 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.dive4elements.river.utils.DoubleUtil;
+
+import gnu.trove.TDoubleArrayList;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.log4j.Logger;
+
+public class WQ
+extends      W
+{
+    public static final Pattern NUMBERS_PATTERN =
+        Pattern.compile("\\D*(\\d++.\\d*)\\D*");
+
+    private static Logger log = Logger.getLogger(WQ.class);
+
+    protected TDoubleArrayList qs;
+
+    public WQ() {
+        this("");
+    }
+
+    public WQ(String name) {
+        super(name);
+        qs = new TDoubleArrayList();
+    }
+
+    public WQ(int capacity) {
+        this(capacity, "");
+    }
+
+
+    public WQ(int capacity, String name) {
+        super(capacity, name);
+        qs = new TDoubleArrayList(capacity);
+    }
+
+    public WQ(double [] qs, double [] ws) {
+        this(qs, ws, "");
+    }
+
+    public WQ(double [] qs, double [] ws, String name) {
+        super(name);
+        this.ws = new TDoubleArrayList(ws);
+        this.qs = new TDoubleArrayList(qs);
+    }
+
+
+    public Double getRawValue() {
+        if (name == null || name.length() == 0) {
+            // this should never happen
+            return null;
+        }
+
+        Matcher m = NUMBERS_PATTERN.matcher(name);
+
+        if (m.matches()) {
+            String raw = m.group(1);
+
+            try {
+                return Double.valueOf(raw);
+            }
+            catch (NumberFormatException nfe) {
+                // do nothing
+            }
+        }
+
+        return null;
+    }
+
+    public void add(double w, double q) {
+        ws.add(w);
+        qs.add(q);
+    }
+
+    public double getQ(int idx) {
+        return qs.getQuick(idx);
+    }
+
+    @Override
+    public double [] get(int idx) {
+        return get(idx, new double [2]);
+    }
+
+    @Override
+    public double [] get(int idx, double [] dst) {
+        dst[0] = ws.getQuick(idx);
+        dst[1] = qs.getQuick(idx);
+        return dst;
+    }
+
+    public double [] getQs() {
+        return qs.toNativeArray();
+    }
+
+    @Override
+    public void removeNaNs() {
+        DoubleUtil.removeNaNs(new TDoubleArrayList [] { ws, qs });
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WQCKms.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,95 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.dive4elements.river.utils.DoubleUtil;
+
+import gnu.trove.TDoubleArrayList;
+
+/**
+ * This class represents a pool of data triples that consists of 'W', 'Q' and
+ * 'KM' data with corrected 'W' values computed by a BackJumpCorrector.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class WQCKms
+extends      WQKms
+{
+    protected TDoubleArrayList cws;
+
+    public WQCKms() {
+    }
+
+    public WQCKms(WQKms other, double [] cws) {
+        this.ws  = other.ws;
+        this.qs  = other.qs;
+        this.kms = other.kms;
+        this.cws = new TDoubleArrayList(cws);
+    }
+
+
+    public WQCKms(double[] kms, double[] qs, double[] ws, double[] cws) {
+        super(kms, qs, ws);
+
+        this.cws = new TDoubleArrayList(cws);
+    }
+
+    @Override
+    public void removeNaNs() {
+        DoubleUtil.removeNaNs(new TDoubleArrayList [] { ws, qs, cws, kms });
+    }
+
+    /**
+     * Adds a new row to this data pool with corrected W.
+     *
+     * @param w a W.
+     * @param q a Q.
+     * @param kms a Kms.
+     * @param cw The corrected W.
+     */
+    public void add(double w, double q, double kms, double cw) {
+        super.add(w, q, kms);
+        cws.add(cw);
+    }
+
+    @Override
+    public double[] get(int idx) {
+        return get(idx, new double[4]);
+    }
+
+    /**
+     * This method returns a 4dim array of W, Q,Kms and corrected W.
+     *
+     * @param idx The position of the triple.
+     * @param dst destination array
+     *
+     * @return a 4dim array of [W, Q, Kms, CW] in dst.
+     */
+    @Override
+    public double[] get(int idx, double[] dst) {
+        dst = super.get(idx, dst);
+
+        if (dst.length < 4) {
+            return dst;
+        }
+
+        if (cws != null && cws.size() > idx) {
+            dst[3] = cws.getQuick(idx);
+        }
+
+        return dst;
+    }
+
+    public double getC(int idx) {
+        return cws.getQuick(idx);
+    }
+
+
+    /**
+     * Returns the double array of corrected W values.
+     *
+     * @return the double array of corrected W values.
+     */
+    public double[] getCWs() {
+        return cws.toNativeArray();
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WQDay.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,77 @@
+package org.dive4elements.river.artifacts.model;
+
+import gnu.trove.TIntArrayList;
+
+/**
+ * This class represents a pool of data triples that consists of 'W', 'Q' and
+ * 'Day' data.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class WQDay
+extends      WQ
+{
+    protected TIntArrayList days;
+
+    public WQDay() {
+        super("");
+        days = new TIntArrayList();
+    }
+
+    public WQDay(int capacity) {
+        super(capacity);
+        days = new TIntArrayList(capacity);
+    }
+
+    public WQDay(int [] days, double [] ws, double [] qs) {
+        super(qs, ws, "");
+        this.days = new TIntArrayList(days);
+    }
+
+
+    public void add(int day, double w, double q) {
+        super.add(w, q);
+        days.add(day);
+    }
+
+
+    public boolean isIncreasing() {
+        int lo = getDay(0);
+        int hi = getDay(size()-1);
+
+        return lo < hi;
+    }
+
+
+    public int getDay(int idx) {
+        return days.getQuick(idx);
+    }
+
+    @Override
+    public void removeNaNs() {
+
+        int dest = 0;
+        int N = ws.size();
+
+        for (int i = 0; i < N; ++i) {
+            double wi = ws.getQuick(i);
+            double qi = qs.getQuick(i);
+
+            if (Double.isNaN(wi) || Double.isNaN(qi)) {
+                continue;
+            }
+
+            days.setQuick(dest, days.getQuick(i));
+            ws.setQuick(dest, wi);
+            qs.setQuick(dest, qi);
+            ++dest;
+        }
+
+        if (dest < N) {
+            days.remove(dest, N-dest);
+            ws  .remove(dest, N-dest);
+            qs  .remove(dest, N-dest);
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WQFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,61 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.artifactdatabase.state.DefaultFacet;
+
+import org.dive4elements.river.artifacts.WQKmsInterpolArtifact;
+
+/**
+ * Facet to show W|Q Values.
+ */
+public class WQFacet
+extends      DefaultFacet
+implements   FacetTypes {
+
+    /** Trivial Constructor. */
+    public WQFacet(String description) {
+        this(STATIC_WQ, description);
+    }
+
+
+    /**
+     * A Facet with WQ data.
+     */
+    public WQFacet(String name, String description) {
+        this.name        = name;
+        this.description = description;
+        this.index       = 0;
+    }
+
+
+    /**
+     * Returns the data this facet provides at km given in context.
+     *
+     * @param artifact the owner artifact.
+     * @param context  the CallContext.
+     *
+     * @return the data.
+     */
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        WQKmsInterpolArtifact interpolData =
+            (WQKmsInterpolArtifact) artifact;
+        Double currentKm = (Double)context.getContextValue("currentKm");
+        return interpolData.getWQAtKm(currentKm);
+    }
+
+
+    /**
+     * Create a deep copy of this Facet.
+     * @return a deep copy.
+     */
+    @Override
+    public WQKmsFacet deepCopy() {
+        WQKmsFacet copy = new WQKmsFacet(description);
+        copy.set(this);
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WQKms.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,130 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.dive4elements.river.utils.DoubleUtil;
+
+import gnu.trove.TDoubleArrayList;
+
+import org.apache.log4j.Logger;
+
+
+/**
+ * This class represents a pool of data triples that consists of 'W', 'Q' and
+ * 'KM' data.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class WQKms
+extends      WQ
+implements   WKms, QKms
+{
+    private static Logger logger = Logger.getLogger(WQKms.class);
+
+    /** The array that contains the 'KMs' values. */
+    protected TDoubleArrayList kms;
+
+
+    public WQKms() {
+        this("");
+    }
+
+
+    public WQKms(String name) {
+        super(name);
+        this.kms = new TDoubleArrayList();
+    }
+
+
+    public WQKms(int capacity) {
+        this(capacity, "");
+    }
+
+
+    public WQKms(int capacity, String name) {
+        super(capacity, name);
+        this.kms = new TDoubleArrayList(capacity);
+    }
+
+    public WQKms(double [] kms, double [] qs, double [] ws) {
+        this(kms, qs, ws, "");
+    }
+
+
+    public WQKms(double [] kms, double [] qs, double [] ws, String name) {
+        super(qs, ws, name);
+        this.kms = new TDoubleArrayList(kms);
+    }
+
+    @Override
+    public void removeNaNs() {
+        DoubleUtil.removeNaNs(new TDoubleArrayList [] { ws, qs, kms });
+    }
+
+    /**
+     * Adds a new row to this data pool.
+     *
+     * @param w a W.
+     * @param q a Q.
+     * @param km a kms.
+     */
+    public void add(double w, double q, double km) {
+        super.add(w, q);
+        kms.add(km);
+    }
+
+    @Override
+    public double [] get(int idx) {
+        return get(idx, new double [3]);
+    }
+
+    /**
+     * This method returns a triple of W, Q and Kms in a single 3dim array.
+     *
+     * @param idx The position of the triple.
+     * @param dst destination array
+     *
+     * @return a triple of [W, Q, Kms] in dst.
+     */
+    @Override
+    public double [] get(int idx, double [] dst) {
+        dst[0] = ws .getQuick(idx);
+        dst[1] = qs .getQuick(idx);
+        dst[2] = kms.getQuick(idx);
+        return dst;
+    }
+
+    @Override
+    public double getKm(int idx) {
+        return kms.getQuick(idx);
+    }
+
+    @Override
+    public TDoubleArrayList allKms() {
+        return kms;
+    }
+
+    @Override
+    public TDoubleArrayList allWs() {
+        return ws;
+    }
+
+    @Override
+    public TDoubleArrayList allQs() {
+        return qs;
+    }
+
+    public double[] getKms() {
+        return kms.toNativeArray();
+    }
+
+    /**
+     * Returns a string that consist of the first and last kilometer.
+     *
+     * @return a string that consist of the first and last kilometer.
+     */
+    public String toString() {
+        double from = getKm(0);
+        double to   = getKm(size()-1);
+        return from + " - " + to;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WQKmsFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,72 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.StaticWQKmsArtifact;
+
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+
+/**
+ * Facet to show W|Q|km Values.
+ * We have following 'Types' (from FacetTypes):
+ *   String STATIC_WQKMS = "other.wqkms";
+ *   String STATIC_WQMS_W = "other.wqkms.w";
+ *   String STATIC_WQKMS_Q = "other.wqkms.q";
+ */
+public class WQKmsFacet
+extends      DataFacet
+implements   FacetTypes {
+
+    /** House logger. */
+    private static Logger logger = Logger.getLogger(WQKmsFacet.class);
+
+    /** Trivial Constructor. */
+    public WQKmsFacet(String description) {
+        this(STATIC_WQKMS, description);
+    }
+
+
+    /**
+     * @param name Name of this facet (we have at least two flavors (w and q).
+     */
+    public WQKmsFacet(String name, String description) {
+        super(0, name, description, ComputeType.FEED, null, null);
+    }
+
+
+    /**
+     * Returns the data this facet requires.
+     *
+     * @param artifact the owner artifact.
+     * @param context  the CallContext (ignored).
+     *
+     * @return the data.
+     */
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        logger.debug("WQKmsFacet.getData");
+
+        StaticWQKmsArtifact staticData =
+            (StaticWQKmsArtifact) artifact;
+        Object res = staticData.compute(context, hash, stateId, type, false);
+
+        return res;
+    }
+
+
+    /**
+     * Create a deep copy of this Facet.
+     * @return a deep copy.
+     */
+    @Override
+    public WQKmsFacet deepCopy() {
+        WQKmsFacet copy = new WQKmsFacet(description);
+        copy.set(this);
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WQKmsFactory.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,199 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.util.List;
+
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.Element;
+
+import org.apache.log4j.Logger;
+
+import org.hibernate.Session;
+
+import org.hibernate.SQLQuery;
+import org.hibernate.type.StandardBasicTypes;
+
+import org.dive4elements.river.artifacts.cache.CacheFactory;
+
+import org.dive4elements.river.backend.SessionHolder;
+
+/**
+ * Factory to access ready-made WQKms for other (than computed) 'kinds' of
+ * WST-data.
+ */
+public class WQKmsFactory
+{
+    private static Logger log = Logger.getLogger(WQKmsFactory.class);
+
+    /** Query to get km and wqs for wst_id and column_pos. */
+    public static final String SQL_SELECT_WQS =
+        "SELECT position, w, q FROM wst_value_table " +
+        "WHERE wst_id = :wst_id AND column_pos = :column_pos";
+
+    /** Get wst_id and position from wst_columns. */
+    public static final String SQL_SELECT_COLUMN =
+        "SELECT wst_id, position FROM wst_columns WHERE id = :column_id";
+
+    /** Query to get name for wst_id and column_pos. */
+    public static final String SQL_SELECT_NAME =
+        "SELECT name " +
+        "FROM wst_columns "+
+        "WHERE id = :column_id";
+
+
+    /** Hidden constructor, use static methods instead. */
+    private WQKmsFactory() {
+    }
+
+
+    /**
+     * Get WKms for given column (pos) and wst_id, caring about the cache.
+     */
+    public static WQKms getWQKms(int columnPos, int wst_id) {
+        log.debug("WQKmsFactory.getWQKms");
+        Cache cache = CacheFactory.getCache(StaticWQKmsCacheKey.CACHE_NAME);
+
+        StaticWQKmsCacheKey cacheKey;
+
+        if (cache != null) {
+            cacheKey = new StaticWQKmsCacheKey(wst_id, columnPos);
+            Element element = cache.get(cacheKey);
+            if (element != null) {
+                log.debug("Got static wst values from cache");
+                return (WQKms)element.getValue();
+            }
+        }
+        else {
+            cacheKey = null;
+        }
+
+        WQKms values = getWQKmsUncached(columnPos, wst_id);
+
+        if (values != null && cacheKey != null) {
+            log.debug("Store static wst values in cache.");
+            Element element = new Element(cacheKey, values);
+            cache.put(element);
+        }
+        return values;
+    }
+
+    /**
+     * Get WKms for given column (id), caring about the cache.
+     */
+    public static WQKms getWQKmsCID(int columnID) {
+        log.debug("WQKmsFactory.getWQKms");
+        Cache cache = CacheFactory.getCache(StaticWQKmsCacheKey.CACHE_NAME);
+
+        StaticWQKmsCacheKey cacheKey;
+
+        if (cache != null) {
+            cacheKey = new StaticWQKmsCacheKey(-columnID, -columnID);
+            Element element = cache.get(cacheKey);
+            if (element != null) {
+                log.debug("Got static wst values from cache");
+                return (WQKms)element.getValue();
+            }
+        }
+        else {
+            cacheKey = null;
+        }
+
+        int[] cInfo = getColumn(columnID);
+        if (cInfo == null) return null;
+        WQKms values = getWQKmsUncached(cInfo[1], cInfo[0]);
+
+
+        if (values != null && cacheKey != null) {
+            log.debug("Store static wst values in cache.");
+            Element element = new Element(cacheKey, values);
+            cache.put(element);
+        }
+        return values;
+    }
+
+
+    /**
+     * Get WQKms from db.
+     * @param column the position columns value
+     * @param wst_id database id of the wst
+     * @return respective WQKms.
+     */
+    public static WQKms getWQKmsUncached(int column, int wst_id) {
+
+        if (log.isDebugEnabled()) {
+            log.debug("WQKmsFactory.getWQKmsUncached, column "
+                + column + ", wst_id " + wst_id);
+        }
+
+        WQKms wqkms = new WQKms(WKmsFactory.getWKmsName(column, wst_id));
+
+        Session session = SessionHolder.HOLDER.get();
+        SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_WQS)
+            .addScalar("position", StandardBasicTypes.DOUBLE)
+            .addScalar("w",  StandardBasicTypes.DOUBLE)
+            .addScalar("q",  StandardBasicTypes.DOUBLE);
+        sqlQuery.setInteger("wst_id",     wst_id);
+        sqlQuery.setInteger("column_pos", column);
+
+        List<Object []> results = sqlQuery.list();
+
+        for (int i = 0, N = results.size(); i < N; i++) {
+            Object[] row = results.get(i);
+            // add(w, q, km)
+            wqkms.add((Double) row[1], (Double) row[2], (Double) row[0]);
+        }
+
+        return wqkms;
+    }
+
+
+    /**
+     * Get WQKms from db.
+     * @param columnID the columns database id value
+     * @param wst_id database id of the wst
+     * @return respective WQKms.
+     */
+    public static int[] getColumn(int columnID) {
+
+        if (log.isDebugEnabled()) {
+            log.debug("WQKmsFactory.getColumn, columnID "
+                + columnID);
+        }
+
+        Session session = SessionHolder.HOLDER.get();
+
+        SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_COLUMN)
+            .addScalar("wst_id",  StandardBasicTypes.INTEGER)
+            .addScalar("position", StandardBasicTypes.INTEGER);
+        sqlQuery.setInteger("column_id", columnID);
+
+        List<Object []> results = sqlQuery.list();
+
+        for (int i = 0, N = results.size(); i < N; i++) {
+            Object[] row = results.get(i);
+            return new int[] {(Integer)row[0], (Integer)row[1]};
+        }
+
+        return null;
+    }
+
+
+    /** Get name for a WKms. */
+    public static String getWQKmsName(int columnID) {
+        log.debug("WQKmsFactory.getWQKmsName c/" + columnID);
+
+        String name = null;
+        Session session = SessionHolder.HOLDER.get();
+
+        SQLQuery nameQuery = session.createSQLQuery(SQL_SELECT_NAME)
+            .addScalar("name", StandardBasicTypes.STRING);
+        nameQuery.setInteger("column_id", columnID);
+
+        List<String> names = nameQuery.list();
+        if (names.size() >= 1) {
+            name = names.get(0);
+        }
+
+        return name;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WQKmsResult.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,9 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.dive4elements.river.artifacts.model.WQKms;
+
+/** A result that delivers WQKms. */
+public interface WQKmsResult {
+    public WQKms[] getWQKms();
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WQTJRDataSource.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,100 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import org.apache.log4j.Logger;
+
+import net.sf.jasperreports.engine.JRDataSource;
+import net.sf.jasperreports.engine.JRException;
+import net.sf.jasperreports.engine.JRField;
+
+
+/**
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class WQTJRDataSource implements JRDataSource
+{
+    /** The logger used in this exporter.*/
+    private static Logger logger = Logger.getLogger(WQTJRDataSource.class);
+
+    /**
+     *
+     */
+    private ArrayList<String[]> data;
+    private HashMap<String, String> metaData;
+
+    private int index = -1;
+
+    /**
+     *
+     */
+    public WQTJRDataSource()
+    {
+        data = new ArrayList<String[]>();
+        metaData = new HashMap<String, String>();
+    }
+
+
+    /**
+     *
+     */
+    public void addData(String[] data) {
+        this.data.add(data);
+    }
+
+
+    /**
+     *
+     */
+    public void addMetaData(String key, String value) {
+        this.metaData.put(key, value);
+    }
+
+
+    /**
+     *
+     */
+    public boolean next() throws JRException
+    {
+        index++;
+
+        return (index < data.size());
+    }
+
+
+    /**
+     *
+     */
+    public Object getFieldValue(JRField field) throws JRException
+    {
+        Object value = "";
+        String fieldName = field.getName();
+        if ("river".equals(fieldName)) {
+            value = metaData.get("river");
+        }
+        else if ("date".equals(fieldName)) {
+            value = metaData.get("date");
+        }
+        else if ("calculation".equals(fieldName)) {
+            value = metaData.get("calculation");
+        }
+        else if ("range".equals(fieldName)) {
+            value = data.get(index)[0];
+        }
+        else if ("W".equals(fieldName)) {
+            value = data.get(index)[1];
+        }
+        else if ("Q".equals(fieldName)) {
+            value = data.get(index)[2];
+        }
+        else if ("delta".equals(fieldName)) {
+            value = data.get(index)[3];
+        }
+        else if ("gaugename".equals(fieldName)) {
+            value = data.get(index)[4];
+        }
+        return value;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WQTimerange.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,96 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+
+/**
+ * A collection of triples W,Q,Timerange.
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class WQTimerange extends WQ {
+
+    /** Used to sort &lt;w,q,timerange&gt; triples. */
+    public static class TimerangeItem implements Comparable<TimerangeItem> {
+        public double q;
+        public double w;
+        public Timerange timerange;
+
+        public TimerangeItem (Timerange timerange, double q, double w) {
+            this.timerange = timerange;
+            this.q = q;
+            this.w = w;
+        }
+
+        /** Sets [w,q] in wq. */
+        public double[] get(double[] wq) {
+            if (wq.length >= 2) {
+                wq[0] = w;
+                wq[1] = q;
+            }
+
+            return wq;
+        }
+
+        @Override
+        public int compareTo(TimerangeItem other) {
+            if (other.timerange.getStart() < timerange.getStart()) {
+                return 1;
+            }
+            else if (other.timerange.getStart() > timerange.getStart()) {
+                return -1;
+            }
+            else if (other.timerange.getEnd() < timerange.getEnd()) {
+                return 1;
+            }
+            else if (other.timerange.getEnd() > timerange.getEnd()){
+                return -1;
+            }
+            else {
+                return 0;
+            }
+        }
+    }
+
+    protected List<Timerange> timeranges;
+
+
+    public WQTimerange() {
+        super("");
+    }
+
+
+    public WQTimerange(String name) {
+        super(name);
+        timeranges = new ArrayList<Timerange>();
+    }
+
+
+    public void add(double w, double q, Timerange t) {
+        ws.add(w);
+        qs.add(q);
+        timeranges.add(t);
+    }
+
+
+    public Timerange getTimerange(int idx) {
+        return timeranges.get(idx);
+    }
+
+
+    public Timerange[] getTimeranges() {
+        return timeranges.toArray(new Timerange[timeranges.size()]);
+    }
+
+    public List<TimerangeItem> sort() {
+        ArrayList<TimerangeItem> items = new ArrayList<TimerangeItem>(timeranges.size());
+        for (int i = 0, n = size(); i < n; i++) {
+            items.add(new TimerangeItem(getTimerange(i), getQ(i), getW(i)));
+        }
+
+        Collections.sort(items);
+        return items;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WW.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,202 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.dive4elements.river.artifacts.math.Function;
+import org.dive4elements.river.artifacts.math.Identity;
+
+import org.dive4elements.river.utils.DoubleUtil;
+
+import gnu.trove.TDoubleArrayList;
+
+public class WW
+extends      W
+{
+    public static class ApplyFunctionIterator
+    {
+        protected Function function1;
+        protected Function function2;
+        protected int      pos;
+        protected WW       ww;
+
+        public ApplyFunctionIterator(WW ww) {
+            this(ww, Identity.IDENTITY, Identity.IDENTITY);
+        }
+
+        public ApplyFunctionIterator(
+            WW       ww,
+            Function function1,
+            Function function2
+        ) {
+            this.ww        = ww;
+            this.function1 = function1;
+            this.function2 = function2;
+        }
+
+        public boolean hasNext() {
+            return pos < ww.size();
+        }
+
+        public int size() {
+            return ww.size();
+        }
+
+        public void reset() {
+            pos = 0;
+        }
+
+        public WW getWW() {
+            return ww;
+        }
+
+        public void get(int idx, double [] wwPair) {
+            wwPair[0] = function1.value(ww.getW(idx));
+            wwPair[1] = function2.value(ww.getW2(idx));
+        }
+
+        public void next(double [] wwPair) {
+            get(pos++, wwPair);
+        }
+    } // class FunctionIterator
+
+    protected TDoubleArrayList ws2;
+
+    protected double startKm;
+    protected double endKm;
+
+    protected Double startDatum;
+    protected Double endDatum;
+
+    public WW() {
+        this("");
+    }
+
+    public WW(String name) {
+        super(name);
+    }
+
+    public WW(int capacity) {
+        this(capacity, "");
+    }
+
+    public WW(int capacity, String name) {
+        super(capacity, name);
+        ws2 = new TDoubleArrayList(capacity);
+    }
+
+    public WW(
+        String    name,
+        double    startKm,
+        Double    startDatum,
+        double [] ws,
+        double    endKm,
+        Double    endDatum,
+        double [] ws2
+    ) {
+        this.name       = name;
+        this.ws         = new TDoubleArrayList(ws);
+        this.ws2        = new TDoubleArrayList(ws2);
+        this.startKm    = startKm;
+        this.startDatum = startDatum;
+        this.endKm      = endKm;
+        this.endDatum   = endDatum;
+    }
+
+    public WW(String name, TDoubleArrayList ws, TDoubleArrayList ws2) {
+        this.name = name;
+        this.ws  = ws;
+        this.ws2 = ws2;
+    }
+
+    public void add(double w1, double w2) {
+        ws .add(w1);
+        ws2.add(w2);
+    }
+
+    public double getW1(int idx) {
+        return ws.getQuick(idx);
+    }
+
+    public double getW2(int idx) {
+        return ws2.getQuick(idx);
+    }
+
+    public double [] getWs2() {
+        return ws2.toNativeArray();
+    }
+
+    @Override
+    public double [] get(int idx) {
+        return get(idx, new double[2]);
+    }
+
+    @Override
+    public double [] get(int idx, double [] dst) {
+        dst[0] = ws .getQuick(idx);
+        dst[1] = ws2.getQuick(idx);
+        return dst;
+    }
+
+    public double getStartKm() {
+        return startKm;
+    }
+
+    public void setStartKm(double startKm) {
+        this.startKm = startKm;
+    }
+
+    public double getEndKm() {
+        return endKm;
+    }
+
+    public void setEndKm(double endKm) {
+        this.endKm = endKm;
+    }
+
+    public Double getStartDatum() {
+        return startDatum;
+    }
+
+    public boolean startAtGauge() {
+        return startDatum != null;
+    }
+
+    public boolean endAtGauge() {
+        return endDatum != null;
+    }
+
+    public void setStartDatum(Double startDatum) {
+        this.startDatum = startDatum;
+    }
+
+    public Double getEndDatum() {
+        return endDatum;
+    }
+
+    public void setEndDatum(Double endDatum) {
+        this.endDatum = endDatum;
+    }
+
+    @Override
+    public void removeNaNs() {
+        DoubleUtil.removeNaNs(new TDoubleArrayList [] { ws, ws2 });
+    }
+
+    public double minWs2() {
+        return ws2.min();
+    }
+
+    // Note that we can also easily define a Function to do so.
+    public double getRelHeight1Cm(int idx) {
+        if (this.startAtGauge()) {
+            return (ws.getQuick(idx) - getStartDatum())*100d;
+        }
+        else return ws.getQuick(idx)*100d;
+    }
+
+    public double getRelHeight2Cm(int idx) {
+        if (this.endAtGauge()) {
+            return (ws2.getQuick(idx) - getEndDatum())*100d;
+        }
+        else return ws2.getQuick(idx)*100d;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WWAxisTypes.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,84 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.dive4elements.river.artifacts.math.Function;
+import org.dive4elements.river.artifacts.math.Identity;
+import org.dive4elements.river.artifacts.math.AddScaleFunction;
+
+public class WWAxisTypes
+{
+    protected boolean inCm1;
+    protected boolean inCm2;
+
+    public WWAxisTypes() {
+        inCm1 = inCm2 = true;
+    }
+
+    public WWAxisTypes(WW ww) {
+        this();
+        classify(ww);
+    }
+
+    public void classify(WW ww) {
+        if (!ww.startAtGauge()) inCm1 = false;
+        if (!ww.endAtGauge())   inCm2 = false;
+    }
+
+    public boolean getInCm(int index) {
+        switch (index) {
+            case 0:  return inCm1;
+            case 1:  return inCm2;
+            default: return false;
+        }
+    }
+
+    public boolean getInCm1() {
+        return inCm1;
+    }
+
+    public void setInCm1(boolean inCm1) {
+        this.inCm1 = inCm1;
+    }
+
+    public boolean getInCm2() {
+        return inCm2;
+    }
+
+    public void setInCm2(boolean inCm2) {
+        this.inCm2 = inCm2;
+    }
+
+    public WW.ApplyFunctionIterator transform(WW ww) {
+        return transform(ww, false);
+    }
+
+    private static final double zero(Double d) {
+        return d == null ? 0 : d;
+    }
+
+    public WW.ApplyFunctionIterator transform(WW ww, boolean normalized) {
+
+        Function function1;
+        Function function2;
+
+        if (!normalized) {
+            function1 = inCm1
+                ? new AddScaleFunction(-ww.getStartDatum(), 100d)
+                : Identity.IDENTITY;
+
+            function2 = inCm2
+                ? new AddScaleFunction(-ww.getEndDatum(), 100d)
+                : Identity.IDENTITY;
+        }
+        else {
+            double minW1 = ww.minWs();
+            double minW2 = ww.minWs2();
+            double scale1 = inCm1 ? 100d : 1d;
+            double scale2 = inCm2 ? 100d : 1d;
+            function1 = new AddScaleFunction(-minW1, scale1);
+            function2 = new AddScaleFunction(-minW2, scale2);
+        }
+
+        return new WW.ApplyFunctionIterator(ww, function1, function2);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WWQQ.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,99 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.dive4elements.river.artifacts.math.Identity;
+
+import org.dive4elements.river.utils.DoubleUtil;
+
+import gnu.trove.TDoubleArrayList;
+
+public class WWQQ
+extends      WW
+{
+    public static class ApplyFunctionIterator
+    extends             WW.ApplyFunctionIterator
+    {
+        public ApplyFunctionIterator(WWQQ ww) {
+            super(ww, Identity.IDENTITY, Identity.IDENTITY);
+        }
+
+        @Override
+        public void get(int idx, double [] wwqqPair) {
+            WWQQ wwqq = (WWQQ)ww;
+            wwqqPair[0] = function1.value(wwqq.getW(idx));
+            wwqqPair[1] = function2.value(wwqq.getW2(idx));
+            wwqqPair[2] = wwqq.getQ1(idx);
+            wwqqPair[3] = wwqq.getQ2(idx);
+        }
+    } // class ApplyFunctionIterator
+
+    protected TDoubleArrayList qs1;
+    protected TDoubleArrayList qs2;
+
+    public WWQQ() {
+        this("");
+    }
+
+    public WWQQ(String name) {
+        super(name);
+    }
+
+    public WWQQ(int capacity) {
+        this(capacity, "");
+    }
+
+    public WWQQ(int capacity, String name) {
+        super(capacity, name);
+    }
+
+    public WWQQ(
+        String    name,
+        double    startKm,
+        Double    startDatum,
+        double [] ws1,
+        double [] qs1,
+        double    endKm,
+        Double    endDatum,
+        double [] ws2,
+        double [] qs2
+    ) {
+        super(name, startKm, startDatum, ws1, endKm, endDatum, ws2);
+        this.qs1 = new TDoubleArrayList(qs1);
+        this.qs2 = new TDoubleArrayList(qs2);
+    }
+
+    public double getQ1(int idx) {
+        return qs1.getQuick(idx);
+    }
+
+    public double getQ2(int idx) {
+        return qs2.getQuick(idx);
+    }
+
+    public double [] getQs1() {
+        return qs1.toNativeArray();
+    }
+
+    public double [] getQs2() {
+        return qs2.toNativeArray();
+    }
+
+    @Override
+    public double [] get(int idx) {
+        return get(idx, new double[4]);
+    }
+
+    @Override
+    public double [] get(int idx, double [] dst) {
+        dst[0] = ws .getQuick(idx);
+        dst[1] = ws2.getQuick(idx);
+        dst[2] = qs1.getQuick(idx);
+        dst[3] = qs2.getQuick(idx);
+        return dst;
+    }
+
+    @Override
+    public void removeNaNs() {
+        DoubleUtil.removeNaNs(new TDoubleArrayList [] { ws, ws2, qs1, qs2 });
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WWQQJRDataSource.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,121 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import org.apache.log4j.Logger;
+
+import net.sf.jasperreports.engine.JRDataSource;
+import net.sf.jasperreports.engine.JRException;
+import net.sf.jasperreports.engine.JRField;
+
+
+/**
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class WWQQJRDataSource implements JRDataSource
+{
+    /** The logger used in this exporter.*/
+    private static Logger logger = Logger.getLogger(WWQQJRDataSource.class);
+
+    /**
+     *
+     */
+    private ArrayList<String[]> data;
+    private HashMap<String, String> metaData;
+
+    private int index = -1;
+
+    /**
+     *
+     */
+    public WWQQJRDataSource()
+    {
+        data = new ArrayList<String[]>();
+        metaData = new HashMap<String, String>();
+    }
+
+
+    /**
+     *
+     */
+    public void addData(String[] data) {
+        this.data.add(data);
+    }
+
+
+    /**
+     *
+     */
+    public void addMetaData(String key, String value) {
+        this.metaData.put(key, value);
+    }
+
+
+    /**
+     *
+     */
+    public boolean next() throws JRException
+    {
+        index++;
+
+        return (index < data.size());
+    }
+
+
+    /**
+     *
+     */
+    public Object getFieldValue(JRField field) throws JRException
+    {
+        Object value = "";
+        String fieldName = field.getName();
+        if ("river".equals(fieldName)) {
+            value = metaData.get("river");
+        }
+        else if ("date".equals(fieldName)) {
+            value = metaData.get("date");
+        }
+        else if ("calculation".equals(fieldName)) {
+            value = metaData.get("calculation");
+        }
+        else if ("reference".equals(fieldName)) {
+            value = metaData.get("reference");
+        }
+        else if ("location".equals(fieldName)) {
+            value = metaData.get("location");
+        }
+        else if ("km1".equals(fieldName)) {
+            value = data.get(index)[0];
+        }
+        else if ("location1".equals(fieldName)) {
+            value = data.get(index)[1];
+        }
+        else if ("W1".equals(fieldName)) {
+            value = data.get(index)[2];
+        }
+        else if ("Q1".equals(fieldName)) {
+            value = data.get(index)[3];
+        }
+        else if ("km2".equals(fieldName)) {
+            value = data.get(index)[4];
+        }
+        else if ("location2".equals(fieldName)) {
+            value = data.get(index)[5];
+        }
+        else if ("W2".equals(fieldName)) {
+            value = data.get(index)[6];
+        }
+        else if ("Q2".equals(fieldName)) {
+            value = data.get(index)[7];
+        }
+        else if ("Wcm1".equals(fieldName)) {
+            value = data.get(index)[8];
+        }
+        else if ("Wcm2".equals(fieldName)) {
+            value = data.get(index)[9];
+        }
+        return value;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WaterlevelFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,160 @@
+package org.dive4elements.river.artifacts.model;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.math.Linear;
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+import org.dive4elements.river.artifacts.model.extreme.ExtremeResult;
+
+import org.apache.log4j.Logger;
+
+/**
+ * Facet of a Waterlevel (WQKms).
+ */
+public class WaterlevelFacet extends DataFacet {
+
+    private static Logger logger = Logger.getLogger(WaterlevelFacet.class);
+
+    public WaterlevelFacet(int index, String name, String description) {
+        super(index, name, description, ComputeType.ADVANCE, null, null);
+    }
+
+    public WaterlevelFacet(
+        int         index,
+        String      name,
+        String      description,
+        ComputeType type,
+        String      stateID,
+        String      hash
+    ) {
+        super(index, name, description, type, hash, stateID);
+    }
+
+    public WaterlevelFacet() {
+    }
+
+    protected WQKms [] getWQKms(CalculationResult res) {
+        if (res.getData() instanceof ExtremeResult)
+            return ((ExtremeResult) res.getData()).getWQKms();
+        else if (res.getData() instanceof WQKms[]) {
+            return (WQKms []) res.getData();
+        }
+        else {
+            logger.error("WaterlevelFacet got wrong data type " + res.getData());
+            return null;
+        }
+    }
+
+    /**
+     * Get waterlevel data.
+     * @return a WQKms at given index.
+     */
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("Get data for waterlevels at index: " + index +
+                " /stateId: " + stateId);
+        }
+
+        if (artifact == null) {
+            logger.error("WaterlevelFacet.getData: artifact is null");
+            return null;
+        }
+
+        FLYSArtifact winfo = (FLYSArtifact) artifact;
+
+        CalculationResult res = (CalculationResult)
+            winfo.compute(context, hash, stateId, type, false);
+
+        if (res == null) {
+            logger.error("WaterlevelFacet.getData: null result");
+            return null;
+        }
+
+        WQKms [] wqkms = getWQKms(res);
+        Object KM = context.getContextValue("currentKm");
+
+        // Interpolation.
+        if (KM != null) {
+            linearInterpolate(wqkms[index], (Double) KM);
+        }
+        else {
+            logger.debug("Do not interpolate.");
+        }
+
+        return wqkms != null ? wqkms[index] : null;
+    }
+
+
+    /**
+     * Linear interpolation of WQKms.
+     * TODO rewrite.
+     * @return [w, q, km]
+     */
+    public WQKms linearInterpolate(WQKms wqkms, double km) {
+        logger.debug("interpolate at given km (" + km + ")");
+
+        WQKms resultWQKms = new WQKms();
+        int size = wqkms.size();
+        boolean kmIncreasing = wqkms.getKm(0) < wqkms.getKm(size-1);
+        int mod = kmIncreasing ? +1 : -1;
+        int idx = 0;
+        // Move idx to closest from one direction, check for match.
+        if (!kmIncreasing) {
+            while (idx < size && wqkms.getKm(idx) < km) {
+                if (Math.abs(wqkms.getKm(idx) - km) < 0.01d) {
+                    resultWQKms.add(wqkms.getW(idx), wqkms.getQ(idx), wqkms.getKm(idx));
+                    return resultWQKms;
+                }
+                idx++;
+            }
+        }
+        else {
+            idx = size-1;
+            while (idx > 0 && wqkms.getKm(idx) > km) {
+                if (Math.abs(wqkms.getKm(idx) - km) < 0.01d) {
+                    resultWQKms.add(wqkms.getW(idx), wqkms.getQ(idx), wqkms.getKm(idx));
+                    return resultWQKms;
+                }
+                idx--;
+            }
+        }
+        if (Math.abs(wqkms.getKm(idx) - km) < 0.01d) {
+            resultWQKms.add(wqkms.getW(idx), wqkms.getQ(idx), wqkms.getKm(idx));
+            return resultWQKms;
+        }
+
+        if ((idx != -1) && (idx < size) && (idx - mod != -1) && (idx - mod < size)) {
+            double inW = Linear.linear(
+                km,
+                wqkms.getKm(idx), wqkms.getKm(idx - mod),
+                wqkms.getW(idx), wqkms.getW(idx - mod));
+            double inQ = Linear.linear(
+                km,
+                wqkms.getKm(idx), wqkms.getKm(idx - mod),
+                wqkms.getQ(idx), wqkms.getQ(idx - mod));
+            resultWQKms.add(inW, inQ, km);
+        }
+        else {
+            logger.debug("waterlevelfacet stuff " + idx + " size " + size + " mod: " + mod);
+        }
+
+        return resultWQKms;
+    }
+
+
+    /** Copy deeply. */
+    @Override
+    public Facet deepCopy() {
+        WaterlevelFacet copy = new WaterlevelFacet();
+        copy.set(this);
+        copy.type    = type;
+        copy.hash    = hash;
+        copy.stateId = stateId;
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WstFactory.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,48 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.util.List;
+
+import org.dive4elements.river.backend.SessionHolder;
+import org.dive4elements.river.model.River;
+import org.dive4elements.river.model.Wst;
+
+import org.hibernate.Query;
+import org.hibernate.Session;
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class WstFactory {
+
+    public static final int DEFAULT_KIND = 0;
+
+    /** We don't need to instantiate concrete objects of this class. */
+    private WstFactory() {
+    }
+
+
+    /**
+     * Returns the Wst object for a given river.
+     *
+     * @param river The river.
+     *
+     * @return the Wst of <i>river</i>.
+     */
+    public static Wst getWst(River river) {
+        return getWst(river, DEFAULT_KIND);
+    }
+
+    public static Wst getWst(River river, int kind) {
+        Session session = SessionHolder.HOLDER.get();
+
+        Query query = session.createQuery(
+            "from Wst where river=:river and kind = :kind");
+        query.setParameter("river", river);
+        query.setInteger("kind", kind);
+
+        List<Wst> wsts = query.list();
+
+        return wsts.isEmpty() ? null : wsts.get(0);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WstLine.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,88 @@
+package org.dive4elements.river.artifacts.model;
+
+import gnu.trove.TDoubleArrayList;
+
+
+/**
+ * A model class that is used to store a line of a WST.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class WstLine {
+
+    /** The kilometer value of the line.*/
+    protected double km;
+
+    /** The W values.*/
+    protected TDoubleArrayList ws;
+
+    /** The Q values.*/
+    protected TDoubleArrayList qs;
+
+
+    /**
+     * A constructor that builds a new WstLine for a specific kilometer.
+     *
+     * @param km The kilometer.
+     */
+    public WstLine(double km) {
+        this.km = km;
+        this.ws = new TDoubleArrayList();
+        this.qs = new TDoubleArrayList();
+    }
+
+
+    /**
+     * Adds a pair of W/Q to this line.
+     *
+     * @param w The W value.
+     * @param q The Q value.
+     */
+    public void add(double w, double q) {
+        ws.add(w);
+        qs.add(q);
+    }
+
+
+    /**
+     * Returns the kilometer of this line.
+     *
+     * @return the kilomter of this line.
+     */
+    public double getKm() {
+        return km;
+    }
+
+
+    /**
+     * Returns the W value at index <i>idx</i> of this line.
+     *
+     * @param idx The position of the desired W value.
+     *
+     * @return the W at position <i>idx</i>.
+     */
+    public double getW(int idx) {
+        return ws.size() > idx ? ws.get(idx) : -1d;
+    }
+
+
+    /**
+     * Returns the Q values of this line.
+     *
+     * @return the Q values of this line.
+     */
+    public double[] getQs() {
+        return qs.toNativeArray();
+    }
+
+
+    /**
+     * Returns the number of columns this line consists of.
+     *
+     * @return the columns this line consists of.
+     */
+    public int getSize() {
+        return qs.size();
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WstValueTable.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,1268 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.io.Serializable;
+
+import org.dive4elements.river.artifacts.math.Linear;
+import org.dive4elements.river.artifacts.math.Function;
+
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Collections;
+
+import org.apache.log4j.Logger;
+
+import org.apache.commons.math.analysis.interpolation.SplineInterpolator;
+
+import org.apache.commons.math.analysis.polynomials.PolynomialSplineFunction;
+
+import org.apache.commons.math.ArgumentOutsideDomainException;
+
+import org.apache.commons.math.exception.MathIllegalArgumentException;
+
+import gnu.trove.TDoubleArrayList;
+
+/**
+ * W, Q and km data from database 'wsts' spiced with interpolation algorithms.
+ */
+public class WstValueTable
+implements   Serializable
+{
+    private static Logger log = Logger.getLogger(WstValueTable.class);
+
+    public static final int DEFAULT_Q_STEPS = 500;
+
+    public static final int RELATE_WS_SAMPLES = 200;
+
+    /**
+     * A Column in the table, typically representing one measurement session.
+     */
+    public static final class Column
+    implements                Serializable
+    {
+        protected String name;
+
+        protected QRangeTree qRangeTree;
+
+        public Column() {
+        }
+
+        public Column(String name) {
+            this.name = name;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public QRangeTree getQRangeTree() {
+            return qRangeTree;
+        }
+
+        public void setQRangeTree(QRangeTree qRangeTree) {
+            this.qRangeTree = qRangeTree;
+        }
+    } // class Column
+
+    /**
+     * A (weighted) position used for interpolation.
+     */
+    public static final class QPosition {
+
+        protected int    index;
+        protected double weight;
+
+        public QPosition() {
+        }
+
+        public QPosition(int index, double weight) {
+            this.index  = index;
+            this.weight = weight;
+        }
+
+        public QPosition set(int index, double weight) {
+            this.index  = index;
+            this.weight = weight;
+            return this;
+        }
+
+    } // class Position
+
+    public static final class SplineFunction {
+
+        public PolynomialSplineFunction spline;
+        public double []                splineQs;
+        public double []                splineWs;
+
+        public SplineFunction(
+            PolynomialSplineFunction spline,
+            double []                splineQs,
+            double []                splineWs
+        ) {
+            this.spline   = spline;
+            this.splineQs = splineQs;
+            this.splineWs = splineWs;
+        }
+
+        public double [][] sample(
+            int         numSamples,
+            double      km,
+            Calculation errors
+        ) {
+            double minQ = getQMin();
+            double maxQ = getQMax();
+
+            double [] outWs = new double[numSamples];
+            double [] outQs = new double[numSamples];
+
+            Arrays.fill(outWs, Double.NaN);
+            Arrays.fill(outQs, Double.NaN);
+
+            double stepWidth = (maxQ - minQ)/numSamples;
+
+            try {
+                double q = minQ;
+                for (int i = 0; i < outWs.length; ++i, q += stepWidth) {
+                    outWs[i] = spline.value(outQs[i] = q);
+                }
+            }
+            catch (ArgumentOutsideDomainException aode) {
+                if (errors != null) {
+                    errors.addProblem(km, "spline.interpolation.failed");
+                }
+                log.error("spline interpolation failed.", aode);
+            }
+
+            return new double [][] { outWs, outQs };
+        }
+
+        public double getQMin() {
+            return Math.min(splineQs[0], splineQs[splineQs.length-1]);
+        }
+
+        public double getQMax() {
+            return Math.max(splineQs[0], splineQs[splineQs.length-1]);
+        }
+
+        /** Constructs a continues index between the columns to Qs. */
+        public PolynomialSplineFunction createIndexQRelation() {
+
+            double [] indices = new double[splineQs.length];
+            for (int i = 0; i < indices.length; ++i) {
+                indices[i] = i;
+            }
+
+            try {
+                SplineInterpolator interpolator = new SplineInterpolator();
+                return interpolator.interpolate(indices, splineQs);
+            }
+            catch (MathIllegalArgumentException miae) {
+                // Ignore me!
+            }
+            return null;
+        }
+    } // class SplineFunction
+
+    /**
+     * A row, typically a position where measurements were taken.
+     */
+    public static final class Row
+    implements                Serializable, Comparable<Row>
+    {
+        double    km;
+        double [] ws;
+
+        public Row() {
+        }
+
+        public Row(double km) {
+            this.km = km;
+        }
+
+        public Row(double km, double [] ws) {
+            this(km);
+            this.ws = ws;
+        }
+
+        /**
+         * Compare according to place of measurement (km).
+         */
+        public int compareTo(Row other) {
+            double d = km - other.km;
+            if (d < -0.0001) return -1;
+            if (d >  0.0001) return +1;
+            return 0;
+        }
+
+        /**
+         * Interpolate Ws, given Qs and a km.
+         *
+         * @param iqs Given ("input") Qs.
+         * @param ows Resulting ("output") Ws.
+         * @param table Table of which to use data for interpolation.
+         */
+        public void interpolateW(
+            Row           other,
+            double        km,
+            double []     iqs,
+            double []     ows,
+            WstValueTable table,
+            Calculation   errors
+        ) {
+            double kmWeight = Linear.factor(km, this.km, other.km);
+
+            QPosition qPosition = new QPosition();
+
+            for (int i = 0; i < iqs.length; ++i) {
+                if (table.getQPosition(km, iqs[i], qPosition) != null) {
+                    double wt =       getW(qPosition);
+                    double wo = other.getW(qPosition);
+                    if (Double.isNaN(wt) || Double.isNaN(wo)) {
+                        if (errors != null) {
+                            errors.addProblem(
+                                km, "cannot.find.w.for.q", iqs[i]);
+                        }
+                        ows[i] = Double.NaN;
+                    }
+                    else {
+                        ows[i] = Linear.weight(kmWeight, wt, wo);
+                    }
+                }
+                else {
+                    if (errors != null) {
+                        errors.addProblem(km, "cannot.find.q", iqs[i]);
+                    }
+                    ows[i] = Double.NaN;
+                }
+            }
+        }
+
+
+        public SplineFunction createSpline(
+            WstValueTable table,
+            Calculation   errors
+        ) {
+            int W = ws.length;
+
+            if (W < 1) {
+                if (errors != null) {
+                    errors.addProblem(km, "no.ws.found");
+                }
+                return null;
+            }
+
+            double [] splineQs = new double[W];
+
+            for (int i = 0; i < W; ++i) {
+                double sq = table.getQIndex(i, km);
+                if (Double.isNaN(sq) && errors != null) {
+                    errors.addProblem(
+                        km, "no.q.found.in.column", (i+1));
+                }
+                splineQs[i] = sq;
+            }
+
+            try {
+                SplineInterpolator interpolator = new SplineInterpolator();
+                PolynomialSplineFunction spline =
+                    interpolator.interpolate(splineQs, ws);
+
+                return new SplineFunction(spline, splineQs, ws);
+            }
+            catch (MathIllegalArgumentException miae) {
+                if (errors != null) {
+                    errors.addProblem(km, "spline.creation.failed");
+                }
+                log.error("spline creation failed", miae);
+            }
+            return null;
+        }
+
+        public SplineFunction createSpline(
+            Row           other,
+            double        km,
+            WstValueTable table,
+            Calculation   errors
+        ) {
+            int W = Math.min(ws.length, other.ws.length);
+
+            if (W < 1) {
+                if (errors != null) {
+                    errors.addProblem("no.ws.found");
+                }
+                return null;
+            }
+
+            double factor = Linear.factor(km, this.km, other.km);
+
+            double [] splineQs = new double[W];
+            double [] splineWs = new double[W];
+
+            for (int i = 0; i < W; ++i) {
+                double wws = Linear.weight(factor, ws[i], other.ws[i]);
+                double wqs = Linear.weight(
+                    factor,
+                    table.getQIndex(i, km),
+                    table.getQIndex(i, other.km));
+
+                if (Double.isNaN(wws) || Double.isNaN(wqs)) {
+                    if (errors != null) {
+                        errors.addProblem(km, "cannot.find.w.or.q");
+                    }
+                }
+
+                splineWs[i] = wws;
+                splineQs[i] = wqs;
+            }
+
+            SplineInterpolator interpolator = new SplineInterpolator();
+
+            try {
+                PolynomialSplineFunction spline =
+                    interpolator.interpolate(splineQs, splineWs);
+
+                return new SplineFunction(spline, splineQs, splineWs);
+            }
+            catch (MathIllegalArgumentException miae) {
+                if (errors != null) {
+                    errors.addProblem(km, "spline.creation.failed");
+                }
+                log.error("spline creation failed", miae);
+            }
+
+            return null;
+        }
+
+        public double [][] interpolateWQ(
+            Row           other,
+            double        km,
+            int           steps,
+            WstValueTable table,
+            Calculation   errors
+        ) {
+            SplineFunction sf = createSpline(other, km, table, errors);
+
+            return sf != null
+                ? sf.sample(steps, km, errors)
+                : new double[2][0];
+        }
+
+
+        public double [][] interpolateWQ(
+            int           steps,
+            WstValueTable table,
+            Calculation   errors
+        ) {
+            SplineFunction sf = createSpline(table, errors);
+
+            return sf != null
+                ? sf.sample(steps, km, errors)
+                : new double[2][0];
+        }
+
+
+        public double getW(QPosition qPosition) {
+            int    index  = qPosition.index;
+            double weight = qPosition.weight;
+
+            return weight == 1.0
+                ? ws[index]
+                : Linear.weight(weight, ws[index-1], ws[index]);
+        }
+
+        public double getW(
+            Row       other,
+            double    km,
+            QPosition qPosition
+        ) {
+            double kmWeight = Linear.factor(km, this.km, other.km);
+
+            int    index  = qPosition.index;
+            double weight = qPosition.weight;
+
+            double tw, ow;
+
+            if (weight == 1.0) {
+                tw = ws[index];
+                ow = other.ws[index];
+            }
+            else {
+                tw = Linear.weight(weight, ws[index-1], ws[index]);
+                ow = Linear.weight(weight, other.ws[index-1], other.ws[index]);
+            }
+
+            return Linear.weight(kmWeight, tw, ow);
+        }
+
+        public double [] findQsForW(double w, WstValueTable table) {
+
+            TDoubleArrayList qs = new TDoubleArrayList();
+
+            if (ws.length > 0 && Math.abs(ws[0]-w) < 0.000001) {
+                double q = table.getQIndex(0, km);
+                if (!Double.isNaN(q)) {
+                    qs.add(q);
+                }
+            }
+
+            for (int i = 1; i < ws.length; ++i) {
+                double w2 = ws[i];
+                if (Double.isNaN(w2)) {
+                    continue;
+                }
+                if (Math.abs(w2-w) < 0.000001) {
+                    double q = table.getQIndex(i, km);
+                    if (!Double.isNaN(q)) {
+                        qs.add(q);
+                    }
+                    continue;
+                }
+                double w1 = ws[i-1];
+                if (Double.isNaN(w1)) {
+                    continue;
+                }
+
+                if (w < Math.min(w1, w2) || w > Math.max(w1, w2)) {
+                    continue;
+                }
+
+                double q1 = table.getQIndex(i-1, km);
+                double q2 = table.getQIndex(i,   km);
+                if (Double.isNaN(q1) || Double.isNaN(q2)) {
+                    continue;
+                }
+
+                double q = Linear.linear(w, w1, w2, q1, q2);
+                qs.add(q);
+            }
+
+            return qs.toNativeArray();
+        }
+
+        public double [] findQsForW(
+            Row           other,
+            double        w,
+            double        km,
+            WstValueTable table
+        ) {
+            TDoubleArrayList qs = new TDoubleArrayList();
+
+            double factor = Linear.factor(km, this.km, other.km);
+
+            if (ws.length > 0) {
+                double wt = Linear.weight(factor, ws[0], other.ws[0]);
+                if (!Double.isNaN(wt)) {
+                    double q = table.getQIndex(0, km);
+                    if (!Double.isNaN(q)) {
+                        qs.add(q);
+                    }
+                }
+            }
+
+            for (int i = 1; i < ws.length; ++i) {
+                double w2 = Linear.weight(factor, ws[i], other.ws[i]);
+                if (Double.isNaN(w2)) {
+                    continue;
+                }
+                if (Math.abs(w2-w) < 0.000001) {
+                    double q = table.getQIndex(i, km);
+                    if (!Double.isNaN(q)) {
+                        qs.add(q);
+                    }
+                    continue;
+                }
+                double w1 = Linear.weight(factor, ws[i-1], other.ws[i-1]);
+                if (Double.isNaN(w1)) {
+                    continue;
+                }
+
+                if (w < Math.min(w1, w2) || w > Math.max(w1, w2)) {
+                    continue;
+                }
+
+                double q1 = table.getQIndex(i-1, km);
+                double q2 = table.getQIndex(i,   km);
+                if (Double.isNaN(q1) || Double.isNaN(q2)) {
+                    continue;
+                }
+
+                double q = Linear.linear(w, w1, w2, q1, q2);
+                qs.add(q);
+            }
+
+            return qs.toNativeArray();
+        }
+
+        public double [] getMinMaxW(double [] result) {
+            double minW =  Double.MAX_VALUE;
+            double maxW = -Double.MAX_VALUE;
+            for (int i = 0; i < ws.length; ++i) {
+                double w = ws[i];
+                if (w < minW) minW = w;
+                if (w > maxW) maxW = w;
+            }
+            result[0] = minW;
+            result[1] = maxW;
+            return result;
+        }
+
+        public double [] getMinMaxW(Row other, double km, double [] result) {
+            double [] m1 = this .getMinMaxW(new double [2]);
+            double [] m2 = other.getMinMaxW(new double [2]);
+            double factor = Linear.factor(km, this.km, other.km);
+            result[0] = Linear.weight(factor, m1[0], m2[0]);
+            result[1] = Linear.weight(factor, m1[1], m2[1]);
+            return result;
+        }
+    } // class Row
+
+    /** Rows in table. */
+    protected List<Row> rows;
+
+    /** Columns in table. */
+    protected Column [] columns;
+
+    public WstValueTable() {
+        rows = new ArrayList<Row>();
+    }
+
+    public WstValueTable(Column [] columns) {
+        this();
+        this.columns = columns;
+    }
+
+    public WstValueTable(Column [] columns, List<Row> rows) {
+        this.columns = columns;
+        this.rows    = rows;
+    }
+
+    public Column [] getColumns() {
+        return columns;
+    }
+
+    /** Sort rows (by km). */
+    public void sortRows() {
+        Collections.sort(rows);
+    }
+
+    /**
+     * @param km Given kilometer.
+     * @param qs Given Q values.
+     * @param ws output parameter.
+     */
+    public double [] interpolateW(double km, double [] qs, double [] ws) {
+        return interpolateW(km, qs, ws, null);
+    }
+
+
+    /**
+     * @param ws (output parameter), gets returned.
+     * @return output parameter ws.
+     */
+    public double [] interpolateW(
+        double      km,
+        double []   qs,
+        double []   ws,
+        Calculation errors
+    ) {
+        int rowIndex = Collections.binarySearch(rows, new Row(km));
+
+        QPosition qPosition = new QPosition();
+
+        if (rowIndex >= 0) { // direct row match
+            Row row = rows.get(rowIndex);
+            for (int i = 0; i < qs.length; ++i) {
+                if (getQPosition(km, qs[i], qPosition) == null) {
+                    if (errors != null) {
+                        errors.addProblem(km, "cannot.find.q", qs[i]);
+                    }
+                    ws[i] = Double.NaN;
+                }
+                else {
+                    if (Double.isNaN(ws[i] = row.getW(qPosition))
+                    && errors != null) {
+                        errors.addProblem(
+                            km, "cannot.find.w.for.q", qs[i]);
+                    }
+                }
+            }
+        }
+        else { // needs bilinear interpolation
+            rowIndex = -rowIndex -1;
+
+            if (rowIndex < 1 || rowIndex >= rows.size()) {
+                // do not extrapolate
+                Arrays.fill(ws, Double.NaN);
+                if (errors != null) {
+                    errors.addProblem(km, "km.not.found");
+                }
+            }
+            else {
+                Row r1 = rows.get(rowIndex-1);
+                Row r2 = rows.get(rowIndex);
+                r1.interpolateW(r2, km, qs, ws, this, errors);
+            }
+        }
+
+        return ws;
+    }
+
+    public double [] getMinMaxQ(double km) {
+        return getMinMaxQ(km, new double [2]);
+    }
+
+    public double [] getMinMaxQ(double km, double [] result) {
+        double minQ =  Double.MAX_VALUE;
+        double maxQ = -Double.MAX_VALUE;
+
+        for (int i = 0; i < columns.length; ++i) {
+            double q = columns[i].getQRangeTree().findQ(km);
+            if (!Double.isNaN(q)) {
+                if (q < minQ) minQ = q;
+                if (q > maxQ) maxQ = q;
+            }
+        }
+
+        if (minQ < Double.MAX_VALUE) {
+            result[0] = minQ;
+            result[1] = maxQ;
+            return result;
+        }
+
+        return null;
+    }
+
+    public double [] getMinMaxQ(double from, double to, double step) {
+        double [] result = new double[2];
+
+        double minQ =  Double.MAX_VALUE;
+        double maxQ = -Double.MAX_VALUE;
+
+        if (from > to) {
+            double tmp = from;
+            from = to;
+            to = tmp;
+        }
+
+        step = Math.max(Math.abs(step), 0.0001);
+
+        double d = from;
+        for (; d <= to; d += step) {
+            if (getMinMaxQ(d, result) != null) {
+                if (result[0] < minQ) minQ = result[0];
+                if (result[1] > maxQ) maxQ = result[1];
+            }
+        }
+
+        if (d != to) {
+            if (getMinMaxQ(to, result) != null) {
+                if (result[0] < minQ) minQ = result[0];
+                if (result[1] > maxQ) maxQ = result[1];
+            }
+        }
+
+        return minQ < Double.MAX_VALUE
+            ? new double [] { minQ, maxQ }
+            : null;
+    }
+
+    public double [] getMinMaxW(double km) {
+        return getMinMaxW(km, new double [2]);
+
+    }
+    public double [] getMinMaxW(double km, double [] result) {
+        int rowIndex = Collections.binarySearch(rows, new Row(km));
+
+        if (rowIndex >= 0) {
+            return rows.get(rowIndex).getMinMaxW(result);
+        }
+
+        rowIndex = -rowIndex -1;
+
+        if (rowIndex < 1 || rowIndex >= rows.size()) {
+            // do not extrapolate
+            return null;
+        }
+
+        Row r1 = rows.get(rowIndex-1);
+        Row r2 = rows.get(rowIndex);
+
+        return r1.getMinMaxW(r2, km, result);
+    }
+
+    public double [] getMinMaxW(double from, double to, double step) {
+        double [] result = new double[2];
+
+        double minW =  Double.MAX_VALUE;
+        double maxW = -Double.MAX_VALUE;
+
+        if (from > to) {
+            double tmp = from;
+            from = to;
+            to = tmp;
+        }
+
+        step = Math.max(Math.abs(step), 0.0001);
+
+        double d = from;
+        for (; d <= to; d += step) {
+            if (getMinMaxW(d, result) != null) {
+                if (result[0] < minW) minW = result[0];
+                if (result[1] > maxW) maxW = result[1];
+            }
+        }
+
+        if (d != to) {
+            if (getMinMaxW(to, result) != null) {
+                if (result[0] < minW) minW = result[0];
+                if (result[1] > maxW) maxW = result[1];
+            }
+        }
+
+        return minW < Double.MAX_VALUE
+            ? new double [] { minW, maxW }
+            : null;
+    }
+
+    /**
+     * Interpolate W and Q values at a given km.
+     */
+    public double [][] interpolateWQ(double km) {
+        return interpolateWQ(km, null);
+    }
+
+    /**
+     * Interpolate W and Q values at a given km.
+     *
+     * @param errors where to store errors.
+     *
+     * @return double double array, first index Ws, second Qs.
+     */
+    public double [][] interpolateWQ(double km, Calculation errors) {
+        return interpolateWQ(km, DEFAULT_Q_STEPS, errors);
+    }
+
+
+    /**
+     * Interpolate W and Q values at a given km.
+     */
+    public double [][] interpolateWQ(double km, int steps, Calculation errors) {
+
+        int rowIndex = Collections.binarySearch(rows, new Row(km));
+
+        if (rowIndex >= 0) { // direct row match
+            Row row = rows.get(rowIndex);
+            return row.interpolateWQ(steps, this, errors);
+        }
+
+        rowIndex = -rowIndex -1;
+
+        if (rowIndex < 1 || rowIndex >= rows.size()) {
+            // do not extrapolate
+            if (errors != null) {
+                errors.addProblem(km, "km.not.found");
+            }
+            return new double[2][0];
+        }
+
+        Row r1 = rows.get(rowIndex-1);
+        Row r2 = rows.get(rowIndex);
+
+        return r1.interpolateWQ(r2, km, steps, this, errors);
+    }
+
+    public boolean interpolate(
+        double    km,
+        double [] out,
+        QPosition qPosition,
+        Function  qFunction
+    ) {
+        int R1 = rows.size()-1;
+
+        out[1] = qFunction.value(getQ(qPosition, km));
+
+        if (Double.isNaN(out[1])) {
+            return false;
+        }
+
+        QPosition nPosition = new QPosition();
+        if (getQPosition(km, out[1], nPosition) == null) {
+            return false;
+        }
+
+        int rowIndex = Collections.binarySearch(rows, new Row(km));
+
+        if (rowIndex >= 0) {
+            // direct row match
+            out[0] = rows.get(rowIndex).getW(nPosition);
+            return !Double.isNaN(out[0]);
+        }
+
+        rowIndex = -rowIndex -1;
+
+        if (rowIndex < 1 || rowIndex > R1) {
+            // do not extrapolate
+            return false;
+        }
+
+        Row r1 = rows.get(rowIndex-1);
+        Row r2 = rows.get(rowIndex);
+        out[0] = r1.getW(r2, km, nPosition);
+
+        return !Double.isNaN(out[0]);
+    }
+
+
+    /**
+     * Look up interpolation of a Q at given positions.
+     *
+     * @param q           the non-interpolated Q value.
+     * @param referenceKm the reference km (e.g. gauge position).
+     * @param kms         positions for which to interpolate.
+     * @param ws          (output) resulting interpolated ws.
+     * @param qs          (output) resulting interpolated qs.
+     * @param errors      calculation object to store errors.
+     */
+    public QPosition interpolate(
+        double      q,
+        double      referenceKm,
+        double []   kms,
+        double []   ws,
+        double []   qs,
+        Calculation errors
+    ) {
+        return interpolate(
+            q, referenceKm, kms, ws, qs, 0, kms.length, errors);
+    }
+
+    public QPosition interpolate(
+        double      q,
+        double      referenceKm,
+        double []   kms,
+        double []   ws,
+        double []   qs,
+        int         startIndex,
+        int         length,
+        Calculation errors
+    ) {
+        QPosition qPosition = getQPosition(referenceKm, q);
+
+        if (qPosition == null) {
+            // we cannot locate q at km
+            Arrays.fill(ws, Double.NaN);
+            Arrays.fill(qs, Double.NaN);
+            if (errors != null) {
+                errors.addProblem(referenceKm, "cannot.find.q", q);
+            }
+            return null;
+        }
+
+        Row kmKey = new Row();
+
+        int R1 = rows.size()-1;
+
+        for (int i = startIndex, end = startIndex+length; i < end; ++i) {
+
+            if (Double.isNaN(qs[i] = getQ(qPosition, kms[i]))) {
+                if (errors != null) {
+                    errors.addProblem(kms[i], "cannot.find.q", q);
+                }
+                ws[i] = Double.NaN;
+                continue;
+            }
+
+            kmKey.km = kms[i];
+            int rowIndex = Collections.binarySearch(rows, kmKey);
+
+            if (rowIndex >= 0) {
+                // direct row match
+                if (Double.isNaN(ws[i] = rows.get(rowIndex).getW(qPosition))
+                && errors != null) {
+                    errors.addProblem(kms[i], "cannot.find.w.for.q", q);
+                }
+                continue;
+            }
+
+            rowIndex = -rowIndex -1;
+
+            if (rowIndex < 1 || rowIndex > R1) {
+                // do not extrapolate
+                if (errors != null) {
+                    errors.addProblem(kms[i], "km.not.found");
+                }
+                ws[i] = Double.NaN;
+                continue;
+            }
+            Row r1 = rows.get(rowIndex-1);
+            Row r2 = rows.get(rowIndex);
+
+            if (Double.isNaN(ws[i] = r1.getW(r2, kms[i], qPosition))
+            && errors != null) {
+                errors.addProblem(kms[i], "cannot.find.w.for.q", q);
+            }
+        }
+
+        return qPosition;
+    }
+
+    /**
+     * Linearly interpolate w at a km at a column of two rows.
+     *
+     * @param km   position for which to interpolate.
+     * @param row1 first row.
+     * @param row2 second row.
+     * @param col  column-index at which to look.
+     *
+     * @return Linearly interpolated w, NaN if one of the given rows was null.
+     */
+    public static double linearW(double km, Row row1, Row row2, int col) {
+        if (row1 == null || row2 == null) {
+            return Double.NaN;
+        }
+
+        return Linear.linear(km,
+            row1.km, row2.km,
+            row1.ws[col], row2.ws[col]);
+    }
+
+    /**
+     * Do interpolation/lookup of W and Q within columns (i.e. ignoring values
+     * of other columns).
+     * @param km position (km) at which to interpolate/lookup.
+     * @return [[q0, q1, .. qx] , [w0, w1, .. wx]] (can contain NaNs)
+     */
+    public double [][] interpolateWQColumnwise(double km) {
+        log.debug("WstValueTable.interpolateWQColumnwise");
+        double [] qs = new double[columns.length];
+        double [] ws = new double[columns.length];
+
+        // Find out row from where we will start searching.
+        int rowIndex = Collections.binarySearch(rows, new Row(km));
+
+        if (rowIndex < 0) {
+            rowIndex = -rowIndex -1;
+        }
+
+        // TODO Beyond definition, we could stop more clever.
+        if (rowIndex >= rows.size()) {
+            rowIndex = rows.size() -1;
+        }
+
+        Row startRow = rows.get(rowIndex);
+
+        for (int col = 0; col < columns.length; col++) {
+            qs[col] = columns[col].getQRangeTree().findQ(km);
+            if (startRow.km == km && startRow.ws[col] != Double.NaN) {
+                // Great. W is defined at km.
+                ws[col] = startRow.ws[col];
+                continue;
+            }
+
+            // Search neighbouring rows that define w at this col.
+            Row rowBefore = null;
+            Row rowAfter  = null;
+            for (int before = rowIndex -1; before >= 0; before--) {
+                if (!Double.isNaN(rows.get(before).ws[col])) {
+                    rowBefore = rows.get(before);
+                    break;
+                }
+            }
+            if (rowBefore != null) {
+                for (int after = rowIndex, R = rows.size(); after < R; after++) {
+                    if (!Double.isNaN(rows.get(after).ws[col])) {
+                        rowAfter = rows.get(after);
+                        break;
+                    }
+                }
+            }
+
+            ws[col] = linearW(km, rowBefore, rowAfter, col);
+        }
+
+        return new double [][] {qs, ws};
+    }
+
+    public double [] findQsForW(double km, double w) {
+
+        int rowIndex = Collections.binarySearch(rows, new Row(km));
+
+        if (rowIndex >= 0) {
+            return rows.get(rowIndex).findQsForW(w, this);
+        }
+
+        rowIndex = -rowIndex - 1;
+
+        if (rowIndex < 1 || rowIndex >= rows.size()) {
+            // Do not extrapolate.
+            return new double[0];
+        }
+
+        // Needs bilinear interpolation.
+        Row r1 = rows.get(rowIndex-1);
+        Row r2 = rows.get(rowIndex);
+
+        return r1.findQsForW(r2, w, km, this);
+    }
+
+    protected SplineFunction createSpline(double km, Calculation errors) {
+
+        int rowIndex = Collections.binarySearch(rows, new Row(km));
+
+        if (rowIndex >= 0) {
+            SplineFunction sf = rows.get(rowIndex).createSpline(this, errors);
+            if (sf == null && errors != null) {
+                errors.addProblem(km, "cannot.create.wq.relation");
+            }
+            return sf;
+        }
+
+        rowIndex = -rowIndex - 1;
+
+        if (rowIndex < 1 || rowIndex >= rows.size()) {
+            // Do not extrapolate.
+            if (errors != null) {
+                errors.addProblem(km, "km.not.found");
+            }
+            return null;
+        }
+
+        // Needs bilinear interpolation.
+        Row r1 = rows.get(rowIndex-1);
+        Row r2 = rows.get(rowIndex);
+
+        SplineFunction sf = r1.createSpline(r2, km, this, errors);
+        if (sf == null && errors != null) {
+            errors.addProblem(km, "cannot.create.wq.relation");
+        }
+
+        return sf;
+    }
+
+    /** 'Bezugslinienverfahren' */
+    public double [][] relateWs(
+        double      km1,
+        double      km2,
+        Calculation errors
+    ) {
+        return relateWs(km1, km2, RELATE_WS_SAMPLES, errors);
+    }
+
+    private static class ErrorHandler {
+
+        boolean     hasErrors;
+        Calculation errors;
+
+        ErrorHandler(Calculation errors) {
+            this.errors = errors;
+        }
+
+        void error(double km, String key, Object ... args) {
+            if (errors != null && !hasErrors) {
+                hasErrors = true;
+                errors.addProblem(km, key, args);
+            }
+        }
+    } // class ErrorHandler
+
+
+    /* TODO: Add optimized methods of relateWs to relate one
+     *       start km to many end kms. The index generation/spline stuff for
+     *       the start km is always the same.
+     */
+    public double [][] relateWs(
+        double      km1,
+        double      km2,
+        int         numSamples,
+        Calculation errors
+    ) {
+        SplineFunction sf1 = createSpline(km1, errors);
+        if (sf1 == null) {
+            return new double[2][0];
+        }
+
+        SplineFunction sf2 = createSpline(km2, errors);
+        if (sf2 == null) {
+            return new double[2][0];
+        }
+
+        PolynomialSplineFunction iQ1 = sf1.createIndexQRelation();
+        if (iQ1 == null) {
+            if (errors != null) {
+                errors.addProblem(km1, "cannot.create.index.q.relation");
+            }
+            return new double[2][0];
+        }
+
+        PolynomialSplineFunction iQ2 = sf2.createIndexQRelation();
+        if (iQ2 == null) {
+            if (errors != null) {
+                errors.addProblem(km2, "cannot.create.index.q.relation");
+            }
+            return new double[2][0];
+        }
+
+        int N = Math.min(sf1.splineQs.length, sf2.splineQs.length);
+        double stepWidth = N/(double)numSamples;
+
+        PolynomialSplineFunction qW1 = sf1.spline;
+        PolynomialSplineFunction qW2 = sf2.spline;
+
+        TDoubleArrayList ws1 = new TDoubleArrayList(numSamples);
+        TDoubleArrayList ws2 = new TDoubleArrayList(numSamples);
+        TDoubleArrayList qs1 = new TDoubleArrayList(numSamples);
+        TDoubleArrayList qs2 = new TDoubleArrayList(numSamples);
+
+        ErrorHandler err = new ErrorHandler(errors);
+
+        int i = 0;
+        for (double p = 0d; p <= N-1; p += stepWidth, ++i) {
+
+            double q1;
+            try {
+                q1 = iQ1.value(p);
+            }
+            catch (ArgumentOutsideDomainException aode) {
+                err.error(km1, "w.w.qkm1.failed", p);
+                continue;
+            }
+
+            double w1;
+            try {
+                w1 = qW1.value(q1);
+            }
+            catch (ArgumentOutsideDomainException aode) {
+                err.error(km1, "w.w.wkm1.failed", q1, p);
+                continue;
+            }
+
+            double q2;
+            try {
+                q2 = iQ2.value(p);
+            }
+            catch (ArgumentOutsideDomainException aode) {
+                err.error(km2, "w.w.qkm2.failed", p);
+                continue;
+            }
+
+            double w2;
+            try {
+                w2 = qW2.value(q2);
+            }
+            catch (ArgumentOutsideDomainException aode) {
+                err.error(km2, "w.w.wkm2.failed", q2, p);
+                continue;
+            }
+
+            ws1.add(w1);
+            ws2.add(w2);
+            qs1.add(q1);
+            qs2.add(q2);
+        }
+
+        return new double [][] {
+            ws1.toNativeArray(),
+            qs1.toNativeArray(),
+            ws2.toNativeArray(),
+            qs2.toNativeArray() };
+    }
+
+    public QPosition getQPosition(double km, double q) {
+        return getQPosition(km, q, new QPosition());
+    }
+
+    public QPosition getQPosition(double km, double q, QPosition qPosition) {
+
+        if (columns.length == 0) {
+            return null;
+        }
+
+        double qLast = columns[0].getQRangeTree().findQ(km);
+
+        if (Math.abs(qLast - q) < 0.00001) {
+            return qPosition.set(0, 1d);
+        }
+
+        for (int i = 1; i < columns.length; ++i) {
+            double qCurrent = columns[i].getQRangeTree().findQ(km);
+            if (Math.abs(qCurrent - q) < 0.00001) {
+                return qPosition.set(i, 1d);
+            }
+
+            double qMin, qMax;
+            if (qLast < qCurrent) { qMin = qLast; qMax = qCurrent; }
+            else                  { qMin = qCurrent; qMax = qLast; }
+
+            if (q > qMin && q < qMax) {
+                double weight = Linear.factor(q, qLast, qCurrent);
+                return qPosition.set(i, weight);
+            }
+            qLast = qCurrent;
+        }
+
+        return null;
+    }
+
+    public double getQIndex(int index, double km) {
+        return columns[index].getQRangeTree().findQ(km);
+    }
+
+    public double getQ(QPosition qPosition, double km) {
+        int    index  = qPosition.index;
+        double weight = qPosition.weight;
+
+        if (weight == 1d) {
+            return columns[index].getQRangeTree().findQ(km);
+        }
+        double q1 = columns[index-1].getQRangeTree().findQ(km);
+        double q2 = columns[index  ].getQRangeTree().findQ(km);
+        return Linear.weight(weight, q1, q2);
+    }
+
+    public double [][] interpolateTabulated(double km) {
+        return interpolateTabulated(km, new double[2][columns.length]);
+    }
+
+    public double [][] interpolateTabulated(double km, double [][] result) {
+
+        int rowIndex = Collections.binarySearch(rows, new Row(km));
+
+        if (rowIndex >= 0) {
+            // Direct hit -> copy ws.
+            Row row = rows.get(rowIndex);
+            System.arraycopy(
+                row.ws, 0, result[0], 0,
+                Math.min(row.ws.length, result[0].length));
+        }
+        else {
+            rowIndex = -rowIndex -1;
+            if (rowIndex < 1 || rowIndex >= rows.size()) {
+                // Out of bounds.
+                return null;
+            }
+            // Interpolate ws.
+            Row r1 = rows.get(rowIndex-1);
+            Row r2 = rows.get(rowIndex);
+            double factor = Linear.factor(km, r1.km, r2.km);
+            Linear.weight(factor, r1.ws, r2.ws, result[0]);
+        }
+
+        double [] qs = result[1];
+        for (int i = Math.min(qs.length, columns.length)-1; i >= 0; --i) {
+            qs[i] = columns[i].getQRangeTree().findQ(km);
+        }
+        return result;
+    }
+
+
+    /** Find ranges that are between km1 and km2 (inclusive?) */
+    public List<Range> findSegments(double km1, double km2) {
+        return columns.length != 0
+            ? columns[columns.length-1].getQRangeTree().findSegments(km1, km2)
+            : Collections.<Range>emptyList();
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WstValueTableCacheKey.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,33 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.io.Serializable;
+
+/**
+ * Cache Key (identifier) for WstValueTables.
+ */
+public final class WstValueTableCacheKey
+implements         Serializable
+{
+    public static final String CACHE_NAME = "wst-value-table";
+
+    private int riverId;
+    private int kind;
+
+    public WstValueTableCacheKey(int riverId, int kind) {
+        this.riverId = riverId;
+        this.kind    = kind;
+    }
+
+    public int hashCode() {
+        return (riverId << 8) | kind;
+    }
+
+    public boolean equals(Object other) {
+        if (!(other instanceof WstValueTableCacheKey)) {
+            return false;
+        }
+        WstValueTableCacheKey o = (WstValueTableCacheKey)other;
+        return riverId == o.riverId && kind == o.kind;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WstValueTableFactory.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,520 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.ArrayList;
+
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.Element;
+
+import org.dive4elements.river.artifacts.cache.CacheFactory;
+
+import org.dive4elements.river.backend.SessionHolder;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.river.model.River;
+import org.dive4elements.river.model.Wst;
+
+import org.hibernate.Session;
+import org.hibernate.Query;
+import org.hibernate.SQLQuery;
+
+import org.hibernate.type.StandardBasicTypes;
+
+/**
+ * Creates WstValueTable s from database.
+ * WstValueTable s are used to interpolate given w/q/km values.
+ */
+public class WstValueTableFactory
+{
+    private static Logger log = Logger.getLogger(WstValueTableFactory.class);
+
+    public static final int DEFAULT_KIND = 0;
+
+    // TODO: put this into a property file
+
+    public static final String HQL_WST =
+        "from Wst where river=:river and kind=:kind";
+
+    public static final String SQL_SELECT_NAMES_POS =
+        "SELECT position, name FROM wst_columns " +
+        "WHERE wst_id = :wst_id ORDER BY position";
+
+    /** Select Qs for wst (view sorted by column). */
+    public static final String SQL_SELECT_QS =
+        "SELECT column_pos, q, a, b FROM wst_q_values " +
+        "WHERE wst_id = :wst_id";
+
+    // (sorted by km)
+    public static final String SQL_SELECT_WS =
+        "SELECT km, w, column_pos FROM wst_w_values " +
+        "WHERE wst_id = :wst_id";
+
+    /** Statement to query qranges of a single column. */
+    public static final String SQL_SELECT_QS_AT_COL =
+        "SELECT q, a, b FROM wst_q_values " +
+        "WHERE wst_id = :wst_id AND column_pos = :column_pos";
+
+    // (sorted by km)
+    public static final String SQL_SELECT_WS_AT_COL =
+        "SELECT km, w FROM wst_w_values " +
+        "WHERE wst_id = :wst_id AND column_pos = :column_pos";
+
+
+    private WstValueTableFactory() {
+    }
+
+
+    public static WstValueTable getTable(River river) {
+        return getTable(river, DEFAULT_KIND);
+    }
+
+
+    /**
+     * Get WstValueTable to interpolate values of a given Wst.
+     */
+    public static WstValueTable getTable(int wst_id) {
+
+        Cache cache = CacheFactory.getCache(WstValueTableCacheKey.CACHE_NAME);
+
+        WstValueTableCacheKey cacheKey;
+
+        if (cache != null) {
+            // "-1" is the symbolic river-id for "no river, but wst_id".
+            cacheKey = new WstValueTableCacheKey(-1, wst_id);
+            Element element = cache.get(cacheKey);
+            if (element != null) {
+                log.debug("Got specific wst value table from cache");
+                return (WstValueTable) element.getValue();
+            }
+        }
+        else {
+            cacheKey = null;
+        }
+
+        Session session = SessionHolder.HOLDER.get();
+
+        // Fetch data for one column only.
+
+        WstValueTable.Column [] columns = loadColumns(session, wst_id);
+        int [] map = loadQRangesMap(session, columns, wst_id);
+        List<WstValueTable.Row> rows = loadRows(session, wst_id, columns.length, map);
+
+        WstValueTable valueTable = new WstValueTable(columns, rows);
+
+        if (valueTable != null && cacheKey != null) {
+            log.debug("Store wst value table in cache");
+            Element element = new Element(cacheKey, valueTable);
+            cache.put(element);
+        }
+
+        return valueTable;
+    }
+
+    /**
+     * Get Table for a specific column of a wst.
+     */
+    public static WstValueTable getWstColumnTable(int wst_id, int col_pos) {
+
+        Cache cache = CacheFactory.getCache(WstValueTableCacheKey.CACHE_NAME);
+
+        WstValueTableCacheKey cacheKey;
+
+        if (cache != null) {
+            // A negaitve/negative number is the symbolic 'river-id' for
+            // "no river and kind but wst_id and colpos".
+            cacheKey = new WstValueTableCacheKey(-wst_id, -col_pos);
+            Element element = cache.get(cacheKey);
+            if (element != null) {
+                log.debug("Got specific wst value table from cache");
+                return (WstValueTable) element.getValue();
+            }
+        }
+        else {
+            cacheKey = null;
+        }
+
+        Session session = SessionHolder.HOLDER.get();
+
+        // Fetch data for one column only.
+
+        WstValueTable.Column [] columns = loadColumn(session, wst_id, col_pos);
+        loadQRanges(session, columns, wst_id, col_pos);
+        List<WstValueTable.Row> rows = loadRowsOneColumn(session, wst_id, col_pos);
+
+        WstValueTable valueTable = new WstValueTable(columns, rows);
+
+        if (valueTable != null && cacheKey != null) {
+            log.debug("Store wst value table in cache (wst: "
+                + wst_id + "/ col: " + col_pos + ")");
+            Element element = new Element(cacheKey, valueTable);
+            cache.put(element);
+        }
+
+        return valueTable;
+    }
+
+
+    /**
+     * Get table for first wst of given kind at given river.
+     */
+    public static WstValueTable getTable(River river, int kind) {
+
+        Cache cache = CacheFactory.getCache(WstValueTableCacheKey.CACHE_NAME);
+
+        WstValueTableCacheKey cacheKey;
+
+        if (cache != null) {
+            cacheKey = new WstValueTableCacheKey(river.getId(), kind);
+            Element element = cache.get(cacheKey);
+            if (element != null) {
+                log.debug("got wst value table from cache");
+                return (WstValueTable)element.getValue();
+            }
+        }
+        else {
+            cacheKey = null;
+        }
+
+        WstValueTable valueTable = getTableUncached(river, kind);
+
+        if (valueTable != null && cacheKey != null) {
+            log.debug("store wst value table in cache");
+            Element element = new Element(cacheKey, valueTable);
+            cache.put(element);
+        }
+
+        return valueTable;
+    }
+
+    public static WstValueTable getTableUncached(River river) {
+        return getTableUncached(river, DEFAULT_KIND);
+    }
+
+    public static WstValueTable getTableUncached(River river, int kind) {
+
+        Session session = SessionHolder.HOLDER.get();
+
+        Wst wst = loadWst(session, river, kind);
+
+        if (wst == null) {
+            return null;
+        }
+
+        WstValueTable.Column [] columns = loadColumns(session, wst);
+
+        int map [] = loadQRangesMap(session, columns, wst);
+
+        List<WstValueTable.Row> rows =
+            loadRows(session, wst.getId(), columns.length, map);
+
+        return new WstValueTable(columns, rows);
+    }
+
+    /**
+     * @param kind Kind of wst.
+     */
+    protected static Wst loadWst(Session session, River river, int kind) {
+        Query query = session.createQuery(HQL_WST);
+        query.setParameter("river", river);
+        query.setInteger("kind",    kind);
+
+        List<Wst> wsts = query.list();
+
+        // TODO Multiple wsts can match, why return just the first one?
+        return wsts.isEmpty() ? null : wsts.get(0);
+    }
+
+
+    /**
+     * Load rows with a single columns result.
+     *
+     * @param session    session to use for querying db.
+     * @param wstId     id of wst (in db).
+     * @param column_pos the column_pos (within the db) of the wst_value_table
+     *                   of which the values shall be fetched.
+     *
+     * @return resultant rows.
+     */
+    protected static List<WstValueTable.Row> loadRowsOneColumn(
+        Session session,
+        int     wstId,
+        int     column_pos
+    ) {
+        SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_WS_AT_COL)
+            .addScalar("km", StandardBasicTypes.DOUBLE)
+            .addScalar("w",  StandardBasicTypes.DOUBLE);
+
+        sqlQuery.setInteger("wst_id", wstId);
+        sqlQuery.setInteger("column_pos", column_pos);
+
+        List<Object []> results = sqlQuery.list();
+
+        double [] ws = null;
+
+        List<WstValueTable.Row> rows =
+            new ArrayList<WstValueTable.Row>(results.size());
+
+        // Walk over rows.
+        for (Object [] result: results) {
+            ws = new double[1];
+            WstValueTable.Row row =
+                new WstValueTable.Row((Double) result[0], ws);
+            rows.add(row);
+
+            Double w = (Double) result[1];
+            ws[0] = w != null ? w : Double.NaN;
+        }
+
+        return rows;
+    }
+
+    protected static List<WstValueTable.Row> loadRows(
+        Session session,
+        int     wst_id,
+        int     numColumns
+    ) {
+        return loadRows(session, wst_id, numColumns, null);
+    }
+
+    protected static List<WstValueTable.Row> loadRows(
+        Session session,
+        int     wst_id,
+        int     numColumns,
+        int []  map
+    ) {
+        SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_WS)
+            .addScalar("km",         StandardBasicTypes.DOUBLE)
+            .addScalar("w",          StandardBasicTypes.DOUBLE)
+            .addScalar("column_pos", StandardBasicTypes.INTEGER);
+
+        sqlQuery.setInteger("wst_id", wst_id);
+
+        List<Object []> results = sqlQuery.list();
+
+        int lastColumn = Integer.MAX_VALUE;
+        double [] ws = null;
+
+        ArrayList<WstValueTable.Row> rows = new ArrayList<WstValueTable.Row>();
+
+        for (Object [] result: results) {
+            int column = (Integer)result[2];
+            if (column < lastColumn) {
+                ws = new double[numColumns];
+                Arrays.fill(ws, Double.NaN);
+                WstValueTable.Row row =
+                    new WstValueTable.Row((Double)result[0], ws);
+                rows.add(row);
+            }
+            Double w = (Double)result[1];
+            int index = map != null ? map[column] : column;
+            ws[index] = w != null ? w : Double.NaN;
+            lastColumn = column;
+        }
+
+        rows.trimToSize();
+        return rows;
+    }
+
+    protected static List<WstValueTable.Row> loadRows(
+        Session session,
+        Wst     wst,
+        int     numColumns
+    ) {
+        return loadRows(session, wst.getId(), numColumns);
+    }
+
+
+    protected static WstValueTable.Column [] loadColumn(
+        Session session,
+        int wst_id,
+        int col_pos
+    ) {
+        return new WstValueTable.Column [] {
+            new WstValueTable.Column(WKmsFactory.getWKmsName(col_pos, wst_id))};
+    }
+
+
+    /**
+     * Get columns from wst-id.
+     */
+    protected static WstValueTable.Column [] loadColumns(
+        Session session,
+        int wst_id
+    ) {
+        SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_NAMES_POS)
+            .addScalar("position",   StandardBasicTypes.INTEGER)
+            .addScalar("name",       StandardBasicTypes.STRING);
+
+        sqlQuery.setInteger("wst_id", wst_id);
+
+        List<Object []> columnNames = sqlQuery.list();
+
+        WstValueTable.Column [] columns =
+            new WstValueTable.Column[columnNames.size()];
+
+        for (int i = 0; i < columns.length; ++i) {
+            columns[i] = new WstValueTable.Column(
+                (String)columnNames.get(i)[1]);
+        }
+        return columns;
+    }
+
+    /**
+     * Get columns from Wst.
+     */
+    protected static WstValueTable.Column [] loadColumns(
+        Session session,
+        Wst     wst
+    ) {
+        return loadColumns(session, wst.getId());
+    }
+
+
+    /**
+     * Build a QRange-Tree.
+     */
+    protected static void loadQRanges(
+        Session                 session,
+        WstValueTable.Column [] columns,
+        int                     wst_id,
+        int                     column_pos
+    ) {
+        SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_QS_AT_COL)
+            .addScalar("q", StandardBasicTypes.DOUBLE)
+            .addScalar("a", StandardBasicTypes.DOUBLE)
+            .addScalar("b", StandardBasicTypes.DOUBLE);
+
+        sqlQuery.setInteger("wst_id",     wst_id);
+        sqlQuery.setInteger("column_pos", column_pos);
+
+        List<Object []> qRanges = sqlQuery.list();
+
+        int qSize = qRanges.size();
+
+        QRangeTree qRangeTree = new QRangeTree(
+            qRanges, QRangeTree.WITHOUT_COLUMN, 0, qSize);
+        columns[0].setQRangeTree(qRangeTree);
+    }
+
+    protected static int [] loadQRangesMap(
+        Session                 session,
+        WstValueTable.Column [] columns,
+        int                     wst_id
+    ) {
+        SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_QS)
+            .addScalar("column_pos", StandardBasicTypes.INTEGER)
+            .addScalar("q",          StandardBasicTypes.DOUBLE)
+            .addScalar("a",          StandardBasicTypes.DOUBLE)
+            .addScalar("b",          StandardBasicTypes.DOUBLE);
+
+        sqlQuery.setInteger("wst_id", wst_id);
+
+        List<Object []> qRanges = sqlQuery.list();
+
+        int     start      = -1;
+        int     Q          = qRanges.size();
+        Integer lastColumn = null;
+
+        for (int i = 0; i < Q; ++i) {
+            Object [] qRange = qRanges.get(i);
+            Integer columnId = (Integer)qRange[0];
+            if (lastColumn == null) {
+                lastColumn = columnId;
+                start = i;
+            }
+            else if (!lastColumn.equals(columnId)) {
+                QRangeTree qRangeTree = new QRangeTree(qRanges, start, i);
+                columns[lastColumn].setQRangeTree(qRangeTree);
+                lastColumn = columnId;
+                start = i;
+            }
+        }
+
+        if (start != -1) {
+            QRangeTree qRangeTree = new QRangeTree(qRanges, start, Q);
+            columns[lastColumn].setQRangeTree(qRangeTree);
+        }
+
+        return sortColumnsByAverageQ(columns);
+
+        /* This is debug code to visualize the q ranges trees
+
+        java.io.PrintWriter out = null;
+        try {
+            out = new java.io.PrintWriter(
+                new java.io.FileWriter(
+                    "/tmp/qranges" + System.currentTimeMillis() + ".dot"));
+
+            out.println("graph \"Q ranges trees\" {");
+
+            for (int i = 0; i < columns.length; ++i) {
+                QRangeTree tree = columns[i].getQRangeTree();
+                out.println(tree.toGraph());
+            }
+
+            out.println("}");
+
+            out.flush();
+        }
+        catch (java.io.IOException ioe) {
+            log.error(ioe);
+        }
+        finally {
+            if (out != null) {
+                out.close();
+            }
+        }
+        */
+    }
+
+    private static final class QIndex implements Comparable<QIndex> {
+        double q;
+        int    index;
+
+        QIndex(double q, int index) {
+            this.q     = q;
+            this.index = index;
+        }
+
+        @Override
+        public int compareTo(QIndex other) {
+            double diff = q - other.q;
+            if (diff < 0d) return -1;
+            if (diff > 0d) return +1;
+            return 0;
+        }
+    } // class QIndex
+
+    /** Ensure that the q colums are sorted in ascending order. */
+    protected static int [] sortColumnsByAverageQ(WstValueTable.Column [] columns) {
+        QIndex [] order = new QIndex[columns.length];
+        for (int i = 0; i < order.length; ++i) {
+            QRangeTree tree = columns[i].getQRangeTree();
+            double avg = tree.averageQ();
+            double max = tree.maxQ();
+            double q   = (avg+max)*0.5d;
+            order[i] = new QIndex(q, i);
+        }
+        Arrays.sort(order);
+        WstValueTable.Column [] copy = new WstValueTable.Column[order.length];
+        int [] map = new int[order.length];
+        for (int i = 0; i < copy.length; ++i) {
+            copy[i] = columns[order[i].index];
+            map[order[i].index] = i;
+        }
+        System.arraycopy(copy, 0, columns, 0, order.length);
+        return map;
+    }
+
+    protected static int [] loadQRangesMap(
+        Session                 session,
+        WstValueTable.Column [] columns,
+        Wst                     wst
+    ) {
+        return loadQRangesMap(session, columns, wst.getId());
+    }
+
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/ZoomScale.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,75 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.river.artifacts.math.Linear;
+
+
+public class ZoomScale
+{
+    private static Logger logger = Logger.getLogger(ZoomScale.class);
+
+    private HashMap<String, TreeMap<Double, Double>> rivers;
+
+    public ZoomScale() {
+        this.rivers = new HashMap<String, TreeMap<Double, Double>>();
+    }
+
+    public ZoomScale(String river) {
+        this();
+        rivers.put(river, new TreeMap<Double, Double>());
+    }
+
+    public double getRadius(String river, double lower, double upper) {
+        double range = Math.abs(upper) - Math.abs(lower);
+        TreeMap<Double, Double> ranges = rivers.get(river);
+        if (ranges == null) {
+            return 0.001;
+        }
+        Map.Entry<Double, Double> next = ranges.higherEntry(range);
+        Map.Entry<Double, Double> prev = ranges.lowerEntry(range);
+        double x0 = 0d;
+        double x1 = 0d;
+        double y0 = 0d;
+        double y1 = 0d;
+        if (prev == null && next != null) {
+            x1 = next.getKey();
+            y1 = next.getValue();
+        }
+        else if (prev != null && next == null) {
+            return prev.getValue();
+        }
+        else {
+            x0 = prev.getKey();
+            x1 = next.getKey();
+            y0 = prev.getValue();
+            y1 = next.getValue();
+        }
+        return Linear.linear(range, x0, x1, y0, y1);
+    }
+
+    public void addRiver(String river) {
+        if (!this.rivers.containsKey(river)) {
+            this.rivers.put(river, new TreeMap<Double, Double>());
+        }
+    }
+
+    public Set<String> getRivers() {
+        return this.rivers.keySet();
+    }
+
+    public void addRange(String river, double range, double radius) {
+        if (this.rivers.containsKey(river)) {
+            this.rivers.get(river).put(range, radius);
+        }
+        else {
+            this.rivers.put(river, new TreeMap<Double, Double>());
+            this.rivers.get(river).put(range, radius);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/extreme/Curve.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,144 @@
+package org.dive4elements.river.artifacts.model.extreme;
+
+import org.dive4elements.river.artifacts.math.Function;
+import org.dive4elements.river.artifacts.math.NaNFunction;
+import org.dive4elements.river.artifacts.math.UnivariateRealFunctionFunction;
+
+import org.dive4elements.river.artifacts.math.fitting.FunctionFactory;
+
+import java.io.Serializable;
+
+import java.lang.ref.SoftReference;
+
+import org.apache.commons.math.analysis.interpolation.SplineInterpolator;
+
+import org.apache.commons.math.exception.MathIllegalArgumentException;
+
+import org.apache.log4j.Logger;
+
+/** An extrapolating W/Q function/curve. */
+public class Curve
+implements   Serializable, Function
+{
+    private static Logger log = Logger.getLogger(Curve.class);
+
+    protected double [] qs;
+    protected double [] ws;
+    protected String    function;
+    protected double [] coeffs;
+    protected double    chiSqr;
+
+    /** Suggested maximum value for q to input. */
+    protected double    suggestedMaxQ;
+
+    // The spline is pretty heavy weight so cache it with a soft ref only.
+    protected transient SoftReference<Function> spline;
+    protected transient Function                extrapolation;
+
+    public Curve() {
+    }
+
+    public Curve(
+        double [] qs,
+        double [] ws,
+        String    function,
+        double [] coeffs,
+        double    chiSqr
+    ) {
+        this.qs       = qs;
+        this.ws       = ws;
+        this.function = function;
+        this.coeffs   = coeffs;
+        this.suggestedMaxQ = Double.MAX_VALUE;
+    }
+
+    public double [] getQs() {
+        return qs;
+    }
+
+    public double [] getWs() {
+        return ws;
+    }
+
+    public String getFunction() {
+        return function;
+    }
+
+    public double [] getCoeffs() {
+        return coeffs;
+    }
+
+
+    public void setSuggestedMaxQ(double newMaxQ) {
+        this.suggestedMaxQ = newMaxQ;
+    }
+
+
+    public double getSuggestedMaxQ() {
+        return this.suggestedMaxQ;
+    }
+
+
+    /** Calculate value at given x. */
+    @Override
+    public double value(double x) {
+        if (qs == null || x < qs[0]) return Double.NaN;
+        return (x <= qs[qs.length-1]
+            ? getSpline()
+            : getExtrapolation()).value(x);
+    }
+
+    protected synchronized Function getExtrapolation() {
+        if (extrapolation == null) {
+            org.dive4elements.river.artifacts.math.fitting.Function
+                f = FunctionFactory.getInstance().getFunction(function);
+
+            extrapolation = f != null
+                ? f.instantiate(coeffs)
+                : NaNFunction.INSTANCE;
+        }
+        return extrapolation;
+    }
+
+    /**
+     * Gets the chiSqr for this instance.
+     *
+     * @return The chiSqr.
+     */
+    public double getChiSqr() {
+        return this.chiSqr;
+    }
+
+    /**
+     * Sets the chiSqr for this instance.
+     *
+     * @param chiSqr The chiSqr.
+     */
+    public void setChiSqr(double chiSqr) {
+        this.chiSqr = chiSqr;
+    }
+
+    protected synchronized Function getSpline() {
+        Function sp;
+        if (spline != null) {
+            if ((sp = spline.get()) != null) {
+                return sp;
+            }
+        }
+        spline = new SoftReference<Function>(sp = createSpline());
+        return sp;
+    }
+
+    protected Function createSpline() {
+        SplineInterpolator interpolator = new SplineInterpolator();
+        try {
+            return new UnivariateRealFunctionFunction(
+                interpolator.interpolate(qs, ws));
+        }
+        catch (MathIllegalArgumentException miae) {
+            log.debug("creation on spline failed", miae);
+            return NaNFunction.INSTANCE;
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/extreme/ExtremeCalculation.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,418 @@
+package org.dive4elements.river.artifacts.model.extreme;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.commons.math.MathException;
+
+import org.apache.commons.math.optimization.fitting.CurveFitter;
+
+import org.apache.commons.math.optimization.general.LevenbergMarquardtOptimizer;
+
+import org.dive4elements.river.artifacts.access.ExtremeAccess;
+
+import org.dive4elements.river.artifacts.math.Linear;
+//import org.dive4elements.river.artifacts.math.Utils;
+
+import org.dive4elements.river.artifacts.math.fitting.Function;
+import org.dive4elements.river.artifacts.math.fitting.FunctionFactory;
+
+import org.dive4elements.river.artifacts.model.Calculation;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.RangeWithValues;
+import org.dive4elements.river.artifacts.model.RiverFactory;
+import org.dive4elements.river.artifacts.model.WQKms;
+import org.dive4elements.river.artifacts.model.WstValueTable;
+import org.dive4elements.river.artifacts.model.WstValueTableFactory;
+
+import org.dive4elements.river.model.River;
+
+import org.dive4elements.river.utils.DoubleUtil;
+import org.dive4elements.river.utils.KMIndex;
+
+import gnu.trove.TDoubleArrayList;
+
+import java.util.List;
+
+import java.awt.geom.Line2D;
+
+/** Calculate extrapolated W. */
+public class ExtremeCalculation
+extends      Calculation
+{
+    private static final Log log =
+        LogFactory.getLog(ExtremeCalculation.class);
+
+    protected String                river;
+    protected String                function;
+    protected double                from;
+    protected double                to;
+    protected double                step;
+    protected double                percent;
+    protected List<RangeWithValues> ranges;
+
+    public ExtremeCalculation() {
+    }
+
+    public ExtremeCalculation(ExtremeAccess access) {
+        String                river    = access.getRiver();
+        String                function = access.getFunction();
+        Double                from     = access.getFrom();
+        Double                to       = access.getTo();
+        Double                step     = access.getStep();
+        Double                percent  = access.getPercent();
+        List<RangeWithValues> ranges   = access.getRanges();
+
+        if (river == null) {
+            // TODO: i18n
+            addProblem("extreme.no.river");
+        }
+
+        if (function == null) {
+            // TODO: i18n
+            addProblem("extreme.no.function");
+        }
+
+        if (from == null) {
+            // TODO: i18n
+            addProblem("extreme.no.from");
+        }
+
+        if (to == null) {
+            // TODO: i18n
+            addProblem("extreme.no.to");
+        }
+
+        if (step == null) {
+            // TODO: i18n
+            addProblem("extreme.no.step");
+        }
+
+        if (percent == null) {
+            // TODO: i18n
+            addProblem("extreme.no.percent");
+        }
+
+        if (ranges == null) {
+            // TODO: i18n
+            addProblem("extreme.no.ranges");
+        }
+
+        if (!hasProblems()) {
+            this.river    = river;
+            this.function = function;
+            this.from     = Math.min(from, to);
+            this.to       = Math.max(from, to);
+            this.step     = Math.max(0.001d, Math.abs(step)/1000d);
+            this.percent  = Math.max(0d, Math.min(100d, percent));
+            this.ranges   = ranges;
+        }
+    }
+
+
+    /** Calculate an extreme curve (extrapolate). */
+    public CalculationResult calculate() {
+
+        WstValueTable wst = null;
+
+        River river = RiverFactory.getRiver(this.river);
+        if (river == null) {
+            // TODO: i18n
+            addProblem("extreme.no.such.river", this.river);
+        }
+        else {
+            wst = WstValueTableFactory.getTable(river);
+            if (wst == null) {
+                // TODO: i18n
+                addProblem("extreme.no.wst.table");
+            }
+        }
+
+        Function function =
+            FunctionFactory.getInstance().getFunction(this.function);
+        if (function == null) {
+            // TODO: i18n
+            addProblem("extreme.no.such.function", this.function);
+        }
+
+        return hasProblems()
+            ? new CalculationResult(this)
+            : innerCalculate(wst, function);
+    }
+
+
+    /** Name of wqkms like W(5000,6000) */
+    protected String wqkmsName(int i) {
+        StringBuilder sb = new StringBuilder("W(");
+        boolean already = false;
+        for (RangeWithValues r: ranges) {
+            double [] values = r.getValues();
+            if (i < values.length) {
+                if (already) {
+                    sb.append(", ");
+                }
+                else {
+                    already = true;
+                }
+                // TODO: i18n
+                sb.append(values[i]);
+            }
+        }
+        return sb.append(')').toString();
+    }
+
+    protected WQKms [] allocWQKms() {
+        int max = 0;
+        for (RangeWithValues r: ranges) {
+            double [] values = r.getValues();
+            if (values.length > max) {
+                max = values.length;
+            }
+        }
+        WQKms [] wqkms = new WQKms[max];
+        for (int i = 0; i < max; ++i) {
+            wqkms[i] = new WQKms(wqkmsName(i));
+        }
+        return wqkms;
+    }
+
+
+    /** Calculate an extreme curve (extrapolate). */
+    protected CalculationResult innerCalculate(
+        WstValueTable wst,
+        Function      function
+    ) {
+        RangeWithValues range = null;
+
+        double [] chiSqr = { 0d };
+
+        KMIndex<Curve> curves = new KMIndex<Curve>();
+        WQKms [] wqkms = allocWQKms();
+
+        boolean debug = log.isDebugEnabled();
+
+        from = DoubleUtil.round(from);
+        to = DoubleUtil.round(to);
+
+        for (double km = from; km <= to; km = DoubleUtil.round(km+step)) {
+
+            if (debug) {
+                log.debug("km: " + km);
+            }
+
+            boolean foundRange = false;
+
+            if (range == null || !range.inside(km)) {
+                for (RangeWithValues r: ranges) {
+                    if (r.inside(km)) {
+                        range = r;
+                        foundRange = true;
+                        break;
+                    }
+                }
+                // TODO: i18n
+                if (!foundRange) {
+                    addProblem(km, "extreme.no.range.inner");
+                    continue;
+                }
+            }
+
+            double [][] wqs = wst.interpolateTabulated(km);
+            if (wqs == null) {
+                // TODO: i18n
+                addProblem(km, "extreme.no.raw.data");
+                continue;
+            }
+
+            // XXX: This should not be necessary for model data.
+            if (!DoubleUtil.isValid(wqs)) {
+                // TODO: i18n
+                addProblem(km, "extreme.invalid.data");
+                continue;
+            }
+
+            double [][] fitWQs = extractPointsToFit(wqs);
+            if (fitWQs == null) {
+                // TODO: i18n
+                addProblem(km, "extreme.too.less.points");
+                continue;
+            }
+
+            double [] coeffs = doFitting(function, fitWQs, chiSqr);
+            if (coeffs == null) {
+                // TODO: i18n
+                addProblem(km, "extreme.fitting.failed");
+                continue;
+            }
+
+            Curve curve = new Curve(
+                wqs[1], wqs[0],
+                function.getName(),
+                coeffs,
+                chiSqr[0]);
+
+            curves.add(km, curve);
+
+            double [] values = range.getValues();
+
+            int V = Math.min(values.length, wqkms.length);
+            for (int i = 0; i < V; ++i) {
+                double q = values[i];
+                double w = curve.value(q);
+                if (Double.isNaN(w)) {
+                    // TODO: i18n
+                    addProblem(km, "extreme.evaluate.failed", values[i]);
+                }
+                else {
+                    wqkms[i].add(w, q, km);
+                }
+            }
+        }
+
+        ExtremeResult result = new ExtremeResult(curves, wqkms);
+        return new CalculationResult(result, this);
+    }
+
+    protected double [] doFitting(
+        Function    function,
+        double [][] wqs,
+        double []   chiSqr
+    ) {
+        LevenbergMarquardtOptimizer lmo = null;
+
+        double [] coeffs = null;
+
+        double [] ws = wqs[0];
+        double [] qs = wqs[1];
+
+        for (double tolerance = 1e-10; tolerance < 1e-3; tolerance *= 10d) {
+            lmo = new LevenbergMarquardtOptimizer();
+            lmo.setCostRelativeTolerance(tolerance);
+            lmo.setOrthoTolerance(tolerance);
+            lmo.setParRelativeTolerance(tolerance);
+
+            CurveFitter cf = new CurveFitter(lmo);
+
+            for (int i = 0; i < ws.length; ++i) {
+                cf.addObservedPoint(qs[i], ws[i]);
+            }
+
+            try {
+                coeffs = cf.fit(function, function.getInitialGuess());
+                break;
+            }
+            catch (MathException me) {
+                if (log.isDebugEnabled()) {
+                    log.debug("tolerance " + tolerance + " + failed.");
+                }
+            }
+        }
+        if (coeffs != null) {
+            chiSqr[0] = lmo.getChiSquare();
+        }
+        return coeffs;
+    }
+
+    protected double [][] extractPointsToFit(double [][] wqs) {
+
+        double [] ws = wqs[0];
+        double [] qs = wqs[1];
+
+        int N = Math.min(ws.length, qs.length);
+
+        if (N < 2) {
+            log.warn("Too less points for fitting");
+            return null;
+        }
+
+        double q2 = qs[N-1];
+        double w2 = ws[N-1];
+        double q1 = qs[N-2];
+        double w1 = ws[N-2];
+
+        boolean ascending = w2 > w1;
+
+        TDoubleArrayList ows = new TDoubleArrayList();
+        TDoubleArrayList oqs = new TDoubleArrayList();
+
+        oqs.add(q2); oqs.add(q1);
+        ows.add(w2); ows.add(w1);
+
+        int lastDir = -2;
+
+        for (int i = N-3; i >= 0; --i) {
+            double q = qs[i];
+            double w = ws[i];
+
+            if ((ascending && w > w1) || (!ascending && w < w1)) {
+                break;
+            }
+
+            int dir = Line2D.relativeCCW(q2, w2, q1, w1, q, w);
+            //int dir = Utils.relativeCCW(q2, w2, q1, w1, q, w);
+            if (lastDir == -2) {
+                lastDir = dir;
+            }
+            else if (lastDir != dir) {
+                break;
+            }
+
+            oqs.add(q);
+            ows.add(w);
+            w2 = w1;
+            q2 = q1;
+            w1 = w;
+            q1 = q;
+        }
+
+        oqs.reverse();
+        ows.reverse();
+
+        boolean debug = log.isDebugEnabled();
+        if (debug) {
+            log.debug("from table: " + N);
+            log.debug("after trim: " + oqs.size());
+        }
+
+        cutPercent(ows, oqs);
+
+        if (debug) {
+            log.debug("after percent cut: " + oqs.size());
+        }
+
+        return new double [][] {
+            ows.toNativeArray(),
+            oqs.toNativeArray()
+        };
+    }
+
+
+    protected void cutPercent(TDoubleArrayList ws, TDoubleArrayList qs) {
+        int N = qs.size();
+        if (percent <= 0d || N == 0) {
+            return;
+        }
+
+        double minQ = qs.getQuick(0);
+        double maxQ = qs.getQuick(N-1);
+        double factor = Math.min(Math.max(0d, percent/100d), 1d);
+        double cutQ = Linear.weight(factor, minQ, maxQ);
+        int cutIndex = 0;
+        for (; cutIndex < N; ++cutIndex) {
+            double q = qs.getQuick(cutIndex);
+            if (minQ < maxQ) {
+                if (q > cutQ) {
+                    break;
+                }
+            }
+            else {
+                if (q < cutQ) {
+                    break;
+                }
+            }
+        }
+        ws.remove(0, cutIndex);
+        qs.remove(0, cutIndex);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/extreme/ExtremeCurveFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,126 @@
+package org.dive4elements.river.artifacts.model.extreme;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+import org.dive4elements.river.artifacts.access.ExtremeAccess;
+
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.fixings.FixingsFacet;
+
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+import org.dive4elements.river.utils.KMIndex;
+import org.dive4elements.river.utils.DoubleUtil;
+
+import org.apache.log4j.Logger;
+
+/**
+ * Facet to show the W|Q values.
+ */
+public class ExtremeCurveFacet
+extends      FixingsFacet
+implements   FacetTypes {
+
+    /** House logger. */
+    private static Logger logger = Logger.getLogger(ExtremeCurveFacet.class);
+
+
+    /** Trivial Constructor. */
+    public ExtremeCurveFacet() {
+    }
+
+
+    /**
+     * @param description Description of the facet.
+     */
+    public ExtremeCurveFacet(String description) {
+        super(0, EXTREME_WQ_CURVE, description, ComputeType.ADVANCE, null, null);
+    }
+
+    /**
+     * @param description Description of the facet.
+     * @param showBase if true, gimme different name.
+     */
+    public ExtremeCurveFacet(String description, boolean showBase) {
+        super(0, EXTREME_WQ_CURVE_BASE, description, ComputeType.ADVANCE, null, null);
+        if (!showBase) {
+            this.name = EXTREME_WQ_CURVE;
+        }
+    }
+
+
+    public ExtremeCurveFacet(int index, String description) {
+        super(index, EXTREME_WQ_CURVE, description, ComputeType.ADVANCE, null, null);
+    }
+
+
+    /**
+     * Returns the data (a curve/function) this facet provides at km given in context.
+     *
+     * @param artifact the owner artifact.
+     * @param context  the CallContext.
+     *
+     * @return the data.
+     */
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        logger.debug("ExtremeCurveFacet.getData");
+        if (artifact instanceof FLYSArtifact) {
+            FLYSArtifact flys = (FLYSArtifact)artifact;
+            CalculationResult res =
+                (CalculationResult) flys.compute(context,
+                                                 ComputeType.ADVANCE,
+                                                 false);
+
+            ExtremeResult result = (ExtremeResult) res.getData();
+            double currentKm = getCurrentKm(context);
+
+            KMIndex<Curve> curves = result.getCurves();
+
+            KMIndex.Entry<Curve> curveEntry = curves.search(currentKm);
+
+            if (curveEntry != null) {
+                logger.debug("A curve at km = " + currentKm);
+                Curve c = curveEntry.getValue();
+                // Find segment in which the curr. km is located.
+                ExtremeAccess access = new ExtremeAccess(flys, context);
+
+                double[] ds = access.getValuesForRange(currentKm);
+
+                if (ds != null) {
+                    double m = DoubleUtil.maxInArray(ds);
+                    // Add 5 percent.
+                    m *= 1.05d;
+                    c.setSuggestedMaxQ(m);
+                }
+
+                return c;
+            }
+            else {
+                logger.debug("No curve at km = " + currentKm);
+                return null;
+            }
+        }
+        else {
+            logger.debug("Not an instance of FLYSArtifact / WINFOArtifact.");
+            return null;
+        }
+    }
+
+
+    /**
+     * Create a deep copy of this Facet.
+     * @return a deep copy.
+     */
+    @Override
+    public ExtremeCurveFacet deepCopy() {
+        ExtremeCurveFacet copy = new ExtremeCurveFacet();
+        copy.set(this);
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/extreme/ExtremeResult.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,84 @@
+package org.dive4elements.river.artifacts.model.extreme;
+
+import java.io.Serializable;
+
+import org.dive4elements.river.artifacts.model.WQKms;
+import org.dive4elements.river.artifacts.model.WQKmsResult;
+
+import org.dive4elements.river.utils.KMIndex;
+
+/** Result from an extreme value (extrapolation) calculation. */
+public class ExtremeResult
+implements   Serializable, WQKmsResult
+{
+    /** Curves that refer to actual values and a function for extrapolation. */
+    protected KMIndex<Curve> curves;
+
+    protected WQKms [] wqkms;
+
+    public ExtremeResult() {
+    }
+
+    public ExtremeResult(KMIndex<Curve> curves, WQKms [] wqkms) {
+        this.curves = curves;
+        this.wqkms = wqkms;
+    }
+
+    /**
+     * Gets the curves for this instance.
+     *
+     * @return The curves.
+     */
+    public KMIndex<Curve> getCurves() {
+        return this.curves;
+    }
+
+    /**
+     * Sets the curves for this instance.
+     *
+     * @param curves The curves.
+     */
+    public void setCurves(KMIndex<Curve> curves) {
+        this.curves = curves;
+    }
+
+    /**
+     * Gets the wqkms for this instance.
+     *
+     * @return The wqkms.
+     */
+    @Override
+    public WQKms[] getWQKms() {
+        return this.wqkms;
+    }
+
+    /**
+     * Gets the wqkms for this instance.
+     *
+     * @param index The index to get.
+     * @return The wqkms.
+     */
+    public WQKms getWQKms(int index) {
+        return this.wqkms[index];
+    }
+
+    /**
+     * Sets the wqkms for this instance.
+     *
+     * @param wqkms The wqkms.
+     */
+    public void setWQKms(WQKms[] wqkms) {
+        this.wqkms = wqkms;
+    }
+
+    /**
+     * Sets the wqkms for this instance.
+     *
+     * @param index The index to set.
+     * @param wqkms The wqkms.
+     */
+    public void setWQKms(int index, WQKms wqkms) {
+        this.wqkms[index] = wqkms;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/AnalysisPeriod.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,98 @@
+package org.dive4elements.river.artifacts.model.fixings;
+
+import org.dive4elements.river.artifacts.model.DateRange;
+
+import java.io.Serializable;
+
+public class AnalysisPeriod
+implements   Serializable
+{
+    protected DateRange dateRange;
+    protected QWD []    qwds;
+    protected QWD []    qSectorAverages;
+    protected double [] qSectorStdDevs;
+
+    public AnalysisPeriod() {
+    }
+
+    public AnalysisPeriod(DateRange dateRange) {
+        this.dateRange = dateRange;
+    }
+
+    public AnalysisPeriod(DateRange dateRange, QWD [] qwds) {
+        this(dateRange);
+        this.dateRange = dateRange;
+        this.qwds      = qwds;
+    }
+
+    public AnalysisPeriod(
+        DateRange dateRange,
+        QWD []    qwds,
+        QWD []    qSectorAverages,
+        double [] qSectorStdDevs
+    ) {
+        this(dateRange, qwds);
+        this.qSectorAverages = qSectorAverages;
+        this.qSectorStdDevs  = qSectorStdDevs;
+    }
+
+    public DateRange getDateRange() {
+        return dateRange;
+    }
+
+    public void setDateRange(DateRange dateRange) {
+        this.dateRange = dateRange;
+    }
+
+    public QWD [] getQWDs() {
+        return qwds;
+    }
+
+    public void setQWDs(QWD [] qwds) {
+        this.qwds = qwds;
+    }
+
+    public QWD [] getQSectorAverages() {
+        return qSectorAverages;
+    }
+
+    public void setQSectorAverages(QWD [] qSectorAverages) {
+        this.qSectorAverages = qSectorAverages;
+    }
+
+    public QWD getQSectorAverage(int i) {
+        return qSectorAverages[i];
+    }
+
+    public double [] getQSectorStdDevs() {
+        return qSectorStdDevs;
+    }
+
+    public void setQSectorStdDevs(double [] qSectorStdDevs) {
+        this.qSectorStdDevs = qSectorStdDevs;
+    }
+
+    public double getQSectorStdDev(int i) {
+        return qSectorStdDevs[i];
+    }
+
+    public double getMaxQ() {
+        double maxQ = -Double.MAX_VALUE;
+        if (qwds != null) {
+            for (QWD qwd: qwds) {
+                if (qwd.getQ() > maxQ) {
+                    maxQ = qwd.getQ();
+                }
+            }
+        }
+        if (qSectorAverages != null) {
+            for (QWD qwd: qSectorAverages) {
+                if (qwd != null && qwd.getQ() > maxQ) {
+                    maxQ = qwd.getQ();
+                }
+            }
+        }
+        return maxQ;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/Fitting.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,242 @@
+package org.dive4elements.river.artifacts.model.fixings;
+
+import gnu.trove.TDoubleArrayList;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.optimization.fitting.CurveFitter;
+import org.apache.commons.math.optimization.general.LevenbergMarquardtOptimizer;
+import org.apache.commons.math.stat.descriptive.moment.StandardDeviation;
+import org.apache.log4j.Logger;
+
+import org.dive4elements.river.artifacts.math.GrubbsOutlier;
+import org.dive4elements.river.artifacts.math.fitting.Function;
+
+public class Fitting
+{
+    private static Logger log = Logger.getLogger(Fitting.class);
+
+    /** Use instance of this factory to find meta infos for outliers. */
+    public interface QWDFactory {
+
+        QWD create(double q, double w);
+
+    } // interface QWFactory
+
+    public static final QWDFactory QWD_FACTORY = new QWDFactory() {
+        @Override
+        public QWD create(double q, double w) {
+            return new QWD(q, w);
+        }
+    };
+
+    protected boolean        checkOutliers;
+    protected Function       function;
+    protected QWDFactory     qwdFactory;
+    protected double         chiSqr;
+    protected double []      parameters;
+    protected ArrayList<QWI> removed;
+    protected QWD []         referenced;
+    protected double         standardDeviation;
+
+
+    public Fitting() {
+        removed = new ArrayList<QWI>();
+    }
+
+    public Fitting(Function function) {
+        this(function, QWD_FACTORY);
+    }
+
+    public Fitting(Function function, QWDFactory qwdFactory) {
+        this(function, qwdFactory, false);
+    }
+
+    public Fitting(
+        Function   function,
+        QWDFactory qwdFactory,
+        boolean    checkOutliers
+    ) {
+        this();
+        this.function      = function;
+        this.qwdFactory    = qwdFactory;
+        this.checkOutliers = checkOutliers;
+    }
+
+    public Function getFunction() {
+        return function;
+    }
+
+    public void setFunction(Function function) {
+        this.function = function;
+    }
+
+    public boolean getCheckOutliers() {
+        return checkOutliers;
+    }
+
+    public void setCheckOutliers(boolean checkOutliers) {
+        this.checkOutliers = checkOutliers;
+    }
+
+    public double getChiSquare() {
+        return chiSqr;
+    }
+
+    public void reset() {
+        chiSqr     = 0.0;
+        parameters = null;
+        removed.clear();
+        referenced = null;
+        standardDeviation = 0.0;
+    }
+
+    public boolean hasOutliers() {
+        return !removed.isEmpty();
+    }
+
+    public List<QWI> getOutliers() {
+        return removed;
+    }
+
+    public QWI [] outliersToArray() {
+        return removed.toArray(new QWI[removed.size()]);
+    }
+
+    public QWD [] referencedToArray() {
+        return referenced != null ? (QWD [])referenced.clone() : null;
+    }
+
+    public double getMaxQ() {
+        double maxQ = -Double.MAX_VALUE;
+        if (referenced != null) {
+            for (QWI qw: referenced) {
+                if (qw.getQ() > maxQ) {
+                    maxQ = qw.getQ();
+                }
+            }
+        }
+        return maxQ;
+    }
+
+    public double [] getParameters() {
+        return parameters;
+    }
+
+    public double getStandardDeviation() {
+        return standardDeviation;
+    }
+
+    public boolean fit(double [] qs, double [] ws) {
+
+        TDoubleArrayList xs = new TDoubleArrayList(qs.length);
+        TDoubleArrayList ys = new TDoubleArrayList(ws.length);
+
+        for (int i = 0; i < qs.length; ++i) {
+            if (!Double.isNaN(qs[i]) && !Double.isNaN(ws[i])) {
+                xs.add(qs[i]);
+                ys.add(ws[i]);
+            }
+            else {
+                log.warn("remove invalid value " + qs[i] + " " + ws[i]);
+            }
+        }
+
+        if (xs.size() < 2) {
+            log.warn("Too less points.");
+            return false;
+        }
+
+        List<Double> inputs = new ArrayList<Double>(xs.size());
+
+        org.dive4elements.river.artifacts.math.Function instance = null;
+
+        LevenbergMarquardtOptimizer lmo = null;
+
+        for (;;) {
+            parameters = null;
+            for (double tolerance = 1e-10; tolerance < 1e-3; tolerance *= 10d) {
+
+                lmo = new LevenbergMarquardtOptimizer();
+                lmo.setCostRelativeTolerance(tolerance);
+                lmo.setOrthoTolerance(tolerance);
+                lmo.setParRelativeTolerance(tolerance);
+
+                CurveFitter cf = new CurveFitter(lmo);
+
+                for (int i = 0, N = xs.size(); i < N; ++i) {
+                    cf.addObservedPoint(xs.getQuick(i), ys.getQuick(i));
+                }
+
+                try {
+                    parameters = cf.fit(function, function.getInitialGuess());
+                    break;
+                }
+                catch (MathException me) {
+                    if (log.isDebugEnabled()) {
+                        log.debug("tolerance " + tolerance + " + failed.");
+                    }
+                }
+            }
+            if (parameters == null) {
+                return false;
+            }
+
+            // This is the paraterized function for a given km.
+            instance = function.instantiate(parameters);
+
+            if (!checkOutliers) {
+                break;
+            }
+
+            inputs.clear();
+
+            for (int i = 0, N = xs.size(); i < N; ++i) {
+                double y = instance.value(xs.getQuick(i));
+                if (Double.isNaN(y)) {
+                    y = Double.MAX_VALUE;
+                }
+                inputs.add(Double.valueOf(ys.getQuick(i) - y));
+            }
+
+            Integer outlier = GrubbsOutlier.findOutlier(inputs);
+
+            if (outlier == null) {
+                break;
+            }
+
+            int idx = outlier.intValue();
+            removed.add(
+                qwdFactory.create(
+                    xs.getQuick(idx), ys.getQuick(idx)));
+            xs.remove(idx);
+            ys.remove(idx);
+        }
+
+        StandardDeviation stdDev = new StandardDeviation();
+
+        referenced = new QWD[xs.size()];
+        for (int i = 0; i < referenced.length; ++i) {
+            QWD qwd = qwdFactory.create(xs.getQuick(i), ys.getQuick(i));
+
+            if (qwd == null) {
+                log.warn("QW creation failed!");
+            }
+            else {
+                referenced[i] = qwd;
+                double dw = (qwd.getW() - instance.value(qwd.getQ()))*100.0;
+                qwd.setDeltaW(dw);
+                stdDev.increment(dw);
+            }
+        }
+
+        standardDeviation = stdDev.getResult();
+
+        chiSqr = lmo.getChiSquare();
+
+        return true;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixAnalysisCalculation.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,293 @@
+package org.dive4elements.river.artifacts.model.fixings;
+
+import org.dive4elements.river.artifacts.access.FixAnalysisAccess;
+
+import org.dive4elements.river.artifacts.math.fitting.Function;
+
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.DateRange;
+
+import org.dive4elements.river.artifacts.model.FixingsOverview.AndFilter;
+import org.dive4elements.river.artifacts.model.FixingsOverview.DateRangeFilter;
+
+import org.dive4elements.river.artifacts.model.FixingsOverview.Fixing.Filter;
+
+import org.dive4elements.river.artifacts.model.FixingsOverview.Fixing;
+import org.dive4elements.river.artifacts.model.FixingsOverview.IdsFilter;
+import org.dive4elements.river.artifacts.model.FixingsOverview.KmFilter;
+import org.dive4elements.river.artifacts.model.FixingsOverview.SectorFilter;
+
+import org.dive4elements.river.artifacts.model.FixingsOverview;
+import org.dive4elements.river.artifacts.model.Parameters;
+import org.dive4elements.river.artifacts.model.Range;
+
+import org.dive4elements.river.utils.DateAverager;
+import org.dive4elements.river.utils.KMIndex;
+
+import gnu.trove.TIntIntHashMap;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import org.apache.commons.math.stat.descriptive.moment.StandardDeviation;
+
+import org.apache.log4j.Logger;
+
+public class FixAnalysisCalculation
+extends      FixCalculation
+{
+    private static Logger log = Logger.getLogger(FixAnalysisCalculation.class);
+
+    protected DateRange    referencePeriod;
+    protected DateRange [] analysisPeriods;
+
+    public FixAnalysisCalculation() {
+    }
+
+    public FixAnalysisCalculation(FixAnalysisAccess access) {
+        super(access);
+
+        DateRange    referencePeriod = access.getReferencePeriod();
+        DateRange [] analysisPeriods = access.getAnalysisPeriods();
+
+        if (referencePeriod == null) {
+            addProblem("fix.missing.reference.period");
+        }
+
+        if (analysisPeriods == null || analysisPeriods.length < 1) {
+            addProblem("fix.missing.analysis.periods");
+        }
+
+        if (!hasProblems()) {
+            this.referencePeriod = referencePeriod;
+            this.analysisPeriods = analysisPeriods;
+        }
+    }
+
+    @Override
+    public CalculationResult innerCalculate(
+        FixingsOverview overview,
+        Function        func
+    ) {
+        ColumnCache cc = new ColumnCache();
+
+        FitResult fitResult = doFitting(overview, cc, func);
+
+        if (fitResult == null) {
+            return new CalculationResult(this);
+        }
+
+        KMIndex<AnalysisPeriod []> analysisPeriods =
+            calculateAnalysisPeriods(
+                func,
+                fitResult.getParameters(),
+                overview,
+                cc);
+
+        analysisPeriods.sort();
+
+        FixAnalysisResult far = new FixAnalysisResult(
+            fitResult.getParameters(),
+            fitResult.getReferenced(),
+            fitResult.getOutliers(),
+            analysisPeriods);
+
+        return new CalculationResult(far, this);
+    }
+
+    @Override
+    protected Filter createFilter() {
+        Filter ids = super.createFilter();
+        DateRangeFilter rdf = new DateRangeFilter(
+            referencePeriod.getFrom(),
+            referencePeriod.getTo());
+        return new AndFilter().add(rdf).add(ids);
+    }
+
+    protected KMIndex<AnalysisPeriod []> calculateAnalysisPeriods(
+        Function        function,
+        Parameters      parameters,
+        FixingsOverview overview,
+        ColumnCache cc
+    ) {
+        Range range = new Range(from, to);
+
+        int kmIndex   = parameters.columnIndex("km");
+        int maxQIndex = parameters.columnIndex("max_q");
+
+        double [] wq = new double[2];
+
+        int [] parameterIndices =
+            parameters.columnIndices(function.getParameterNames());
+
+        double [] parameterValues = new double[parameterIndices.length];
+
+        DateAverager dateAverager = new DateAverager();
+
+        KMIndex<AnalysisPeriod []> results =
+            new KMIndex<AnalysisPeriod []>(parameters.size());
+
+        IdsFilter idsFilter = new IdsFilter(events);
+
+        TIntIntHashMap [] col2indices =
+            new TIntIntHashMap[analysisPeriods.length];
+
+        for (int i = 0; i < analysisPeriods.length; ++i) {
+            col2indices[i] = new TIntIntHashMap();
+        }
+
+        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.
+            org.dive4elements.river.artifacts.math.Function instance =
+                function.instantiate(parameterValues);
+
+            KmFilter kmFilter = new KmFilter(km);
+
+            ArrayList<AnalysisPeriod> periodResults =
+                new ArrayList<AnalysisPeriod>(analysisPeriods.length);
+
+            for (int ap = 0; ap < analysisPeriods.length; ++ap) {
+                DateRange analysisPeriod = analysisPeriods[ap];
+                TIntIntHashMap col2index = col2indices[ap];
+
+                DateRangeFilter drf = new DateRangeFilter(
+                    analysisPeriod.getFrom(),
+                    analysisPeriod.getTo());
+
+                QWD []    qSectorAverages = new QWD[4];
+                double [] qSectorStdDevs  = new double[4];
+
+                ArrayList<QWD> allQWDs = new ArrayList<QWD>();
+
+                // for all Q sectors.
+                for (int qSector = qSectorStart; qSector < qSectorEnd; ++qSector) {
+
+                    Filter filter = new AndFilter()
+                        .add(kmFilter)
+                        .add(new SectorFilter(qSector))
+                        .add(drf)
+                        .add(idsFilter);
+
+                    List<Fixing.Column> metas = overview.filter(range, filter);
+
+                    if (metas.isEmpty()) {
+                        // No fixings for km and analysis period
+                        continue;
+                    }
+
+                    double sumQ = 0.0;
+                    double sumW = 0.0;
+
+                    StandardDeviation stdDev = new StandardDeviation();
+
+                    List<QWD> qwds = new ArrayList<QWD>(metas.size());
+
+                    dateAverager.clear();
+
+                    for (Fixing.Column meta: metas) {
+                        if (meta.findQSector(km) != qSector) {
+                            // Ignore not matching sectors.
+                            continue;
+                        }
+
+                        Column column = cc.getColumn(meta);
+                        if (column == null || !column.getQW(km, wq)) {
+                            continue;
+                        }
+
+                        double fw = instance.value(wq[1]);
+                        if (Double.isNaN(fw)) {
+                            continue;
+                        }
+
+                        double dw = (wq[0] - fw)*100.0;
+
+                        stdDev.increment(dw);
+
+                        Date date = column.getDate();
+                        String description = column.getDescription();
+
+                        QWD qwd = new QWD(
+                            wq[1], wq[0],
+                            description,
+                            date, true,
+                            dw, getIndex(col2index, column.getIndex()));
+
+                        qwds.add(qwd);
+
+                        sumW += wq[0];
+                        sumQ += wq[1];
+
+                        dateAverager.add(date);
+                    }
+
+                    // Calulate average per Q sector.
+                    int N = qwds.size();
+                    if (N > 0) {
+                        allQWDs.addAll(qwds);
+                        double avgW = sumW / N;
+                        double avgQ = sumQ / N;
+
+                        double avgFw = instance.value(avgQ);
+                        if (!Double.isNaN(avgFw)) {
+                            double avgDw = (avgW - avgFw)*100.0;
+                            Date avgDate = dateAverager.getAverage();
+
+                            String avgDescription = "avg.deltawt." + qSector;
+
+                            QWD avgQWD = new QWD(
+                                avgQ, avgW, avgDescription, avgDate, true, avgDw, 0);
+
+                            qSectorAverages[qSector] = avgQWD;
+                        }
+                        qSectorStdDevs[qSector] = stdDev.getResult();
+                    }
+                    else {
+                        qSectorStdDevs[qSector] = Double.NaN;
+                    }
+                } // for all Q sectors
+
+                QWD [] aqwds = allQWDs.toArray(new QWD[allQWDs.size()]);
+
+                AnalysisPeriod periodResult = new AnalysisPeriod(
+                    analysisPeriod,
+                    aqwds,
+                    qSectorAverages,
+                    qSectorStdDevs);
+                periodResults.add(periodResult);
+            }
+
+            double maxQ = -Double.MAX_VALUE;
+            for (AnalysisPeriod ap: periodResults) {
+                double q = ap.getMaxQ();
+                if (q > maxQ) {
+                    maxQ = q;
+                }
+            }
+
+            double oldMaxQ = parameters.get(row, maxQIndex);
+            if (oldMaxQ < maxQ) {
+                parameters.set(row, maxQIndex, maxQ);
+            }
+
+            results.add(km, periodResults.toArray(
+                new AnalysisPeriod[periodResults.size()]));
+        }
+
+        return results;
+    }
+
+    private static final int getIndex(TIntIntHashMap map, int colIdx) {
+        if (map.containsKey(colIdx)) {
+            return map.get(colIdx);
+        }
+        int index = map.size();
+        map.put(colIdx, index);
+        return index;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixAnalysisEventsFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,115 @@
+package org.dive4elements.river.artifacts.model.fixings;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+import org.dive4elements.river.utils.KMIndex;
+
+import org.apache.log4j.Logger;
+
+/**
+ * Facet to show W values for Q values at km for a date.
+ *
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class FixAnalysisEventsFacet
+extends      FixingsFacet
+implements   FacetTypes {
+
+    /** House logger. */
+    private static Logger logger = Logger.getLogger(FixAnalysisEventsFacet.class);
+
+    /** Trivial Constructor. */
+    public FixAnalysisEventsFacet() {
+    }
+
+
+    /**
+     * @param name
+     */
+    public FixAnalysisEventsFacet(int index, String name, String description) {
+        super(index,
+             name,
+             description,
+             ComputeType.ADVANCE,
+             null,
+             null);
+    }
+
+
+    /**
+     * Returns the data this facet requires.
+     *
+     * @param artifact the owner artifact.
+     * @param context  the CallContext (ignored).
+     *
+     * @return the data.
+     */
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        logger.debug("FixAnalysisEventsFacet.getData");
+
+        if (artifact instanceof FLYSArtifact) {
+            FLYSArtifact flys = (FLYSArtifact)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 {
+            logger.debug("Not an instance of FixationArtifact.");
+            return null;
+        }
+    }
+
+
+    /**
+     * Create a deep copy of this Facet.
+     * @return a deep copy.
+     */
+    @Override
+    public FixAnalysisEventsFacet deepCopy() {
+        FixAnalysisEventsFacet copy = new FixAnalysisEventsFacet();
+        copy.set(this);
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixAnalysisPeriodsFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,111 @@
+package org.dive4elements.river.artifacts.model.fixings;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+import org.dive4elements.river.artifacts.access.FixAnalysisAccess;
+
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.DateRange;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+import org.dive4elements.river.utils.KMIndex;
+
+import org.apache.log4j.Logger;
+
+/**
+ * Facet to show W values for Q values at km for a date.
+ *
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class FixAnalysisPeriodsFacet
+extends      FixingsFacet
+implements   FacetTypes {
+
+    /** House logger. */
+    private static Logger logger = Logger.getLogger(FixAnalysisPeriodsFacet.class);
+
+    /** Trivial Constructor. */
+    public FixAnalysisPeriodsFacet() {
+    }
+
+
+    /**
+     * @param name
+     */
+    public FixAnalysisPeriodsFacet(int index, String name, String description) {
+        super(index,
+            name,
+            description,
+            ComputeType.ADVANCE,
+            null,
+            null);
+    }
+
+
+    /**
+     * Returns the data this facet requires.
+     *
+     * @param artifact the owner artifact.
+     * @param context  the CallContext (ignored).
+     *
+     * @return the data.
+     */
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        logger.debug("FixAnalysisPeriodsFacet.getData");
+
+        if (artifact instanceof FLYSArtifact) {
+            FLYSArtifact flys = (FLYSArtifact)artifact;
+            FixAnalysisAccess access = new FixAnalysisAccess(flys, context);
+
+            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) {
+                return null;
+            }
+
+            AnalysisPeriod[] periods = kmPeriodsEntry.getValue();
+
+            if (periods == null) {
+                return null;
+            }
+            DateRange[] dates = new DateRange[periods.length];
+            for (int i = 0; i < periods.length; i++) {
+                dates[i] = periods[i].getDateRange();
+            }
+            return dates;
+        }
+        else {
+            logger.debug("Not an instance of FixationArtifact.");
+            return null;
+        }
+    }
+
+
+    /**
+     * Create a deep copy of this Facet.
+     * @return a deep copy.
+     */
+    @Override
+    public FixAnalysisPeriodsFacet deepCopy() {
+        FixAnalysisPeriodsFacet copy = new FixAnalysisPeriodsFacet();
+        copy.set(this);
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixAnalysisResult.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,77 @@
+package org.dive4elements.river.artifacts.model.fixings;
+
+import java.util.Collection;
+import java.util.Date;
+import java.util.TreeSet;
+
+import org.dive4elements.river.artifacts.model.Parameters;
+
+import org.dive4elements.river.utils.KMIndex;
+
+public class FixAnalysisResult
+extends      FixResult
+{
+    protected KMIndex<AnalysisPeriod []> analysisPeriods;
+
+    public FixAnalysisResult() {
+    }
+
+    public FixAnalysisResult(
+        Parameters                 parameters,
+        KMIndex<QWD []>            referenced,
+        KMIndex<QWI []>            outliers,
+        KMIndex<AnalysisPeriod []> analysisPeriods
+    ) {
+        super(parameters, referenced, outliers);
+        this.analysisPeriods = analysisPeriods;
+    }
+
+    public int getUsedSectorsInAnalysisPeriods() {
+        int result = 0;
+        for (KMIndex.Entry<AnalysisPeriod []> entry: analysisPeriods) {
+            for (AnalysisPeriod period: entry.getValue()) {
+                for (int i = 0; i < 4; ++i) {
+                    result |= period.getQSectorAverage(i) != null
+                        ? (1 << i)
+                        : 0;
+                }
+                // XXX: Stop early on result == ~(~0 << 4)) ?
+            }
+        }
+        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);
+            }
+        }
+        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);
+            }
+        }
+        return dates;
+    }
+
+    public KMIndex<AnalysisPeriod []> getAnalysisPeriods() {
+        return analysisPeriods;
+    }
+
+    public void setAnalysisPeriods(KMIndex<AnalysisPeriod []> analysisPeriods) {
+        this.analysisPeriods = analysisPeriods;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixAvSectorFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,126 @@
+package org.dive4elements.river.artifacts.model.fixings;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+import org.dive4elements.river.artifacts.access.FixAnalysisAccess;
+
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+
+import org.dive4elements.river.artifacts.model.QWDDateRange;
+
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+import org.dive4elements.river.utils.KMIndex;
+
+import org.apache.log4j.Logger;
+
+
+/**
+ * Facet to show average W values for Q sectors.
+ *
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class FixAvSectorFacet
+extends      FixingsFacet
+implements   FacetTypes {
+
+    /** House logger. */
+    private static Logger logger = Logger.getLogger(FixAvSectorFacet.class);
+
+    /** Trivial Constructor. */
+    public FixAvSectorFacet() {
+    }
+
+
+    public FixAvSectorFacet(int ndx, String name, String description) {
+        super(
+            ndx,
+            name,
+            description,
+            ComputeType.ADVANCE,
+            null,
+            null);
+    }
+
+
+    /**
+     * Returns the data this facet requires.
+     *
+     * @param artifact the owner artifact.
+     * @param context  the CallContext.
+     *
+     * @return the data as QWD array (QWD[]).
+     */
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        logger.debug("FixAvSectorFacet.getData");
+
+        if (artifact instanceof FLYSArtifact) {
+            FLYSArtifact flys = (FLYSArtifact)artifact;
+            FixAnalysisAccess access = new FixAnalysisAccess(flys, context);
+
+            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.warn("No analysis periods found for km '" + currentKm + "'");
+                return null;
+            }
+
+            AnalysisPeriod[] periods = kmPeriodsEntry.getValue();
+
+            if (periods == null) {
+                logger.warn("No analysis periods specified!");
+                return null;
+            }
+
+            QWD[] qwdData = null;
+            int sectorNdx = index & 3;
+            int periodNdx = index >> 2;
+
+            if (periodNdx < periods.length) {
+                qwdData = periods[periodNdx].getQSectorAverages();
+            }
+
+            if (logger.isDebugEnabled()) {
+                int resSize = qwdData != null ? qwdData.length : -1;
+                logger.debug("Found " + resSize + " result elements.");
+            }
+
+            if (qwdData == null) {
+                return null;
+            }
+            return new QWDDateRange(qwdData[sectorNdx], periods[periodNdx].getDateRange());
+        }
+        else {
+            logger.warn("Artifact is no instance of FLYSArtifact.");
+            return null;
+        }
+    }
+
+
+    /**
+     * Create a deep copy of this Facet.
+     * @return a deep copy.
+     */
+    @Override
+    public FixAvSectorFacet deepCopy() {
+        FixAvSectorFacet copy = new FixAvSectorFacet();
+        copy.set(this);
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixCalculation.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,425 @@
+package org.dive4elements.river.artifacts.model.fixings;
+
+import org.dive4elements.artifacts.common.utils.StringUtils;
+
+import org.dive4elements.river.artifacts.access.FixAccess;
+
+import org.dive4elements.river.artifacts.math.fitting.Function;
+import org.dive4elements.river.artifacts.math.fitting.FunctionFactory;
+
+import org.dive4elements.river.artifacts.model.Calculation;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.FixingsColumn;
+import org.dive4elements.river.artifacts.model.FixingsColumnFactory;
+
+import org.dive4elements.river.artifacts.model.FixingsOverview.Fixing.Filter;
+
+import org.dive4elements.river.artifacts.model.FixingsOverview.Fixing;
+import org.dive4elements.river.artifacts.model.FixingsOverview.IdsFilter;
+
+import org.dive4elements.river.artifacts.model.FixingsOverview;
+import org.dive4elements.river.artifacts.model.FixingsOverviewFactory;
+import org.dive4elements.river.artifacts.model.Parameters;
+
+import org.dive4elements.river.utils.DoubleUtil;
+import org.dive4elements.river.utils.KMIndex;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+
+/** Calculation base class for fix. */
+public abstract class FixCalculation
+extends               Calculation
+{
+    private static Logger log = Logger.getLogger(FixCalculation.class);
+
+    public static final double EPSILON = 1e-4;
+
+    public static final String [] STANDARD_COLUMNS = {
+        "km", "chi_sqr", "max_q", "std-dev"
+    };
+
+    protected static class FitResult {
+
+        protected Parameters      parameters;
+        protected KMIndex<QWD []> referenced;
+        protected KMIndex<QWI []> outliers;
+
+        public FitResult() {
+        }
+
+        public FitResult(
+            Parameters      parameters,
+            KMIndex<QWD []> referenced,
+            KMIndex<QWI []> outliers
+        ) {
+            this.parameters = parameters;
+            this.referenced = referenced;
+            this.outliers   = outliers;
+        }
+
+        public Parameters getParameters() {
+            return parameters;
+        }
+
+        public KMIndex<QWD []> getReferenced() {
+            return referenced;
+        }
+
+        public KMIndex<QWI []> getOutliers() {
+            return outliers;
+        }
+    } // class FitResult
+
+    /** Helper class to bundle the meta information of a column
+     *  and the real data.
+     */
+    protected static class Column {
+
+        protected Fixing.Column meta;
+        protected FixingsColumn data;
+        protected int           index;
+
+        public Column() {
+        }
+
+        public Column(Fixing.Column meta, FixingsColumn data, int index) {
+            this.meta  = meta;
+            this.data  = data;
+            this.index = index;
+        }
+
+        public Date getDate() {
+            return meta.getStartTime();
+        }
+
+        public String getDescription() {
+            return meta.getDescription();
+        }
+
+        public int getIndex() {
+            return index;
+        }
+
+        public boolean getQW(
+            double    km,
+            double [] qs,
+            double [] ws,
+            int       index
+        ) {
+            qs[index] = data.getQ(km);
+            return data.getW(km, ws, index);
+        }
+
+        public boolean getQW(double km, double [] wq) {
+            data.getW(km, wq, 0);
+            if (Double.isNaN(wq[0])) return false;
+            wq[1] = data.getQ(km);
+            return !Double.isNaN(wq[1]);
+        }
+    } // class Column
+
+    /**
+     * Helper class to find the data belonging to meta info more quickly.
+     */
+    protected static class ColumnCache {
+
+        protected Map<Integer, Column> columns;
+
+        public ColumnCache() {
+            columns = new HashMap<Integer, Column>();
+        }
+
+        public Column getColumn(Fixing.Column meta) {
+            Integer key = meta.getId();
+            Column column = columns.get(key);
+            if (column == null) {
+                FixingsColumn data = FixingsColumnFactory
+                    .getInstance()
+                    .getColumnData(meta);
+                if (data != null) {
+                    column = new Column(meta, data, columns.size());
+                    columns.put(key, column);
+                }
+            }
+            return column;
+        }
+    } // class ColumnCache
+
+
+    protected String  river;
+    protected double  from;
+    protected double  to;
+    protected double  step;
+    protected boolean preprocessing;
+    protected String  function;
+    protected int []  events;
+    protected int     qSectorStart;
+    protected int     qSectorEnd;
+
+    public FixCalculation() {
+    }
+
+    public FixCalculation(FixAccess access) {
+        String  river         = access.getRiver();
+        Double  from          = access.getFrom();
+        Double  to            = access.getTo();
+        Double  step          = access.getStep();
+        String  function      = access.getFunction();
+        int []  events        = access.getEvents();
+        Integer qSectorStart  = access.getQSectorStart();
+        Integer qSectorEnd    = access.getQSectorEnd();
+        Boolean preprocessing = access.getPreprocessing();
+
+        if (river == null) {
+            addProblem("fix.missing.river");
+        }
+
+        if (from == null) {
+            addProblem("fix.missing.from");
+        }
+
+        if (to == null) {
+            addProblem("fix.missing.to");
+        }
+
+        if (step == null) {
+            addProblem("fix.missing.step");
+        }
+
+        if (function == null) {
+            addProblem("fix.missing.function");
+        }
+
+        if (events == null || events.length < 1) {
+            addProblem("fix.missing.events");
+        }
+
+        if (qSectorStart == null) {
+            addProblem("fix.missing.qstart.sector");
+        }
+
+        if (qSectorEnd == null) {
+            addProblem("fix.missing.qend.sector");
+        }
+
+        if (preprocessing == null) {
+            addProblem("fix.missing.preprocessing");
+        }
+
+        if (!hasProblems()) {
+            this.river         = river;
+            this.from          = from;
+            this.to            = to;
+            this.step          = step;
+            this.function      = function;
+            this.events        = events;
+            this.qSectorStart  = qSectorStart;
+            this.qSectorEnd    = qSectorEnd;
+            this.preprocessing = preprocessing;
+        }
+    }
+
+    protected static String toString(
+        String [] parameterNames,
+        double [] values
+    ) {
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < parameterNames.length; ++i) {
+            if (i > 0) sb.append(", ");
+            sb.append(parameterNames[i]).append(": ").append(values[i]);
+        }
+        return sb.toString();
+    }
+
+
+    /** Create filter to accept only the chosen events. 
+     *  This factored out out to be overwritten. 
+     */
+    protected Filter createFilter() {
+        return new IdsFilter(events);
+    }
+
+    protected List<Column> getEventColumns(
+        FixingsOverview overview,
+        ColumnCache     cc
+    ) {
+        FixingsColumnFactory fcf = FixingsColumnFactory.getInstance();
+
+        Filter filter = createFilter();
+
+        List<Fixing.Column> metas = overview.filter(null, filter);
+
+        List<Column> columns = new ArrayList<Column>(metas.size());
+
+        for (Fixing.Column meta: metas) {
+
+            Column data = cc.getColumn(meta);
+            if (data == null) {
+                addProblem("fix.cannot.load.data");
+            }
+            else {
+                columns.add(data);
+            }
+        }
+
+        return columns;
+    }
+
+    // Fit a function to the given points from fixation.
+    protected FitResult doFitting(
+        FixingsOverview overview,
+        ColumnCache     cc,
+        Function        func
+    ) {
+        boolean debug = log.isDebugEnabled();
+
+        final List<Column> eventColumns = getEventColumns(overview, cc);
+
+        if (eventColumns.size() < 2) {
+            addProblem("fix.too.less.data.columns");
+            return null;
+        }
+
+        final double  [] qs = new double[eventColumns.size()];
+        final double  [] ws = new double[qs.length];
+        final boolean [] interpolated = new boolean[ws.length];
+
+        Fitting.QWDFactory qwdFactory = new Fitting.QWDFactory() {
+            @Override
+            public QWD create(double q, double w) {
+                // Check all the event columns for close match
+                // and take the description and the date from meta.
+                for (int i = 0; i < qs.length; ++i) {
+                    if (Math.abs(qs[i]-q) < EPSILON
+                    &&  Math.abs(ws[i]-w) < EPSILON) {
+                        Column column = eventColumns.get(i);
+                        return new QWD(
+                            qs[i], ws[i],
+                            column.getDescription(),
+                            column.getDate(),
+                            interpolated[i],
+                            0d,
+                            column.getIndex());
+                    }
+                }
+                log.warn("cannot find column for (" + q + ", " + w + ")");
+                return new QWD(q, w);
+            }
+        };
+
+        Fitting fitting = new Fitting(func, qwdFactory, preprocessing);
+
+        String [] parameterNames = func.getParameterNames();
+
+        Parameters results =
+            new Parameters(
+                StringUtils.join(STANDARD_COLUMNS, parameterNames));
+
+        boolean invalid = false;
+
+        double [] kms = DoubleUtil.explode(from, to, step / 1000.0);
+
+        if (debug) {
+            log.debug("number of kms: " + kms.length);
+        }
+
+        KMIndex<QWI []> outliers   = new KMIndex<QWI []>();
+        KMIndex<QWD []> referenced = new KMIndex<QWD []>(kms.length);
+
+        int kmIndex             = results.columnIndex("km");
+        int chiSqrIndex         = results.columnIndex("chi_sqr");
+        int maxQIndex           = results.columnIndex("max_q");
+        int stdDevIndex         = results.columnIndex("std-dev");
+        int [] parameterIndices = results.columnIndices(parameterNames);
+
+        int numFailed = 0;
+
+        for (int i = 0; i < kms.length; ++i) {
+            double km = kms[i];
+
+            // Fill Qs and Ws from event columns.
+            for (int j = 0; j < ws.length; ++j) {
+                interpolated[j] = !eventColumns.get(j).getQW(km, qs, ws, j);
+            }
+
+            fitting.reset();
+
+            if (!fitting.fit(qs, ws)) {
+                ++numFailed;
+                addProblem(km, "fix.fitting.failed");
+                continue;
+            }
+
+            referenced.add(km, fitting.referencedToArray());
+
+            if (fitting.hasOutliers()) {
+                outliers.add(km, fitting.outliersToArray());
+            }
+
+            int row = results.newRow();
+            double [] values = fitting.getParameters();
+
+            results.set(row, kmIndex, km);
+            results.set(row, chiSqrIndex, fitting.getChiSquare());
+            results.set(row, stdDevIndex, fitting.getStandardDeviation());
+            results.set(row, maxQIndex,   fitting.getMaxQ());
+            invalid |= results.set(row, parameterIndices, values);
+
+            if (debug) {
+                log.debug("km: "+km+" " + toString(parameterNames, values));
+            }
+        }
+
+        if (debug) {
+            log.debug("success: " + (kms.length - numFailed));
+            log.debug("failed: " + numFailed);
+        }
+
+        if (invalid) {
+            addProblem("fix.invalid.values");
+            results.removeNaNs();
+        }
+
+        outliers.sort();
+        referenced.sort();
+
+        return new FitResult(
+            results,
+            referenced,
+            outliers);
+    }
+
+    public CalculationResult calculate() {
+        FixingsOverview overview =
+            FixingsOverviewFactory.getOverview(river);
+
+        if (overview == null) {
+            addProblem("fix.no.overview.available");
+        }
+
+        Function func = FunctionFactory.getInstance()
+            .getFunction(function);
+
+        if (func == null) {
+            addProblem("fix.invalid.function.name");
+        }
+
+        if (hasProblems()) {
+            return new CalculationResult(this);
+        }
+
+        return innerCalculate(overview, func);
+    }
+
+    protected abstract CalculationResult innerCalculate(
+        FixingsOverview overview,
+        Function        function
+    );
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixDerivateFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,119 @@
+package org.dive4elements.river.artifacts.model.fixings;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+import org.dive4elements.river.artifacts.access.FixAnalysisAccess;
+
+import org.dive4elements.river.artifacts.math.fitting.Function;
+import org.dive4elements.river.artifacts.math.fitting.FunctionFactory;
+
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.Parameters;
+
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+import org.apache.log4j.Logger;
+
+
+/**
+ * Facet to show the W|Q values.
+ *
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class FixDerivateFacet
+extends      FixingsFacet
+implements   FacetTypes {
+
+    /** House logger. */
+    private static Logger logger = Logger.getLogger(FixDerivateFacet.class);
+
+
+    /** Trivial Constructor. */
+    public FixDerivateFacet() {
+    }
+
+
+    /**
+     * @param name
+     */
+    public FixDerivateFacet(String name, String description) {
+        super(0, name, description, ComputeType.ADVANCE, null, null);
+    }
+
+    public FixDerivateFacet(int index, String name, String description) {
+        super(index, name, description, ComputeType.ADVANCE, null, null);
+    }
+
+
+    /**
+     * Returns the data this facet requires.
+     *
+     * @param artifact the owner artifact.
+     * @param context  the CallContext (ignored).
+     *
+     * @return the data.
+     */
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+
+        logger.debug("FixDerivateFacet.getData");
+        if (!(artifact instanceof FLYSArtifact)) {
+            logger.debug("Not an instance of FixationArtifact.");
+            return null;
+        }
+
+        FLYSArtifact flys = (FLYSArtifact)artifact;
+        FixAnalysisAccess access = new FixAnalysisAccess(flys, context);
+
+        CalculationResult res =
+            (CalculationResult) flys.compute(context,
+                                             ComputeType.ADVANCE,
+                                             false);
+
+        FixAnalysisResult result = (FixAnalysisResult) res.getData();
+        double currentKm = getCurrentKm(context);
+
+        String function = access.getFunction();
+        Function ff = FunctionFactory.getInstance().getFunction(function);
+        Function.Derivative fd = ff.getDerivative();
+
+        Parameters params = result.getParameters();
+        double maxQ = FixFacetUtils.getMaxQ(params, currentKm);
+
+        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 =
+            fd.instantiate(coeffs);
+
+        FixFunction fix = new FixFunction(
+            "",
+            fd.getDescription(),
+            mf,
+            maxQ);
+
+        return fix;
+    }
+
+
+    /**
+     * Create a deep copy of this Facet.
+     * @return a deep copy.
+     */
+    @Override
+    public FixDerivateFacet deepCopy() {
+        FixDerivateFacet copy = new FixDerivateFacet();
+        copy.set(this);
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixDeviationFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,102 @@
+package org.dive4elements.river.artifacts.model.fixings;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.access.FixAnalysisAccess;
+
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.Parameters;
+
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+import org.apache.log4j.Logger;
+
+
+/**
+ * Facet to show the W|Q values.
+ *
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class FixDeviationFacet
+extends      FixingsFacet
+implements   FacetTypes {
+
+    /** House logger. */
+    private static Logger logger = Logger.getLogger(FixDeviationFacet.class);
+
+    public static final String [] STD_DEV_COLUMN = { "std-dev" };
+
+    /** Trivial Constructor. */
+    public FixDeviationFacet() {
+    }
+
+
+    /**
+     * @param name
+     */
+    public FixDeviationFacet(String name, String description) {
+        super(0, name, description, ComputeType.ADVANCE, null, null);
+    }
+
+    public FixDeviationFacet(int index, String name, String description) {
+        super(index, name, description, ComputeType.ADVANCE, null, null);
+    }
+
+
+    /**
+     * Returns the data this facet requires.
+     *
+     * @param artifact the owner artifact.
+     * @param context  the CallContext (ignored).
+     *
+     * @return the data.
+     */
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        logger.debug("FixDeviationFacet.getData");
+        if (artifact instanceof FLYSArtifact) {
+            FLYSArtifact flys = (FLYSArtifact)artifact;
+            FixAnalysisAccess access = new FixAnalysisAccess(flys, context);
+
+            CalculationResult res =
+                (CalculationResult) flys.compute(context,
+                                                 ComputeType.ADVANCE,
+                                                 false);
+
+            FixAnalysisResult result = (FixAnalysisResult) res.getData();
+            double currentKm = getCurrentKm(context);
+
+            Parameters params = result.getParameters();
+
+            double[] stdDev =
+                params.interpolate("km", currentKm, STD_DEV_COLUMN);
+
+            if (stdDev == null) {
+                logger.warn("getData: stdDev == null at km " + currentKm);
+                return null;
+            }
+
+            return stdDev;
+        }
+        else {
+            logger.debug("Not an instance of FixationArtifact.");
+            return null;
+        }
+    }
+
+
+    /**
+     * Create a deep copy of this Facet.
+     * @return a deep copy.
+     */
+    @Override
+    public FixDerivateFacet deepCopy() {
+        FixDerivateFacet copy = new FixDerivateFacet();
+        copy.set(this);
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixEventFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,62 @@
+package org.dive4elements.river.artifacts.model.fixings;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.model.DataFacet;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.WQKmsFactory;
+import org.dive4elements.river.artifacts.access.FixRealizingAccess;
+
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+
+/**
+ * Facet to show W|Q|km Values.
+ */
+public class FixEventFacet
+extends      DataFacet
+implements   FacetTypes {
+
+    /** House logger. */
+    private static Logger logger = Logger.getLogger(FixEventFacet.class);
+
+
+    public FixEventFacet(int index, String name, String description) {
+        super(index, name, description, ComputeType.FEED, null, null);
+    }
+
+
+    /**
+     * Returns the data this facet requires.
+     *
+     * @param artifact the owner artifact.
+     * @param context  the CallContext (ignored).
+     *
+     * @return the data.
+     */
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        logger.debug("FixEventFacet.getData");
+
+        FixRealizingAccess access = new FixRealizingAccess((FLYSArtifact) artifact, context);
+        int wstColID = access.getEvents()[index];
+        return WQKmsFactory.getWQKmsCID(wstColID);
+    }
+
+
+    /**
+     * Create a deep copy of this Facet.
+     * @return a deep copy.
+     */
+    @Override
+    public FixEventFacet deepCopy() {
+        FixEventFacet copy = new FixEventFacet(index, name, description);
+        copy.set(this);
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixFacetUtils.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,18 @@
+package org.dive4elements.river.artifacts.model.fixings;
+
+import org.dive4elements.river.artifacts.model.Parameters;
+
+public final class FixFacetUtils {
+
+    public static final String [] MAX_Q_COLUMN = { "max_q" };
+
+    public static double getMaxQ(Parameters params, double km) {
+        double [] maxQ = params.interpolate("km", km, MAX_Q_COLUMN);
+        if (maxQ == null) {
+            return 1000d;
+        }
+        double mQ = Math.min(10000d, Math.abs(maxQ[0]));
+        return mQ + 0.05*mQ;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixFunction.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,40 @@
+package org.dive4elements.river.artifacts.model.fixings;
+
+import org.dive4elements.river.artifacts.math.Function;
+
+public class FixFunction
+{
+    protected String   name;
+    protected String   description;
+    protected Function function;
+    protected double   maxQ;
+
+    public FixFunction (
+        String   name,
+        String   description,
+        Function function,
+        double   maxQ
+    ) {
+        this.name        = name;
+        this.description = description;
+        this.function    = function;
+        this.maxQ        = maxQ;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public Function getFunction() {
+        return function;
+    }
+
+    public double getMaxQ() {
+        return maxQ;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixLongitudinalAnalysisFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,108 @@
+package org.dive4elements.river.artifacts.model.fixings;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.DataFacet;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+import org.dive4elements.river.utils.KMIndex;
+
+/**
+ * Facet to show average W values for Q sectors.
+ *
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class FixLongitudinalAnalysisFacet
+extends      DataFacet
+implements   FacetTypes {
+
+    /** House logger. */
+    private static Logger logger = Logger.getLogger(FixLongitudinalAnalysisFacet.class);
+
+    /** Trivial Constructor. */
+    public FixLongitudinalAnalysisFacet() {
+    }
+
+
+    public FixLongitudinalAnalysisFacet(
+        int ndx,
+        String name,
+        String description)
+    {
+        super(
+            ndx,
+            name,
+            description,
+            ComputeType.ADVANCE,
+            null,
+            null);
+    }
+
+
+    /**
+     * Returns the data this facet requires.
+     *
+     * @param artifact the owner artifact.
+     * @param context  the CallContext.
+     *
+     * @return the data as KMIndex.
+     */
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        logger.debug("FixLongitudinalAnalysisFacet.getData");
+
+        if (artifact instanceof FLYSArtifact) {
+            FLYSArtifact flys = (FLYSArtifact)artifact;
+
+            CalculationResult res =
+                (CalculationResult) flys.compute(context,
+                                                 ComputeType.ADVANCE,
+                                                 false);
+
+            FixAnalysisResult result = (FixAnalysisResult) res.getData();
+
+            KMIndex<AnalysisPeriod []> kmPeriods = result.getAnalysisPeriods();
+            if (kmPeriods == null) {
+                logger.warn("No analysis periods found.");
+                return null;
+            }
+            int periodNdx = index >> 8;
+            int qwdNdx = index & 255;
+            KMIndex<QWD> resPeriods =
+                    new KMIndex<QWD>();
+            for (KMIndex.Entry<AnalysisPeriod[]> entry: kmPeriods) {
+                AnalysisPeriod ap = entry.getValue()[periodNdx];
+                QWD[] qwds = ap.qwds;
+                for(int i = 0; i < qwds.length; i++) {
+                    if(qwds[i].getIndex() == qwdNdx) {
+                        resPeriods.add(entry.getKm(), qwds[i]);
+                    }
+                }
+            }
+
+
+            return resPeriods;
+        }
+        else {
+            logger.warn("Artifact is no instance of FLYSArtifact.");
+            return null;
+        }
+    }
+
+
+    /**
+     * Create a deep copy of this Facet.
+     * @return a deep copy.
+     */
+    @Override
+    public FixLongitudinalAnalysisFacet deepCopy() {
+        FixLongitudinalAnalysisFacet copy = new FixLongitudinalAnalysisFacet();
+        copy.set(this);
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixLongitudinalAvSectorFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,97 @@
+package org.dive4elements.river.artifacts.model.fixings;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.DataFacet;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+import org.dive4elements.river.utils.KMIndex;
+
+public class FixLongitudinalAvSectorFacet
+extends DataFacet
+implements FacetTypes {
+
+    /** House logger. */
+    private static Logger logger =
+        Logger.getLogger(FixLongitudinalAvSectorFacet.class);
+
+    /** Trivial Constructor. */
+    public FixLongitudinalAvSectorFacet() {
+    }
+
+
+    public FixLongitudinalAvSectorFacet(
+        int ndx,
+        String name,
+        String description)
+    {
+        super(
+            ndx,
+            name,
+            description,
+            ComputeType.ADVANCE,
+            null,
+            null);
+    }
+
+
+    /**
+     * Returns the data this facet requires.
+     *
+     * @param artifact the owner artifact.
+     * @param context  the CallContext.
+     *
+     * @return the data as KMIndex.
+     */
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        logger.debug("FixLongitudinalAvSectorFacet.getData");
+
+        if (artifact instanceof FLYSArtifact) {
+            FLYSArtifact flys = (FLYSArtifact)artifact;
+
+            CalculationResult res =
+                (CalculationResult) flys.compute(context,
+                                                 ComputeType.ADVANCE,
+                                                 false);
+
+            FixAnalysisResult result = (FixAnalysisResult) res.getData();
+
+            KMIndex<AnalysisPeriod []> kmPeriods = result.getAnalysisPeriods();
+            if (kmPeriods == null) {
+                logger.warn("No analysis periods found.");
+                return null;
+            }
+            int periodNdx = index >> 2;
+            KMIndex<AnalysisPeriod> resPeriods =
+                    new KMIndex<AnalysisPeriod>();
+            for (KMIndex.Entry<AnalysisPeriod[]> entry: kmPeriods) {
+                AnalysisPeriod ap = entry.getValue()[periodNdx];
+                resPeriods.add(entry.getKm(), ap);
+            }
+
+            return resPeriods;
+        }
+        else {
+            logger.warn("Artifact is no instance of FLYSArtifact.");
+            return null;
+        }
+    }
+
+
+    /**
+     * Create a deep copy of this Facet.
+     * @return a deep copy.
+     */
+    @Override
+    public FixLongitudinalAvSectorFacet deepCopy() {
+        FixLongitudinalAvSectorFacet copy = new FixLongitudinalAvSectorFacet();
+        copy.set(this);
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixLongitudinalDeviationFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,118 @@
+package org.dive4elements.river.artifacts.model.fixings;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+import org.dive4elements.river.artifacts.access.FixAnalysisAccess;
+
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.DataFacet;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.Parameters;
+
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+import org.dive4elements.river.utils.KMIndex;
+
+import org.apache.log4j.Logger;
+
+/**
+ * Facet to show average W values for Q sectors.
+ *
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class FixLongitudinalDeviationFacet
+extends      DataFacet
+implements   FacetTypes {
+
+    /** House logger. */
+    private static Logger logger = Logger.getLogger(FixLongitudinalDeviationFacet.class);
+
+    /** Trivial Constructor. */
+    public FixLongitudinalDeviationFacet() {
+    }
+
+
+    public FixLongitudinalDeviationFacet(
+        int ndx,
+        String name,
+        String description)
+    {
+        super(
+            ndx,
+            name,
+            description,
+            ComputeType.ADVANCE,
+            null,
+            null);
+    }
+
+
+    /**
+     * Returns the data this facet requires.
+     *
+     * @param artifact the owner artifact.
+     * @param context  the CallContext.
+     *
+     * @return the data as KMIndex.
+     */
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        logger.debug("FixLongitudinalDeviationFacet.getData");
+
+        if (artifact instanceof FLYSArtifact) {
+            FLYSArtifact flys = (FLYSArtifact)artifact;
+            FixAnalysisAccess access = new FixAnalysisAccess(flys, context);
+
+            CalculationResult res =
+                (CalculationResult) flys.compute(context,
+                                                 ComputeType.ADVANCE,
+                                                 false);
+
+            FixAnalysisResult result = (FixAnalysisResult) res.getData();
+
+            KMIndex<AnalysisPeriod []> kmPeriods = result.getAnalysisPeriods();
+
+            if (kmPeriods == null) {
+                logger.warn("No analysis periods found.");
+                return null;
+            }
+
+            Parameters params = result.getParameters();
+
+            KMIndex<double[]> kmIndex = new KMIndex<double[]>();
+            for (KMIndex.Entry<AnalysisPeriod[]> entry: kmPeriods) {
+                double km = entry.getKm();
+                double[] stdDev =
+                    params.interpolate("km", km, new String[] {"std-dev"});
+
+                if(stdDev == null) {
+                    logger.warn("getData: stdDev == null");
+                    continue;
+                }
+                kmIndex.add(km, stdDev);
+            }
+
+            return kmIndex;
+        }
+        else {
+            logger.warn("Artifact is no instance of FLYSArtifact.");
+            return null;
+        }
+    }
+
+
+    /**
+     * Create a deep copy of this Facet.
+     * @return a deep copy.
+     */
+    @Override
+    public FixLongitudinalDeviationFacet deepCopy() {
+        FixLongitudinalDeviationFacet copy = new FixLongitudinalDeviationFacet();
+        copy.set(this);
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixLongitudinalReferenceFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,115 @@
+package org.dive4elements.river.artifacts.model.fixings;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+import org.dive4elements.river.artifacts.access.FixAnalysisAccess;
+
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.DataFacet;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+import org.dive4elements.river.utils.KMIndex;
+
+import org.apache.log4j.Logger;
+
+
+/**
+ * Facet to show average W values for Q sectors.
+ *
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class FixLongitudinalReferenceFacet
+extends      DataFacet
+implements   FacetTypes {
+
+    /** House logger. */
+    private static Logger logger = Logger.getLogger(FixLongitudinalReferenceFacet.class);
+
+    /** Trivial Constructor. */
+    public FixLongitudinalReferenceFacet() {
+    }
+
+
+    public FixLongitudinalReferenceFacet(
+        int ndx,
+        String name,
+        String description)
+    {
+        super(
+            ndx,
+            name,
+            description,
+            ComputeType.ADVANCE,
+            null,
+            null);
+    }
+
+
+    /**
+     * Returns the data this facet requires.
+     *
+     * @param artifact the owner artifact.
+     * @param context  the CallContext.
+     *
+     * @return the data as KMIndex.
+     */
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        logger.debug("FixLongitudinalReferenceFacet.getData");
+
+        if (artifact instanceof FLYSArtifact) {
+            FLYSArtifact flys = (FLYSArtifact)artifact;
+            FixAnalysisAccess access = new FixAnalysisAccess(flys, context);
+
+            CalculationResult res =
+                (CalculationResult) flys.compute(context,
+                                                 ComputeType.ADVANCE,
+                                                 false);
+
+            FixAnalysisResult result = (FixAnalysisResult) res.getData();
+
+            KMIndex<QWD []> kmReference = result.getReferenced();
+
+            if (kmReference == null) {
+                logger.warn("No references found.");
+                return null;
+            }
+
+            int qwdNdx = index & 255;
+            KMIndex<QWD> resReference =
+                    new KMIndex<QWD>();
+            for (KMIndex.Entry<QWD[]> entry: kmReference) {
+                QWD[] qwds = entry.getValue();
+                for(int i = 0; i < qwds.length; i++) {
+                    if(qwds[i].getIndex() == qwdNdx) {
+                        resReference.add(entry.getKm(), qwds[i]);
+                    }
+                }
+            }
+            return resReference;
+        }
+        else {
+            logger.warn("Artifact is no instance of FLYSArtifact.");
+            return null;
+        }
+    }
+
+
+    /**
+     * Create a deep copy of this Facet.
+     * @return a deep copy.
+     */
+    @Override
+    public FixLongitudinalReferenceFacet deepCopy() {
+        FixLongitudinalReferenceFacet copy =
+            new FixLongitudinalReferenceFacet();
+        copy.set(this);
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixOutlierFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,101 @@
+package org.dive4elements.river.artifacts.model.fixings;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+import org.dive4elements.river.utils.KMIndex;
+
+/**
+ * Facet to show the outliers in a fix calculation.
+ *
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class FixOutlierFacet
+extends      FixingsFacet
+implements   FacetTypes {
+
+    /** House logger. */
+    private static Logger logger = Logger.getLogger(FixOutlierFacet.class);
+
+    /** Trivial Constructor. */
+    public FixOutlierFacet() {
+    }
+
+
+    /**
+     * @param name
+     */
+    public FixOutlierFacet(String name, String description) {
+        super(0, name, description, ComputeType.ADVANCE, null, null);
+    }
+
+    public FixOutlierFacet(int index, String name, String description) {
+        super(index, name, description, ComputeType.ADVANCE, null, null);
+    }
+
+
+    /**
+     * Returns the data this facet requires.
+     *
+     * @param artifact the owner artifact; needs to be a FLYSArtifact.
+     * @param context  the CallContext; required to retrieve the value of
+     * <i>currentKm</i>.
+     *
+     * @return an array of QW objects or null.
+     */
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        logger.debug("FixOutlierFacet.getData");
+
+        if (artifact instanceof FLYSArtifact) {
+            FLYSArtifact flys = (FLYSArtifact)artifact;
+
+            CalculationResult res =
+                (CalculationResult) flys.compute(context,
+                                                 ComputeType.ADVANCE,
+                                                 false);
+
+            FixResult result = (FixResult) res.getData();
+            double currentKm = getCurrentKm(context);
+
+            KMIndex<QWI []>       kmQWs    = result.getOutliers();
+            KMIndex.Entry<QWI []> qwsEntry = kmQWs.binarySearch(currentKm);
+
+            QWI [] qws = null;
+            if (qwsEntry != null) {
+                qws = qwsEntry.getValue();
+
+                if (logger.isDebugEnabled()) {
+                    logger.debug("Found " + (qws != null ? qws.length : 0)
+                        + " KMIndex.Entry for km " + currentKm);
+                }
+            }
+            else {
+                logger.debug("Found no KMIndex.Entry for km " + currentKm);
+            }
+
+            return qws;
+        }
+
+        logger.warn("Not an instance of FLYSArtifact.");
+        return null;
+    }
+
+
+    /**
+     * Create a deep copy of this Facet.
+     * @return a deep copy.
+     */
+    @Override
+    public FixOutlierFacet deepCopy() {
+        FixOutlierFacet copy = new FixOutlierFacet();
+        copy.set(this);
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixRealizingCalculation.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,164 @@
+package org.dive4elements.river.artifacts.model.fixings;
+
+import org.dive4elements.river.artifacts.access.FixRealizingAccess;
+
+import org.dive4elements.river.artifacts.math.fitting.Function;
+
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.FixingsOverview;
+import org.dive4elements.river.artifacts.model.RiverFactory;
+import org.dive4elements.river.artifacts.model.Segment;
+import org.dive4elements.river.artifacts.model.WQKms;
+import org.dive4elements.river.artifacts.model.Parameters;
+
+import org.dive4elements.river.model.River;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+/** Calculation for FixRealize (german: ausgel. WSPL). */
+public class FixRealizingCalculation
+extends      FixCalculation
+{
+    private static Logger log =
+        Logger.getLogger(FixRealizingCalculation.class);
+
+    protected boolean       isQ;
+    protected List<Segment> segments;
+
+    public FixRealizingCalculation() {
+    }
+
+    public FixRealizingCalculation(FixRealizingAccess access) {
+        super(access);
+
+        Boolean       isQ      = access.isQ();
+        List<Segment> segments = access.getSegments();
+
+        if (isQ == null) {
+            // TODO: i18n
+            addProblem("fix.realize.missing.is.q");
+        }
+
+        if (segments == null || segments.isEmpty()) {
+            // TODO: i18n
+            addProblem("fix.realize.missing.segments");
+        }
+
+        River r = RiverFactory.getRiver(river);
+
+        if (r == null) {
+            // TODO: i18n
+            addProblem("fix.no.such.river");
+        }
+
+        if (!hasProblems()) {
+            this.isQ      = isQ;
+            this.segments = segments;
+
+            // Convert from W to Q
+            Segment.setReferencePointConvertQ(segments, r, isQ, this);
+        }
+    }
+
+    @Override
+    protected CalculationResult innerCalculate(
+        FixingsOverview overview,
+        Function        func
+    ) {
+        ColumnCache cc = new ColumnCache();
+        FitResult fitResult = doFitting(overview, cc, func);
+
+        if (fitResult == null) {
+            return new CalculationResult(this);
+        }
+
+        Segment segment = segments.get(0);
+        int numResults = segment.numValues();
+
+        WQKms [] results = new WQKms[numResults];
+        for (int i = 0; i < results.length; ++i) {
+            results[i] = new WQKms();
+        }
+
+        Parameters parameters = fitResult.getParameters();
+
+        int kmIndex = parameters.columnIndex("km");
+        int [] parameterIndices =
+            parameters.columnIndices(func.getParameterNames());
+
+        double [] parameterValues = new double[parameterIndices.length];
+
+        for (int row = 0, R = parameters.size(); row < R; ++row) {
+            double km = parameters.get(row, kmIndex);
+
+            if (!segment.inside(km)) {
+                Segment nextSeg = null;
+                for (Segment seg: segments) {
+                    if (seg.inside(km)) {
+                        nextSeg = seg;
+                        break;
+                    }
+                }
+                if (nextSeg == null) {
+                    // TODO: i18n
+                    addProblem(km, "fix.cannot.find.segment");
+                    continue;
+                }
+                segment = nextSeg;
+            }
+
+            parameters.get(row, parameterIndices, parameterValues);
+
+            org.dive4elements.river.artifacts.math.Function instance =
+                func.instantiate(parameterValues);
+
+            double [] values = segment.getValues();
+            for (int i = 0; i < numResults; ++i) {
+                double q = values[i];
+                double w = instance.value(q);
+
+                if (Double.isNaN(w)) {
+                    // TODO: i18n
+                    addProblem(km, "fix.cannot.calculate.function", q);
+                }
+                else {
+                    results[i].add(w, q, km);
+                }
+            }
+        }
+
+        // Name the curves.
+        for (int i = 0; i < results.length; ++i) {
+            results[i].setName(createName(i));
+        }
+
+        FixRealizingResult frr = new FixRealizingResult(
+            parameters,
+            fitResult.getReferenced(),
+            fitResult.getOutliers(),
+            results);
+
+        return new CalculationResult(frr, this);
+    }
+
+    // TODO: issue1109/2
+    protected String createName(int index) {
+        // TODO: i18n
+        StringBuilder sb = new StringBuilder(isQ ? "Q" : "W");
+        sb.append(" benutzerdefiniert (");
+        for (int i = 0, N = segments.size(); i < N; ++i) {
+            if (i > 0) {
+                sb.append("; ");
+            }
+            Segment segment = segments.get(i);
+            double [] backup = segment.getBackup();
+            double [] values = segment.getValues();
+            sb.append((backup != null ? backup : values)[index]);
+        }
+        sb.append(')');
+        return sb.toString();
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixRealizingResult.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,39 @@
+package org.dive4elements.river.artifacts.model.fixings;
+
+import org.dive4elements.river.artifacts.model.WQKms;
+
+import org.dive4elements.river.artifacts.model.Parameters;
+import org.dive4elements.river.artifacts.model.WQKmsResult;
+
+import org.dive4elements.river.utils.KMIndex;
+
+/** Result of a FixRealizing Calculation. */
+public class FixRealizingResult
+extends      FixResult
+implements   WQKmsResult
+{
+    public WQKms [] wqkms;
+
+    public FixRealizingResult() {
+    }
+
+    public FixRealizingResult(
+        Parameters      parameters,
+        KMIndex<QWD []> referenced,
+        KMIndex<QWI []> outliers,
+        WQKms []        wqkms
+    ) {
+        super(parameters, referenced, outliers);
+        this.wqkms = wqkms;
+    }
+
+    @Override
+    public WQKms [] getWQKms() {
+        return wqkms;
+    }
+
+    public void setWQKms(WQKms [] wqkms) {
+        this.wqkms = wqkms;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixReferenceEventsFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,102 @@
+package org.dive4elements.river.artifacts.model.fixings;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+import org.dive4elements.river.utils.KMIndex;
+
+
+/**
+ * Facet to show W values for Q values at km for a date.
+ *
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class FixReferenceEventsFacet
+extends      FixingsFacet
+implements   FacetTypes {
+
+    /** House logger. */
+    private static Logger logger = Logger.getLogger(FixReferenceEventsFacet.class);
+
+    /** Trivial Constructor. */
+    public FixReferenceEventsFacet() {
+    }
+
+
+    /**
+     * @param name
+     */
+    public FixReferenceEventsFacet(int index, String name, String description) {
+        super(index,
+             name,
+             description,
+             ComputeType.ADVANCE,
+             null,
+             null);
+    }
+
+
+    /**
+     * Returns the data this facet requires.
+     *
+     * @param artifact the owner artifact.
+     * @param context  the CallContext (ignored).
+     *
+     * @return the data.
+     */
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        logger.debug("FixReferenceEventsFacet.getData");
+
+        if (artifact instanceof FLYSArtifact) {
+            FLYSArtifact flys = (FLYSArtifact)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 {
+            logger.debug("Not an instance of FixationArtifact.");
+            return null;
+        }
+    }
+
+
+    /**
+     * Create a deep copy of this Facet.
+     * @return a deep copy.
+     */
+    @Override
+    public FixReferenceEventsFacet deepCopy() {
+        FixReferenceEventsFacet copy = new FixReferenceEventsFacet();
+        copy.set(this);
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixResult.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,53 @@
+package org.dive4elements.river.artifacts.model.fixings;
+
+import org.dive4elements.river.artifacts.model.Parameters;
+
+import org.dive4elements.river.utils.KMIndex;
+
+import java.io.Serializable;
+
+public class FixResult
+implements   Serializable
+{
+    protected Parameters      parameters;
+    protected KMIndex<QWD []> referenced;
+    protected KMIndex<QWI []> outliers;
+
+    public FixResult() {
+    }
+
+    public FixResult(
+        Parameters      parameters,
+        KMIndex<QWD []> referenced,
+        KMIndex<QWI []> outliers
+    ) {
+        this.parameters = parameters;
+        this.referenced = referenced;
+        this.outliers   = outliers;
+    }
+
+    public KMIndex<QWD []> getReferenced() {
+        return referenced;
+    }
+
+    public void setReferenced(KMIndex<QWD []> referenced) {
+        this.referenced = referenced;
+    }
+
+    public KMIndex<QWI []> getOutliers() {
+        return outliers;
+    }
+
+    public void setOutliers(KMIndex<QWI []> outliers) {
+        this.outliers = outliers;
+    }
+
+    public Parameters getParameters() {
+        return parameters;
+    }
+
+    public void setParameters(Parameters parameters) {
+        this.parameters = parameters;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixWQCurveFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,125 @@
+package org.dive4elements.river.artifacts.model.fixings;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+import org.dive4elements.river.artifacts.access.FixAnalysisAccess;
+
+import org.dive4elements.river.artifacts.math.fitting.Function;
+import org.dive4elements.river.artifacts.math.fitting.FunctionFactory;
+
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.Parameters;
+
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+import org.apache.log4j.Logger;
+
+/**
+ * Facet to show the W|Q values.
+ *
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class FixWQCurveFacet
+extends      FixingsFacet
+implements   FacetTypes {
+
+    /** House logger. */
+    private static Logger logger = Logger.getLogger(FixWQCurveFacet.class);
+
+
+    /** Trivial Constructor. */
+    public FixWQCurveFacet() {
+    }
+
+
+    /**
+     * @param description Description of the facet.
+     */
+    public FixWQCurveFacet(String description) {
+        super(0, FIX_WQ_CURVE, description, ComputeType.ADVANCE, null, null);
+    }
+
+    public FixWQCurveFacet(int index, String description) {
+        super(index, FIX_WQ_CURVE, description, ComputeType.ADVANCE, null, null);
+    }
+
+
+    /**
+     * Returns the data this facet provides at given km, a function.
+     *
+     * @param artifact the owner artifact.
+     * @param context  the CallContext.
+     *
+     * @return the data.
+     */
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        logger.debug("getData");
+        if (artifact instanceof FLYSArtifact) {
+            FLYSArtifact flys = (FLYSArtifact)artifact;
+            FixAnalysisAccess access = new FixAnalysisAccess(flys, context);
+
+            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("Not an instance of FLYSArtifact / FixationArtifact.");
+            return null;
+        }
+    }
+
+    /**
+     * Create a deep copy of this Facet.
+     * @return a deep copy.
+     */
+    @Override
+    public FixWQCurveFacet deepCopy() {
+        FixWQCurveFacet copy = new FixWQCurveFacet();
+        copy.set(this);
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixWaterlevelFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,50 @@
+package org.dive4elements.river.artifacts.model.fixings;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.WQKms;
+import org.dive4elements.river.artifacts.model.WaterlevelFacet;
+
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+/** Waterlevel from fix realize compute. */
+public class FixWaterlevelFacet
+extends      WaterlevelFacet
+{
+    public FixWaterlevelFacet() {
+    }
+
+    public FixWaterlevelFacet(int index, String name, String description) {
+        super(index, name, description, ComputeType.ADVANCE, null, null);
+    }
+
+    public FixWaterlevelFacet(
+        int         index,
+        String      name,
+        String      description,
+        ComputeType type,
+        String      stateID,
+        String      hash
+    ) {
+        super(index, name, description, type, hash, stateID);
+    }
+
+    @Override
+    protected WQKms [] getWQKms(CalculationResult res) {
+        FixRealizingResult fr = (FixRealizingResult)res.getData();
+        return fr != null ? fr.getWQKms() : null;
+    }
+
+    /** Copy deeply. */
+    @Override
+    public Facet deepCopy() {
+        FixWaterlevelFacet copy = new FixWaterlevelFacet();
+        copy.set(this);
+        copy.type    = type;
+        copy.hash    = hash;
+        copy.stateId = stateId;
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixingsFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,52 @@
+package org.dive4elements.river.artifacts.model.fixings;
+
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.model.DataFacet;
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+/**
+ * Facet to access the current Km from the context safely
+ *
+ * @author <a href="mailto:bjoern.ricks@intevation.de">Björn Ricks</a>
+ */
+public class FixingsFacet extends DataFacet {
+
+    public static final Double INVALID_KM = Double.valueOf(-1d);
+    public static final String CURRENT_KM = "currentKm";
+
+    public FixingsFacet() {
+    }
+
+    public  FixingsFacet(String name, String description) {
+        super(0, name, description, ComputeType.ADVANCE, null, null);
+    }
+
+    public FixingsFacet(
+            int         index,
+            String      name,
+            String      description,
+            ComputeType type,
+            String      hash,
+            String      stateId
+            ) {
+        super(index, name, description, type, hash, stateId);
+    }
+
+    /**
+     * Returns the current km from the context.
+     * If the context is null or doesn't contain a currentKm then a double value of -1 will
+     *  be returned.
+     * @param context The CallContext instance
+     * @return the current km as double
+     */
+    protected double getCurrentKm(CallContext context) {
+        if (context == null) {
+            return INVALID_KM;
+        }
+        Double dkm = (Double)context.getContextValue(CURRENT_KM);
+        if (dkm == null) {
+            return INVALID_KM;
+        }
+        return dkm.doubleValue();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/QWD.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,38 @@
+package org.dive4elements.river.artifacts.model.fixings;
+
+import java.util.Date;
+
+public class QWD
+extends      QWI
+{
+    protected double deltaW;
+
+    public QWD() {
+    }
+
+    public QWD(double q, double w) {
+        super(q, w);
+    }
+
+    public QWD(
+        double  q,
+        double  w,
+        String  description,
+        Date    date,
+        boolean interpolated,
+        double  deltaW,
+        int     index
+    ) {
+        super(q, w, description, date, interpolated, index);
+        this.deltaW = deltaW;
+    }
+
+    public double getDeltaW() {
+        return deltaW;
+    }
+
+    public void setDeltaW(double deltaW) {
+        this.deltaW = deltaW;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/QWI.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,69 @@
+package org.dive4elements.river.artifacts.model.fixings;
+
+import org.dive4elements.river.artifacts.model.QW;
+
+import java.util.Date;
+
+public class QWI
+extends      QW
+{
+    protected String  description;
+    protected Date    date;
+    protected boolean interpolated;
+    protected int     index;
+
+    public QWI() {
+    }
+
+    public QWI(double q, double w) {
+        super(q, w);
+    }
+
+    public QWI(
+        double  q,
+        double  w,
+        String  description,
+        Date    date,
+        boolean interpolated,
+        int     index
+    ) {
+        super(q, w);
+        this.description  = description;
+        this.date         = date;
+        this.interpolated = interpolated;
+        this.index        = index;
+    }
+
+    public Date getDate() {
+        return date;
+    }
+
+    public void setDate(Date date) {
+        this.date = date;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public boolean getInterpolated() {
+        return interpolated;
+    }
+
+    public void setInterpolated(boolean interpolated) {
+        this.interpolated = interpolated;
+    }
+
+    public int getIndex() {
+        return index;
+    }
+
+    public void setIndex(int index) {
+        this.index = index;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/map/HWS.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,153 @@
+package org.dive4elements.river.artifacts.model.map;
+
+import org.geotools.feature.simple.SimpleFeatureBuilder;
+import org.opengis.feature.simple.SimpleFeature;
+import org.opengis.feature.simple.SimpleFeatureType;
+
+import com.vividsolutions.jts.geom.Geometry;
+
+import org.dive4elements.river.artifacts.model.NamedObjectImpl;
+import org.dive4elements.river.utils.GeometryUtils;
+
+
+public class HWS
+extends NamedObjectImpl
+{
+
+    public enum TYPE {LINE, POINT};
+
+    private Geometry geom;
+    private String id;
+    private int kind;
+    private int official;
+    private String fedState;
+    private String description;
+    private TYPE type;
+
+    public HWS() {
+        this.geom = null;
+        // TODO Auto-generated constructor stub
+    }
+
+    public HWS(String name) {
+        super(name);
+        this.geom = null;
+    }
+
+    public HWS(
+        String name,
+        Geometry geom,
+        String id,
+        int kind,
+        int official,
+        String fedState,
+        String description,
+        TYPE type
+    ) {
+        super(name);
+        this.geom = geom;
+        this.id = id;
+        this.kind = kind;
+        this.official = official;
+        this.fedState = fedState;
+        this.description = description;
+        this.type = type;
+    }
+
+    public Geometry getGeom() {
+        return geom;
+    }
+
+    public void setGeom(Geometry geom) {
+        this.geom = geom;
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public int getKind() {
+        return kind;
+    }
+
+    public void setKind(int kind) {
+        this.kind = kind;
+    }
+
+    public boolean isOfficial() {
+        return official == 1;
+    }
+
+    public void setOfficial(boolean o) {
+        this.official = o ? 1 : 0;
+    }
+
+    public String getFedState() {
+        return fedState;
+    }
+
+    public void setFedState(String fedState) {
+        this.fedState = fedState;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public TYPE getType() {
+        return type;
+    }
+
+    public void setType(TYPE type) {
+        this.type = type;
+    }
+
+    public SimpleFeatureType getFeatureType() {
+        int srid = this.geom.getSRID();
+        String srs = "EPSG:" + srid;
+
+        Object[][] attrs = new Object[5][];
+        attrs[0] = new Object[] { "name", String.class };
+        attrs[1] = new Object[] { "description", String.class };
+        attrs[2] = new Object[] { "TYP", String.class };
+        attrs[3] = new Object[] { "fed_state", String.class };
+        attrs[4] = new Object[] { "official", Integer.class };
+        SimpleFeatureType ft =
+            GeometryUtils.buildFeatureType(
+                "hws", srs, this.geom.getClass(), attrs);
+        return ft;
+    }
+
+    public SimpleFeature getFeature() {
+        SimpleFeatureType ft = getFeatureType();
+
+        SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(ft);
+        featureBuilder.add(this.geom);
+        featureBuilder.add(this.name);
+        featureBuilder.add(this.description);
+        if (this.kind == 1) {
+            featureBuilder.add("Rohr 1");
+        }
+        else if (this.kind == 2) {
+            featureBuilder.add("Damm");
+        }
+        else if (this.kind == 3) {
+            featureBuilder.add("Graben");
+        }
+        else {
+            featureBuilder.add("");
+        }
+        featureBuilder.add(this.fedState);
+        featureBuilder.add(this.official);
+
+        return featureBuilder.buildFeature(null);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/map/HWSContainer.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,93 @@
+package org.dive4elements.river.artifacts.model.map;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+public class HWSContainer
+{
+    private static Logger logger = Logger.getLogger(HWSContainer.class);
+    private String river;
+    private HWS.TYPE type;
+    private List<HWS> hws;
+
+    public HWSContainer() {
+        river = null;
+        hws = new ArrayList<HWS>();
+    }
+
+    public HWSContainer(String river, HWS.TYPE type, List<HWS> hws) {
+        this.river = river;
+        this.hws = hws;
+        this.type = type;
+    }
+
+    public void setRiver(String river) {
+        this.river = river;
+    }
+
+    public String getRiver() {
+        return this.river;
+    }
+
+    public HWS.TYPE getType() {
+        return type;
+    }
+
+    public void setType(HWS.TYPE type) {
+        this.type = type;
+    }
+
+    public List<HWS> getHws() {
+        return hws;
+    }
+
+    public void addHws(HWS hws) {
+        logger.debug("add hws: " + hws.getName());
+        this.hws.add(hws);
+    }
+
+    public void addHws(List<HWS> hws) {
+        this.hws.addAll(hws);
+    }
+
+    public List<HWS> getOfficialHWS() {
+        if (hws == null || hws.size() == 0) {
+            return new ArrayList<HWS>();
+        }
+        List<HWS> results = new ArrayList<HWS>();
+        for (HWS h: hws) {
+            if (h.isOfficial()) {
+                results.add(h);
+            }
+        }
+        return results;
+    }
+
+    public List<HWS> getHws(String name) {
+        logger.debug("find: " + name + " in " + hws.size() + " elements");
+        if (hws == null || hws.size() == 0) {
+            return new ArrayList<HWS>();
+        }
+        List<HWS> results = new ArrayList<HWS>();
+        for (HWS h: hws) {
+            if (h.getName().equals(name)) {
+                results.add(h);
+            }
+        }
+        logger.debug("found: " + results.size());
+        return results;
+    }
+
+    public List<HWS> getHws(List<String> list) {
+        if (hws == null || hws.size() == 0) {
+            return new ArrayList<HWS>();
+        }
+        List<HWS> results = new ArrayList<HWS>();
+        for (String name : list) {
+            results.addAll(getHws(name));
+        }
+        return results;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/map/HWSFactory.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,179 @@
+package org.dive4elements.river.artifacts.model.map;
+
+import java.util.List;
+
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.Element;
+
+import org.apache.log4j.Logger;
+import org.hibernate.SQLQuery;
+import org.hibernate.Session;
+import org.hibernate.type.StandardBasicTypes;
+import org.hibernatespatial.GeometryUserType;
+
+import com.vividsolutions.jts.geom.Geometry;
+
+import org.dive4elements.river.artifacts.cache.CacheFactory;
+import org.dive4elements.river.backend.SessionHolder;
+
+
+public class HWSFactory
+{
+    /** Private logger to use here. */
+    private static Logger log = Logger.getLogger(HWSFactory.class);
+
+    private static final int HWS_LINES = 0;
+    private static final int HWS_POINTS = 1;
+
+    public static final String SQL_SELECT_LINES =
+        "SELECT hl.name, hl.geom, hl.id, hl.kind_id, hl.official, fs.name AS fed, hl.description " +
+        "   FROM hws_lines hl" +
+        "       JOIN rivers r ON hl.river_id = r.id" +
+        "       LEFT JOIN fed_states fs ON hl.fed_state_id = fs.id" +
+        "   WHERE r.name = :river";
+
+    public static final String SQL_SELECT_POINTS =
+        "SELECT hp.name, hp.geom, hp.id, hp.kind_id, hp.official, fs.name AS fed, hp.description " +
+        "   FROM hws_points hp" +
+        "       JOIN rivers r ON hp.river_id = r.id" +
+        "       LEFT JOIN fed_states fs ON hp.fed_state_id = fs.id" +
+        "   WHERE r.name = :river";
+
+
+    private HWSFactory() {
+    }
+
+
+    public static HWSContainer getHWSLines(String river) {
+        log.debug("HWSFactory.getHWS");
+        Cache cache = CacheFactory.getCache(StaticHWSCacheKey.CACHE_NAME);
+
+        StaticHWSCacheKey cacheKey;
+
+        if (cache != null) {
+            cacheKey = new StaticHWSCacheKey(river, HWS_LINES);
+            Element element = cache.get(cacheKey);
+            if (element != null) {
+                log.debug("Got static hws values from cache");
+                return (HWSContainer)element.getValue();
+            }
+        }
+        else {
+            cacheKey = null;
+        }
+
+        HWSContainer values = getHWSLinesUncached(river);
+
+        if (values != null && cacheKey != null) {
+            log.debug("Store static hws values in cache.");
+            Element element = new Element(cacheKey, values);
+            cache.put(element);
+        }
+        return values;
+    }
+
+    public static HWSContainer getHWSPoints(String river) {
+        log.debug("HWSFactory.getHWS");
+        Cache cache = CacheFactory.getCache(StaticHWSCacheKey.CACHE_NAME);
+
+        StaticHWSCacheKey cacheKey;
+
+        if (cache != null) {
+            cacheKey = new StaticHWSCacheKey(river, HWS_LINES);
+            Element element = cache.get(cacheKey);
+            if (element != null) {
+                log.debug("Got static hws values from cache");
+                return (HWSContainer)element.getValue();
+            }
+        }
+        else {
+            cacheKey = null;
+        }
+
+        HWSContainer values = getHWSPointsUncached(river);
+
+        if (values != null && cacheKey != null) {
+            log.debug("Store static hws values in cache.");
+            Element element = new Element(cacheKey, values);
+            cache.put(element);
+        }
+        return values;
+    }
+
+    private static HWSContainer getHWSLinesUncached(String river) {
+        if (log.isDebugEnabled()) {
+            log.debug("HWSFactory.getHWSLinesUncached");
+        }
+
+        Session session = SessionHolder.HOLDER.get();
+        SQLQuery sqlQuery = null;
+        HWSContainer container = new HWSContainer();
+        container.setRiver(river);
+        container.setType(HWS.TYPE.LINE);
+        sqlQuery = session.createSQLQuery(SQL_SELECT_LINES)
+            .addScalar("name", StandardBasicTypes.STRING)
+            .addScalar("geom", GeometryUserType.TYPE)
+            .addScalar("id", StandardBasicTypes.STRING)
+            .addScalar("kind_id", StandardBasicTypes.INTEGER)
+            .addScalar("official", StandardBasicTypes.INTEGER)
+            .addScalar("fed", StandardBasicTypes.STRING)
+            .addScalar("description", StandardBasicTypes.STRING);
+
+        sqlQuery.setString("river", river);
+        List<Object []> resultsLines = sqlQuery.list();
+
+        for (Object [] row: resultsLines) {
+            container.addHws(
+                new HWS(
+                    (String) row[0],
+                    (Geometry) row[1],
+                    (String) row[2],
+                    (Integer) row[3],
+                    (Integer) row[4],
+                    (String) row[5],
+                    (String) row[6],
+                    HWS.TYPE.LINE));
+        }
+
+        return container;
+    }
+
+    private static HWSContainer getHWSPointsUncached(String river) {
+        if (log.isDebugEnabled()) {
+            log.debug("HWSFactory.getHWSLinesUncached");
+        }
+
+        Session session = SessionHolder.HOLDER.get();
+        SQLQuery sqlQuery = null;
+        HWSContainer container = new HWSContainer();
+        container.setRiver(river);
+        container.setType(HWS.TYPE.LINE);
+        sqlQuery = session.createSQLQuery(SQL_SELECT_POINTS)
+            .addScalar("name", StandardBasicTypes.STRING)
+            .addScalar("geom", GeometryUserType.TYPE)
+            .addScalar("id", StandardBasicTypes.STRING)
+            .addScalar("kind_id", StandardBasicTypes.INTEGER)
+            .addScalar("official", StandardBasicTypes.INTEGER)
+            .addScalar("fed", StandardBasicTypes.STRING)
+            .addScalar("description", StandardBasicTypes.STRING);
+
+        sqlQuery.setString("river", river);
+        List<Object []> resultsPoints = sqlQuery.list();
+
+        for (int i = 0; i < resultsPoints.size(); i++) {
+            Object[] row = resultsPoints.get(i);
+            container.addHws(
+                new HWS(
+                    (String) row[0],
+                    (Geometry) row[1],
+                    (String) row[2],
+                    (Integer) row[3],
+                    (Integer) row[4],
+                    (String) row[5],
+                    (String) row[6],
+                    HWS.TYPE.POINT));
+        }
+
+        return container;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/map/StaticHWSCacheKey.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,27 @@
+package org.dive4elements.river.artifacts.model.map;
+
+
+public class StaticHWSCacheKey
+{
+    public static final String CACHE_NAME = "hws-value-table-static";
+
+    private String river;
+    private int type;
+
+    public StaticHWSCacheKey(String river, int type) {
+        this.river = river;
+        this.type = type;
+    }
+
+    public int hashCode() {
+        return river.hashCode() | (type << 8);
+    }
+
+    public boolean equals(Object other) {
+        if (!(other instanceof StaticHWSCacheKey)) {
+            return false;
+        }
+        StaticHWSCacheKey o = (StaticHWSCacheKey) other;
+        return this.river == o.river;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/map/WMSDBLayerFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,106 @@
+package org.dive4elements.river.artifacts.model.map;
+
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+
+public class WMSDBLayerFacet extends WMSLayerFacet {
+
+    protected String data;
+    protected String filter;
+    protected String labelItem;
+    protected String geometryType;
+    protected String connection;
+    protected String connectionType;
+
+
+    public WMSDBLayerFacet() {
+        super();
+    }
+
+
+    public WMSDBLayerFacet(int index, String name, String description) {
+        this(index, name, description, ComputeType.FEED, null, null);
+    }
+
+
+    public WMSDBLayerFacet(
+        int         index,
+        String      name,
+        String      description,
+        ComputeType type,
+        String      stateId,
+        String      hash
+
+    ) {
+        super(index, name, description, type, stateId, hash);
+    }
+
+
+    public WMSDBLayerFacet(
+        int         index,
+        String      name,
+        String      description,
+        ComputeType type,
+        String      stateId,
+        String      hash,
+        String      url
+    ) {
+        super(index, name, description, type, stateId, hash, url);
+    }
+
+
+    public void setFilter(String filter) {
+        this.filter = filter;
+    }
+
+    public String getFilter() {
+        return filter;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setGeometryType(String geometryType) {
+        this.geometryType = geometryType;
+    }
+
+    public String getGeometryType() {
+        return geometryType;
+    }
+
+    public void setConnection(String connection) {
+        this.connection = connection;
+    }
+
+    public String getConnection() {
+        return connection;
+    }
+
+    public void setConnectionType(String connectionType) {
+        this.connectionType = connectionType;
+    }
+
+    public String getConnectionType() {
+        return connectionType;
+    }
+
+    public void setLabelItem(String labelItem) {
+        this.labelItem = labelItem;
+    }
+
+    public String getLabelItem() {
+        return labelItem;
+    }
+
+
+    @Override
+    public boolean isQueryable() {
+        return true;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/map/WMSLayerFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,184 @@
+package org.dive4elements.river.artifacts.model.map;
+
+import com.vividsolutions.jts.geom.Envelope;
+
+import org.dive4elements.artifactdatabase.state.DefaultFacet;
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.ArtifactNamespaceContext;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator;
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+import org.dive4elements.river.utils.GeometryUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+
+public class WMSLayerFacet
+extends      DefaultFacet
+{
+    protected ComputeType  type;
+    protected List<String> layers;
+    protected String       stateId;
+    protected String       hash;
+    protected String       url;
+    protected Envelope     extent;
+    protected Envelope     originalExtent;
+    protected String       srid;
+
+
+    private static final Logger logger = Logger.getLogger(WMSLayerFacet.class);
+
+    public WMSLayerFacet() {
+    }
+
+
+    public WMSLayerFacet(int index, String name, String description) {
+        this(index, name, description, ComputeType.FEED, null, null);
+    }
+
+
+    public WMSLayerFacet(
+        int         index,
+        String      name,
+        String      description,
+        ComputeType type,
+        String      stateId,
+        String      hash
+
+    ) {
+        super(index, name, description);
+        this.layers  = new ArrayList<String>();
+        this.type    = type;
+        this.stateId = stateId;
+        this.hash    = hash;
+    }
+
+
+    public WMSLayerFacet(
+        int         index,
+        String      name,
+        String      description,
+        ComputeType type,
+        String      stateId,
+        String      hash,
+        String      url
+    ) {
+        this(index, name, description, type, stateId, hash);
+        this.url = url;
+    }
+
+
+    public void addLayer(String name) {
+        if (name != null && name.length() > 0) {
+            layers.add(name);
+        }
+    }
+
+
+    public List<String> getLayers() {
+        return layers;
+    }
+
+
+    public void removeLayer(String layer) {
+        if (layers != null) {
+            layers.remove(layer);
+        }
+    }
+
+
+    public void setExtent(Envelope extent) {
+        if (extent != null) {
+            this.extent = extent;
+        }
+        else {
+            logger.debug("setExtent(): extent is null");
+        }
+    }
+
+
+    public Envelope getExtent() {
+        return extent;
+    }
+
+
+    public void setOriginalExtent(Envelope originalExtent) {
+        this.originalExtent = originalExtent;
+    }
+
+
+    public Envelope getOriginalExtent() {
+        return originalExtent;
+    }
+
+
+    public void setSrid(String srid) {
+        if (srid != null) {
+            this.srid = srid;
+        }
+    }
+
+
+    public String getSrid() {
+        return srid;
+    }
+
+
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        return null;
+    }
+
+
+    @Override
+    public Node toXML(Document doc) {
+        ElementCreator ec = new ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element facet = ec.create("facet");
+        ec.addAttr(facet, "description", description, true);
+        ec.addAttr(facet, "index", String.valueOf(index), true);
+        ec.addAttr(facet, "name", name, true);
+        ec.addAttr(facet, "url", url, true);
+        ec.addAttr(facet, "layers", layers.get(0), true);
+        ec.addAttr(facet, "srid", srid != null ? srid : "", true);
+        ec.addAttr(facet, "extent", originalExtent != null
+            ? GeometryUtils.jtsBoundsToOLBounds(originalExtent)
+            : "", true);
+        ec.addAttr(facet, "queryable", String.valueOf(isQueryable()), true);
+
+        return facet;
+    }
+
+
+    public boolean isQueryable() {
+        return false;
+    }
+
+
+    @Override
+    public Facet deepCopy() {
+        WMSLayerFacet copy = new WMSLayerFacet();
+        copy.set(this);
+
+        copy.type    = type;
+        copy.layers  = new ArrayList<String>(layers);
+        copy.stateId = stateId;
+        copy.hash    = hash;
+        copy.url     = url;
+        copy.extent  = extent;
+        copy.srid    = srid;
+
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/map/WSPLGENCalculation.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,78 @@
+package org.dive4elements.river.artifacts.model.map;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.river.artifacts.model.Calculation;
+
+
+public class WSPLGENCalculation extends Calculation {
+
+    private static final Logger log = Logger.getLogger(WSPLGENCalculation.class);
+
+    protected Map<Integer, String> errors;
+    protected Map<Integer, String> warnings;
+
+
+    public WSPLGENCalculation() {
+        errors   = new HashMap<Integer, String>();
+        warnings = new HashMap<Integer, String>();
+    }
+
+
+    public void addError(Integer key, String msg) {
+        log.debug("New error: (" + key + ") " + msg);
+        errors.put(key, msg);
+    }
+
+
+    public void addWarning(Integer key, String msg) {
+        log.debug("New warning: (" + key + ") " + msg);
+        warnings.put(key, msg);
+    }
+
+
+    public int numErrors() {
+        return errors.size();
+    }
+
+
+    public int numWarnings() {
+        return warnings.size();
+    }
+
+
+    @Override
+    public void toXML(Document document, CallMeta meta) {
+        Element root = document.createElement("problems");
+
+        if (numErrors() > 0) {
+            for (Map.Entry<Integer, String> entry: errors.entrySet()) {
+                Element problem = document.createElement("problem");
+                problem.setAttribute("error", String.valueOf(entry.getKey()));
+                problem.setTextContent(entry.getValue());
+
+                root.appendChild(problem);
+            }
+        }
+
+        if (numWarnings() > 0) {
+            for (Map.Entry<Integer, String> entry: warnings.entrySet()) {
+                Element problem = document.createElement("problem");
+                problem.setAttribute("error", String.valueOf(entry.getKey()));
+                problem.setTextContent(entry.getValue());
+
+                root.appendChild(problem);
+            }
+        }
+
+        document.appendChild(root);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/map/WSPLGENJob.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,473 @@
+package org.dive4elements.river.artifacts.model.map;
+
+import java.io.IOException;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.wsplgen.FacetCreator;
+
+
+public class WSPLGENJob {
+
+    public static final String GEL_SPERRE   = "SPERRE";
+    public static final String GEL_NOSPERRE = "NOSPERRE";
+
+
+    protected FLYSArtifact artifact;
+
+    protected CallContext callContext;
+
+    protected WSPLGENCalculation calculation;
+
+    protected FacetCreator facetCreator;
+
+    protected File workingDir;
+
+    protected String dgm;
+    protected String pro;
+    protected String wsp;
+    protected String wspTag;
+    protected String axis;
+    protected String area;
+    protected String gel;
+    protected String outFile;
+
+    protected List<String> lin;
+
+    protected int out;
+
+    protected double start;
+    protected double end;
+    protected double from;
+    protected double to;
+    protected double diff;
+    protected double dist;
+
+
+
+    public WSPLGENJob(
+        FLYSArtifact       flys,
+        File               workingDir,
+        FacetCreator       facetCreator,
+        CallContext        context,
+        WSPLGENCalculation calculation)
+    {
+        this.artifact     = flys;
+        this.workingDir   = workingDir;
+        this.facetCreator = facetCreator;
+        this.callContext  = context;
+        this.calculation  = calculation;
+
+        out   = -1;
+        start = Double.NaN;
+        end   = Double.NaN;
+        from  = Double.NaN;
+        to    = Double.NaN;
+        diff  = Double.NaN;
+        dist  = Double.NaN;
+        lin   = new ArrayList<String>(3);
+    }
+
+
+    public File getWorkingDir() {
+        return workingDir;
+    }
+
+
+    public FLYSArtifact getArtifact() {
+        return artifact;
+    }
+
+
+    public FacetCreator getFacetCreator() {
+        return facetCreator;
+    }
+
+
+    public WSPLGENCalculation getCalculation() {
+        return calculation;
+    }
+
+
+    public CallContext getCallContext() {
+        return callContext;
+    }
+
+
+    public void setWsp(String wsp) {
+        this.wsp = wsp;
+    }
+
+
+    public String getWsp() {
+        return wsp;
+    }
+
+
+    public void setWspTag(String wspTag) {
+        this.wspTag = wspTag;
+    }
+
+
+    public String getWspTag() {
+        return wspTag;
+    }
+
+
+    public void addLin(String lin) {
+        this.lin.add(lin);
+    }
+
+
+    public List<String> getLin() {
+        return lin;
+    }
+
+
+    public void setAxis(String axis) {
+        this.axis = axis;
+    }
+
+
+    public String getAxis() {
+        return axis;
+    }
+
+
+    public void setArea(String area) {
+        this.area = area;
+    }
+
+
+    public String getArea() {
+        return area;
+    }
+
+
+    public void setOut(int out) {
+        this.out = out;
+    }
+
+
+    public int getOut() {
+        return out;
+    }
+
+
+    public void setOutFile(String outFile) {
+        this.outFile = outFile;
+    }
+
+
+    public String getOutFile() {
+        return outFile;
+    }
+
+
+    public void setStart(double start) {
+        this.start = start;
+    }
+
+
+    public double getStart() {
+        return start;
+    }
+
+
+    public void setEnd(double end) {
+        this.end = end;
+    }
+
+
+    public double getEnd() {
+        return end;
+    }
+
+
+    public void setPro(String pro) {
+        this.pro = pro;
+    }
+
+
+    public String getPro() {
+        return pro;
+    }
+
+
+    public void setDgm(String dgm) {
+        this.dgm = dgm;
+    }
+
+
+    public String getDgm() {
+        return dgm;
+    }
+
+
+    public void setFrom(double from) {
+        this.from = from;
+    }
+
+
+    public double getFrom() {
+        return from;
+    }
+
+
+    public void setTo(double to) {
+        this.to = to;
+    }
+
+
+    public double getTo() {
+        return to;
+    }
+
+
+    public void setDiff(double diff) {
+        this.diff = diff;
+    }
+
+
+    public double getDiff() {
+        return diff;
+    }
+
+
+    public void setDist(double dist) {
+        this.dist = dist;
+    }
+
+
+    public double getDist() {
+        return dist;
+    }
+
+
+    public void setGel(String gel) {
+        if (gel == null || gel.length() == 0) {
+            return;
+        }
+
+        if (gel.equals(GEL_SPERRE) || gel.equals(GEL_NOSPERRE)) {
+            this.gel = gel;
+        }
+    }
+
+
+    public String getGel() {
+        return gel;
+    }
+
+
+    public void toFile(File file)
+    throws IOException, IllegalArgumentException
+    {
+        PrintWriter writer = null;
+
+        try {
+            writer =
+                new PrintWriter(
+                    new OutputStreamWriter(
+                        new FileOutputStream(file)));
+
+            write(writer);
+        }
+        finally {
+            if (writer != null) {
+                writer.flush();
+                writer.close();
+            }
+        }
+    }
+
+
+    protected void write(PrintWriter writer)
+    throws IOException, IllegalArgumentException
+    {
+        writeWsp(writer);    // required
+        writeWspTag(writer); // required
+        writeLin(writer);
+        writeAxis(writer);
+        writeArea(writer);
+        writeOut(writer);
+        writeOutFile(writer);
+        writeRange(writer);
+        writeDelta(writer);
+        writeGel(writer);
+        writeDist(writer);
+        writePro(writer);
+        writeDgm(writer);    // required
+    }
+
+
+    protected void writeWsp(PrintWriter writer)
+    throws IllegalArgumentException
+    {
+        String wsp = getWsp();
+
+        if (wsp != null && wsp.length() > 0) {
+            writer.println("-WSP=\"" + wsp + "\"");
+            return;
+        }
+
+        throw new IllegalArgumentException("Required WSP missing!");
+    }
+
+    protected void writeWspTag(PrintWriter writer)
+    throws IllegalArgumentException
+    {
+        String wspTag = getWspTag();
+
+        if (wspTag != null && wspTag.length() > 0) {
+            writer.println("-WSPTAG=\"" + wspTag + "\"");
+            return;
+        }
+
+        throw new IllegalArgumentException("Required WSPTAG missing!");
+    }
+
+    protected void writeLin(PrintWriter writer)
+    throws IllegalArgumentException
+    {
+        List<String> lins = getLin();
+
+        if (lins != null && !lins.isEmpty()) {
+            for (String lin: lins) {
+                writer.println("-LIN=\"" + lin + "\"");
+            }
+        }
+    }
+
+    protected void writeAxis(PrintWriter writer)
+    throws IllegalArgumentException
+    {
+        String axis = getAxis();
+
+        if (axis != null && axis.length() > 0) {
+            writer.println("-ACHSE=\"" + axis + "\"");
+        }
+    }
+
+    protected void writeGel(PrintWriter writer)
+    throws IllegalArgumentException
+    {
+        if (area != null && area.length() > 0) {
+            writer.println("-GEL=" + getGel());
+        }
+    }
+
+    protected void writeArea(PrintWriter writer)
+    throws IllegalArgumentException
+    {
+        String area = getArea();
+
+        if (area != null && area.length() > 0) {
+            writer.println("-GEBIET=\"" + area + "\"");
+        }
+    }
+
+
+    protected void writeOut(PrintWriter writer)
+    throws IllegalArgumentException
+    {
+        int out = getOut();
+
+        if (out >= 0) {
+            writer.println("-OUTPUT=" + String.valueOf(out));
+        }
+    }
+
+    protected void writeOutFile(PrintWriter writer)
+    throws IllegalArgumentException
+    {
+        String outFile = getOutFile();
+
+        if (outFile != null && outFile.length() > 0) {
+            writer.println("-AUSGABE=\""+ outFile + "\"");
+        }
+    }
+
+    protected void writeRange(PrintWriter writer)
+    throws IllegalArgumentException
+    {
+        StringBuilder sb = new StringBuilder("-STRECKE=");
+
+        double start = getStart();
+        double end   = getEnd();
+
+        if (Double.isNaN(start) && Double.isNaN(end)) {
+            return;
+        }
+
+        if (! Double.isNaN(getStart())) {
+            sb.append(getStart());
+        }
+
+        sb.append(",");
+
+        if (! Double.isNaN(getEnd())) {
+            sb.append(getEnd());
+        }
+
+        writer.println(sb.toString());
+    }
+
+    protected void writeDelta(PrintWriter writer)
+    throws IllegalArgumentException
+    {
+        StringBuilder sb = new StringBuilder("-DELTA=");
+        if (! Double.isNaN(from)) {
+            sb.append(from);
+        }
+
+        sb.append(",");
+
+        if (! Double.isNaN(to)) {
+            sb.append(to);
+        }
+
+        sb.append(",");
+
+        if (! Double.isNaN(diff)) {
+            sb.append(diff);
+        }
+
+        writer.println(sb.toString());
+    }
+
+    protected void writeDist(PrintWriter writer)
+    throws IllegalArgumentException
+    {
+        if (! Double.isNaN(getDist())) {
+            writer.println("-DIST=" + String.valueOf(getDist()));
+        }
+    }
+
+    protected void writePro(PrintWriter writer)
+    throws IllegalArgumentException
+    {
+        if (pro != null && pro.length() > 0) {
+            writer.println("-PRO=\"" + getPro() + "\"");
+        }
+    }
+
+    protected void writeDgm(PrintWriter writer)
+    throws IllegalArgumentException
+    {
+        if (dgm != null && dgm.length() > 0) {
+            writer.println("-DGM=\"" + getDgm() + "\"");
+            return;
+        }
+
+        throw new IllegalArgumentException("Required DGM missing!");
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/map/WSPLGENLayerFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,46 @@
+package org.dive4elements.river.artifacts.model.map;
+
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+public class WSPLGENLayerFacet
+extends      WMSLayerFacet
+{
+    public WSPLGENLayerFacet() {
+    }
+
+
+    public WSPLGENLayerFacet(int index, String name, String description) {
+        this(index, name, description, ComputeType.FEED, null, null);
+    }
+
+
+    public WSPLGENLayerFacet(
+        int         index,
+        String      name,
+        String      description,
+        ComputeType type,
+        String      stateId,
+        String      hash
+    ) {
+        super(index, name, description, type, stateId, hash);
+    }
+
+    public WSPLGENLayerFacet(
+        int         index,
+        String      name,
+        String      description,
+        ComputeType type,
+        String      stateId,
+        String      hash,
+        String      url
+    ) {
+        super(index, name, description, type, stateId, hash, url);
+    }
+
+
+    @Override
+    public boolean isQueryable() {
+        return true;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/map/WSPLGENReportFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,58 @@
+package org.dive4elements.river.artifacts.model.map;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.ReportFacet;
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+
+/**
+ * This facet is used to provide WSPLGEN reports <b>only</b>.
+ */
+public class WSPLGENReportFacet extends ReportFacet {
+
+    private static Logger logger = Logger.getLogger(WSPLGENReportFacet.class);
+
+
+    protected CalculationResult result;
+
+
+    public WSPLGENReportFacet() {
+    }
+
+
+    public WSPLGENReportFacet(
+        ComputeType       type,
+        String            hash,
+        String            stateId,
+        CalculationResult result
+    ) {
+        super(type, hash, stateId);
+        this.result = result;
+    }
+
+
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        return result.getReport();
+    }
+
+
+    @Override
+    public Facet deepCopy() {
+        WSPLGENReportFacet copy = new WSPLGENReportFacet();
+        copy.set(this);
+        copy.type    = type;
+        copy.hash    = hash;
+        copy.stateId = stateId;
+        copy.result  = result;
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedDensityFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,60 @@
+package org.dive4elements.river.artifacts.model.minfo;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.DataFacet;
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+
+/**
+ * Facet for serving bed density data.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class BedDensityFacet extends DataFacet {
+
+    private static final long serialVersionUID = 1L;
+
+    private static Logger logger = Logger.getLogger(BedDensityFacet.class);
+
+    public BedDensityFacet() {
+    }
+
+    public BedDensityFacet(int idx, String name, String description,
+        ComputeType type, String stateId, String hash) {
+        super(idx, name, description, type, hash, stateId);
+    }
+
+    public Object getData(Artifact artifact, CallContext context) {
+        logger.debug("Get data for bed density at index: " + index);
+
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        CalculationResult res = (CalculationResult) flys.compute(context, hash,
+            stateId, type, false);
+
+        int ndx = index >> 8;
+        Object[] data =
+            ((BedQualityResult[]) res.getData())[ndx].getParameters(); // TODO CAST TO SPECIFIC CLASS
+
+        int ndy = index & 255;
+        return data != null && data.length > ndy ? data[ndy] : null;
+    }
+
+    /** Copy deeply. */
+    @Override
+    public Facet deepCopy() {
+        BedDensityFacet copy = new BedDensityFacet();
+        copy.set(this);
+        copy.type = type;
+        copy.hash = hash;
+        copy.stateId = stateId;
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedDiameterFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,59 @@
+package org.dive4elements.river.artifacts.model.minfo;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.DataFacet;
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+
+/**
+ * Facet for serving bed diameter data.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class BedDiameterFacet extends DataFacet {
+
+    private static final long serialVersionUID = 1L;
+
+    private static Logger logger = Logger.getLogger(BedDiameterFacet.class);
+
+    public BedDiameterFacet() {
+    }
+
+    public BedDiameterFacet(int idx, String name, String description,
+        ComputeType type, String stateId, String hash) {
+        super(idx, name, description, type, hash, stateId);
+    }
+
+    public Object getData(Artifact artifact, CallContext context) {
+        logger.debug("Get data for bed diameter at index: " + index);
+
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        CalculationResult res = (CalculationResult) flys.compute(context, hash,
+            stateId, type, false);
+
+        int ndx = index >> 8;
+        Object[] data = ((BedQualityResult[]) res.getData())[ndx].getBedResults(); // TODO CAST TO SPECIFIC CLASS
+
+        int ndy = index & 255;
+        return data != null && data.length > ndy ? data[ndy] : null;
+    }
+
+    /** Copy deeply. */
+    @Override
+    public Facet deepCopy() {
+        BedDiameterFacet copy = new BedDiameterFacet();
+        copy.set(this);
+        copy.type = type;
+        copy.hash = hash;
+        copy.stateId = stateId;
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedDiameterResult.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,64 @@
+package org.dive4elements.river.artifacts.model.minfo;
+
+import gnu.trove.TDoubleArrayList;
+
+
+public class BedDiameterResult
+extends BedQualityDiameterResult
+{
+    protected TDoubleArrayList diameterCap;
+    protected TDoubleArrayList diameterSub;
+
+    public BedDiameterResult (
+        String type,
+        TDoubleArrayList diameterCap,
+        TDoubleArrayList diameterSub,
+        TDoubleArrayList km
+    ) {
+        super(type, km);
+        this.diameterCap = diameterCap;
+        this.diameterSub = diameterSub;
+    }
+
+    public double getDiameterCap(int ndx) {
+        if (diameterCap != null) {
+            return this.diameterCap.get(ndx);
+        }
+        return Double.NaN;
+    }
+
+    public double getDiameterSub(int ndx) {
+        if (diameterSub != null) {
+            return this.diameterSub.get(ndx);
+        }
+        return Double.NaN;
+    }
+
+    public double getDiameterCap(double km) {
+        if (kms.indexOf(km) >= 0) {
+            return diameterCap.get(kms.indexOf(km));
+        }
+        return Double.NaN;
+    }
+
+    public double getDiameterSub(double km) {
+        if (kms.indexOf(km) >= 0) {
+            return diameterSub.get(kms.indexOf(km));
+        }
+        return Double.NaN;
+    }
+
+    public double[][] getDiameterCapData() {
+        return new double[][] {
+            kms.toNativeArray(),
+            diameterCap.toNativeArray()
+        };
+    }
+
+    public double[][] getDiameterSubData() {
+        return new double[][] {
+            kms.toNativeArray(),
+            diameterSub.toNativeArray()
+        };
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedDiffCalculation.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,187 @@
+package org.dive4elements.river.artifacts.model.minfo;
+
+import gnu.trove.TDoubleArrayList;
+
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.access.BedDifferencesAccess;
+import org.dive4elements.river.artifacts.model.Calculation;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+
+
+public class BedDiffCalculation
+extends Calculation
+{
+
+    private static final Logger logger = Logger
+        .getLogger(BedDiffCalculation.class);
+
+    protected String river;
+    protected String yearEpoch;
+    protected FLYSArtifact[][] artifacts;
+
+    public BedDiffCalculation() {
+    }
+
+    public CalculationResult calculate(BedDifferencesAccess access) {
+        logger.info("BedDiffCalculation.calculate");
+
+        String river = access.getRiver();
+        String yearEpoch = access.getYearEpoch();
+        FLYSArtifact[][] artifacts = access.getDifferenceArtifacts();
+
+        logger.debug("got artifacts: " + artifacts.length + "; " + artifacts[0].length);
+        if (river == null) {
+            // TODO: i18n
+            addProblem("minfo.missing.river");
+        }
+
+        if (yearEpoch == null) {
+            addProblem("minfo.missing.year_epoch");
+        }
+
+        if (artifacts == null) {
+            addProblem("minfo.missing.differences");
+        }
+
+        if (!hasProblems()) {
+            this.river = river;
+            this.yearEpoch = yearEpoch;
+            this.artifacts = artifacts;
+            return internalCalculate();
+        }
+
+        return new CalculationResult();
+    }
+
+    private CalculationResult internalCalculate() {
+
+        if (yearEpoch.equals("year")) {
+            List<BedDiffYearResult> results =
+                new LinkedList<BedDiffYearResult>();
+
+            for (int i = 0; i < artifacts.length; i++) {
+                BedHeight[] pair =
+                    getHeightPair(artifacts[i][0], artifacts[i][1], "single");
+                BedDiffYearResult res = calculateYearDifference(pair);
+                results.add(res);
+            }
+            return new CalculationResult(
+                results.toArray(new BedDiffYearResult[results.size()]), this);
+        }
+        if (yearEpoch.equals("epoch")) {
+            List<BedDiffEpochResult> results =
+                new LinkedList<BedDiffEpochResult>();
+            for (int i = 0; i < artifacts.length; i++) {
+                BedHeight[] pair =
+                    getHeightPair(artifacts[i][0], artifacts[i][1], "epoch");
+                BedDiffEpochResult res = calculateEpochDifference(pair);
+                results.add(res);
+            }
+            return new CalculationResult(
+                results.toArray(new BedDiffEpochResult[results.size()]), this);
+        }
+
+       return new CalculationResult();
+    }
+
+    private BedHeight[] getHeightPair(
+        FLYSArtifact art1,
+        FLYSArtifact art2,
+        String type
+    ) {
+        int id1 = BedDifferencesAccess.getHeightId(art1);
+        int id2 = BedDifferencesAccess.getHeightId(art2);
+
+        BedHeight[] heights = new BedHeight[2];
+        heights[0] = BedHeightFactory.getHeight(type, id1, 0);
+        heights[1] = BedHeightFactory.getHeight(type, id2, 0);
+        return heights;
+    }
+
+    private BedDiffEpochResult calculateEpochDifference(BedHeight[] pair) {
+
+        TDoubleArrayList stations = pair[0].getStations();
+        TDoubleArrayList diffRes = new TDoubleArrayList();
+        TDoubleArrayList kms = new TDoubleArrayList();
+        TDoubleArrayList heights1 = new TDoubleArrayList();
+        TDoubleArrayList heights2 = new TDoubleArrayList();
+
+        for (int i = 0; i < stations.size(); i++) {
+            if (!Double.isNaN(pair[0].getHeight(stations.get(i))) &&
+                !Double.isNaN(pair[1].getHeight(stations.get(i)))) {
+                double hDiff =
+                    pair[0].getHeight(stations.get(i)) -
+                    pair[1].getHeight(stations.get(i));
+                diffRes.add(hDiff);
+                kms.add(stations.get(i));
+                heights1.add(pair[0].getHeight(stations.get(i)));
+                heights2.add(pair[1].getHeight(stations.get(i)));
+            }
+        }
+        Date start = ((BedHeightEpoch)pair[0]).getStart();
+        Date end = ((BedHeightEpoch)pair[1]).getEnd();
+        return new BedDiffEpochResult(kms, diffRes, heights1, heights2, start, end);
+    }
+
+    private BedDiffYearResult calculateYearDifference(BedHeight[] pair) {
+
+        TDoubleArrayList stations = pair[0].getStations();
+        TDoubleArrayList diffRes = new TDoubleArrayList();
+        TDoubleArrayList kms = new TDoubleArrayList();
+        TDoubleArrayList morphs = new TDoubleArrayList();
+        TDoubleArrayList absolute = new TDoubleArrayList();
+        TDoubleArrayList gap = new TDoubleArrayList();
+        TDoubleArrayList heights1 = new TDoubleArrayList();
+        TDoubleArrayList heights2 = new TDoubleArrayList();
+
+        BedHeightSingle s1 = (BedHeightSingle)pair[0];
+        BedHeightSingle s2 = (BedHeightSingle)pair[1];
+        int range = s1.getYear() - s2.getYear();
+        if (range  < 0) {
+            range = range * -1;
+        }
+        for (int i = 0; i < stations.size(); i++) {
+            if (!Double.isNaN(s1.getHeight(stations.get(i))) &&
+                !Double.isNaN(s2.getHeight(stations.get(i)))) {
+                double hDiff =
+                    s1.getHeight(stations.get(i)) -
+                    s2.getHeight(stations.get(i));
+                diffRes.add(hDiff);
+                double km = stations.get(i);
+                kms.add(km);
+                if (s1.getMorphWidth(km) >
+                    s2.getMorphWidth(km)) {
+                    morphs.add(s1.getMorphWidth(km));
+                }
+                else {
+                    morphs.add(s2.getMorphWidth(km));
+                }
+                if (s1.getDataGap(km) > s2.getDataGap(km)) {
+                    gap.add(s1.getDataGap(km));
+                }
+                else {
+                    gap.add(s2.getDataGap(km));
+                }
+                absolute.add((hDiff / range) * 100);
+                heights1.add(s1.getHeight(km));
+                heights2.add(s2.getHeight(km));
+            }
+        }
+        return new BedDiffYearResult(
+            kms,
+            diffRes,
+            heights1,
+            heights2,
+            morphs,
+            absolute,
+            gap,
+            s1.getYear(),
+            s2.getYear());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedDiffEpochFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,52 @@
+package org.dive4elements.river.artifacts.model.minfo;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.DataFacet;
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+
+public class BedDiffEpochFacet
+extends DataFacet
+{
+    private static Logger logger = Logger.getLogger(BedDensityFacet.class);
+
+    public BedDiffEpochFacet() {
+    }
+
+    public BedDiffEpochFacet(int idx, String name, String description,
+        ComputeType type, String stateId, String hash) {
+        super(idx, name, description, type, hash, stateId);
+    }
+
+    public Object getData(Artifact artifact, CallContext context) {
+        logger.debug("Get data for bed density at index: " + index);
+
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        CalculationResult res = (CalculationResult) flys.compute(context, hash,
+            stateId, type, false);
+
+        BedDiffEpochResult[] data =
+            (BedDiffEpochResult[]) res.getData();
+
+        return data != null && data.length > index ? data[index] : null;
+    }
+
+    /** Copy deeply. */
+    @Override
+    public Facet deepCopy() {
+        BedDiffEpochFacet copy = new BedDiffEpochFacet();
+        copy.set(this);
+        copy.type = type;
+        copy.hash = hash;
+        copy.stateId = stateId;
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedDiffEpochFilterFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,80 @@
+package org.dive4elements.river.artifacts.model.minfo;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.access.RiverAccess;
+import org.dive4elements.river.artifacts.context.FLYSContext;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.DataFacet;
+import org.dive4elements.river.artifacts.math.MovingAverage;
+import org.dive4elements.river.artifacts.model.ZoomScale;
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+
+public class BedDiffEpochFilterFacet
+extends DataFacet
+{
+    private static Logger logger = Logger.getLogger(BedDensityFacet.class);
+
+    public BedDiffEpochFilterFacet() {
+    }
+
+    public BedDiffEpochFilterFacet(int idx, String name, String description,
+        ComputeType type, String stateId, String hash) {
+        super(idx, name, description, type, hash, stateId);
+    }
+
+    public Object getData(Artifact artifact, CallContext context) {
+        logger.debug("Get data for bed density at index: " + index);
+
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        CalculationResult res = (CalculationResult) flys.compute(context, hash,
+            stateId, type, false);
+
+        BedDiffEpochResult[] data =
+            (BedDiffEpochResult[]) res.getData(); // TODO CAST TO SPECIFIC CLASS
+        Double start = (Double)context.getContextValue("startkm");
+        Double end = (Double)context.getContextValue("endkm");
+
+        if(start != null && end != null) {
+            FLYSContext fc = (FLYSContext)context.globalContext();
+            ZoomScale scales = (ZoomScale)fc.get("zoomscale");
+            RiverAccess access = new RiverAccess((FLYSArtifact)artifact);
+            String river = access.getRiver();
+
+            double radius = scales.getRadius(river, start, end);
+            BedDiffEpochResult oldData = data[index];
+            BedDiffEpochResult newData = new BedDiffEpochResult();
+            newData.setStart(oldData.getStart());
+            newData.setEnd(oldData.getEnd());
+            double[][] diffs = MovingAverage.weighted(oldData.getDifferencesData(), radius);
+            double[][] heights1 = MovingAverage.weighted(oldData.getHeights1Data(), radius);
+            double[][] heights2 = MovingAverage.weighted(oldData.getHeights2Data(), radius);
+            for(int j = 0; j < diffs[0].length; j++) {
+                newData.addKm(diffs[0][j]);
+                newData.addDifference(diffs[1][j]);
+                newData.addHeight1(heights1[1][j]);
+                newData.addHeight2(heights2[1][j]);
+            }
+            return newData;
+        }
+        return data != null && data.length > index ? data[index] : null;
+    }
+
+    /** Copy deeply. */
+    @Override
+    public Facet deepCopy() {
+        BedDiffEpochFilterFacet copy = new BedDiffEpochFilterFacet();
+        copy.set(this);
+        copy.type = type;
+        copy.hash = hash;
+        copy.stateId = stateId;
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedDiffEpochResult.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,47 @@
+package org.dive4elements.river.artifacts.model.minfo;
+
+import java.util.Date;
+
+import gnu.trove.TDoubleArrayList;
+
+
+public class BedDiffEpochResult
+extends BedDifferencesResult
+{
+
+    protected Date start;
+    protected Date end;
+
+    public BedDiffEpochResult () {
+        super();
+    }
+
+    public BedDiffEpochResult (
+        TDoubleArrayList kms,
+        TDoubleArrayList differences,
+        TDoubleArrayList heights1,
+        TDoubleArrayList heights2,
+        Date start,
+        Date end
+    ) {
+        super(kms, differences, heights1, heights2);
+        this.start = start;
+        this.end = end;
+    }
+
+    public Date getStart() {
+        return this.start;
+    }
+
+    public void setStart(Date value) {
+        this.start = value;
+    }
+
+    public void setEnd(Date value) {
+        this.end = value;
+    }
+
+    public Date getEnd() {
+        return this.end;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedDiffYearFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,52 @@
+package org.dive4elements.river.artifacts.model.minfo;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.DataFacet;
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+
+public class BedDiffYearFacet
+extends DataFacet
+{
+    private static Logger logger = Logger.getLogger(BedDiffYearFacet.class);
+
+    public BedDiffYearFacet() {
+    }
+
+    public BedDiffYearFacet(int idx, String name, String description,
+        ComputeType type, String stateId, String hash) {
+        super(idx, name, description, type, hash, stateId);
+    }
+
+    public Object getData(Artifact artifact, CallContext context) {
+        logger.debug("Get data for bed density at index: " + index);
+
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        CalculationResult res = (CalculationResult) flys.compute(context, hash,
+            stateId, type, false);
+
+        BedDiffYearResult[] data =
+            (BedDiffYearResult[]) res.getData(); // TODO CAST TO SPECIFIC CLASS
+
+        return data != null && data.length > index ? data[index] : null;
+    }
+
+    /** Copy deeply. */
+    @Override
+    public Facet deepCopy() {
+        BedDiffYearFacet copy = new BedDiffYearFacet();
+        copy.set(this);
+        copy.type = type;
+        copy.hash = hash;
+        copy.stateId = stateId;
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedDiffYearFilterFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,83 @@
+package org.dive4elements.river.artifacts.model.minfo;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.access.RiverAccess;
+import org.dive4elements.river.artifacts.context.FLYSContext;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.DataFacet;
+import org.dive4elements.river.artifacts.math.MovingAverage;
+import org.dive4elements.river.artifacts.model.ZoomScale;
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+
+public class BedDiffYearFilterFacet
+extends DataFacet
+{
+    private static Logger logger = Logger.getLogger(BedDiffYearFilterFacet.class);
+
+    public BedDiffYearFilterFacet() {
+    }
+
+    public BedDiffYearFilterFacet(int idx, String name, String description,
+        ComputeType type, String stateId, String hash) {
+        super(idx, name, description, type, hash, stateId);
+    }
+
+    public Object getData(Artifact artifact, CallContext context) {
+        logger.debug("Get data for bed density at index: " + index);
+
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        CalculationResult res = (CalculationResult) flys.compute(context, hash,
+            stateId, type, false);
+
+        BedDiffYearResult[] data =
+            (BedDiffYearResult[]) res.getData(); // TODO CAST TO SPECIFIC CLASS
+        Double start = (Double)context.getContextValue("startkm");
+        Double end = (Double)context.getContextValue("endkm");
+        if(start != null && end != null) {
+            FLYSContext fc = (FLYSContext)context.globalContext();
+            ZoomScale scales = (ZoomScale)fc.get("zoomscale");
+            RiverAccess access = new RiverAccess((FLYSArtifact)artifact);
+            String river = access.getRiver();
+
+            double radius = scales.getRadius(river, start, end);
+            BedDiffYearResult oldData = data[index];
+            BedDiffYearResult newData = new BedDiffYearResult();
+            newData.setStart(oldData.getStart());
+            newData.setEnd(oldData.getEnd());
+            double[][] diffs = MovingAverage.weighted(oldData.getDifferencesData(), radius);
+            double[][] heights1 = MovingAverage.weighted(oldData.getHeights1Data(), radius);
+            double[][] heights2 = MovingAverage.weighted(oldData.getHeights2Data(), radius);
+            double[][] morph = oldData.getMorphWidthData();
+            double[][] year = MovingAverage.weighted(oldData.getHeightPerYearData(), radius);
+            for(int j = 0; j < diffs[0].length; j++) {
+                newData.addKm(diffs[0][j]);
+                newData.addBedHeights(year[1][j]);
+                newData.addMorphWidth(morph[1][j]);
+                newData.addDifference(diffs[1][j]);
+                newData.addHeight1(heights1[1][j]);
+                newData.addHeight2(heights2[1][j]);
+            }
+            return newData;
+        }
+        return data != null && data.length > index ? data[index] : null;
+    }
+
+    /** Copy deeply. */
+    @Override
+    public Facet deepCopy() {
+        BedDiffYearFilterFacet copy = new BedDiffYearFilterFacet();
+        copy.set(this);
+        copy.type = type;
+        copy.hash = hash;
+        copy.stateId = stateId;
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedDiffYearResult.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,93 @@
+package org.dive4elements.river.artifacts.model.minfo;
+
+import gnu.trove.TDoubleArrayList;
+
+
+public class BedDiffYearResult
+extends BedDifferencesResult
+{
+
+    protected TDoubleArrayList bedHeights;
+    protected TDoubleArrayList dataGap;
+    protected TDoubleArrayList morphWidth;
+    protected int start;
+    protected int end;
+
+    public BedDiffYearResult () {
+        super();
+        this.bedHeights = new TDoubleArrayList();
+        this.dataGap = new TDoubleArrayList();
+        this.morphWidth = new TDoubleArrayList();
+        this.start = -1;
+        this.end = -1;
+    }
+
+    public BedDiffYearResult(
+        TDoubleArrayList kms,
+        TDoubleArrayList differences,
+        TDoubleArrayList heights1,
+        TDoubleArrayList heights2,
+        TDoubleArrayList morphWidth,
+        TDoubleArrayList bedHeights,
+        TDoubleArrayList dataGap,
+        int start,
+        int end
+    ) {
+        super(kms, differences, heights1, heights2);
+        this.bedHeights = bedHeights;
+        this.dataGap = dataGap;
+        this.morphWidth = morphWidth;
+        this.start = start;
+        this.end = end;
+    }
+
+    public TDoubleArrayList getBedHeights() {
+        return this.bedHeights;
+    }
+
+    public TDoubleArrayList getDataGap() {
+        return this.dataGap;
+    }
+
+    public TDoubleArrayList getMorphWidth() {
+        return this.morphWidth;
+    }
+
+    public int getStart() {
+        return this.start;
+    }
+
+    public void setStart(int value) {
+        this.start = value;
+    }
+
+    public void setEnd(int value) {
+        this.end = value;
+    }
+
+    public int getEnd() {
+        return this.end;
+    }
+
+    public void addMorphWidth(double value) {
+        this.morphWidth.add(value);
+    }
+
+    public void addBedHeights(double value) {
+        this.bedHeights.add(value);
+    }
+
+    public double[][] getMorphWidthData() {
+        return new double[][] {
+            kms.toNativeArray(),
+            morphWidth.toNativeArray()
+        };
+    }
+
+    public double[][] getHeightPerYearData() {
+        return new double[][] {
+            kms.toNativeArray(),
+            bedHeights.toNativeArray()
+        };
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedDifferencesResult.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,88 @@
+package org.dive4elements.river.artifacts.model.minfo;
+
+import gnu.trove.TDoubleArrayList;
+
+import java.io.Serializable;
+
+
+public class BedDifferencesResult
+implements Serializable
+{
+
+    protected TDoubleArrayList kms;
+    protected TDoubleArrayList differences;
+    protected TDoubleArrayList height1;
+    protected TDoubleArrayList height2;
+
+    public BedDifferencesResult () {
+        kms = new TDoubleArrayList();
+        differences = new TDoubleArrayList();
+        height1 = new TDoubleArrayList();
+        height2 = new TDoubleArrayList();
+    }
+
+    public BedDifferencesResult(
+        TDoubleArrayList kms,
+        TDoubleArrayList differences,
+        TDoubleArrayList heights1,
+        TDoubleArrayList heights2
+    ) {
+        this.kms = kms;
+        this.differences = differences;
+        this.height1 = heights1;
+        this.height2 = heights2;
+    }
+
+    public TDoubleArrayList getKms() {
+        return this.kms;
+    }
+
+    public TDoubleArrayList getDifferences() {
+        return this.differences;
+    }
+
+    public void addKm(double value) {
+        this.kms.add(value);
+    }
+
+    public void addDifference(double value) {
+        this.differences.add(value);
+    }
+
+    public void addHeight1(double value) {
+        this.height1.add(value);
+    }
+
+    public void addHeight2(double value) {
+        this.height2.add(value);
+    }
+
+    public double[][] getDifferencesData() {
+        return new double[][] {
+            kms.toNativeArray(),
+            differences.toNativeArray()
+        };
+    }
+
+    public TDoubleArrayList getHeights1() {
+        return this.height1;
+    }
+
+    public TDoubleArrayList getHeights2() {
+        return this.height2;
+    }
+
+    public double[][] getHeights1Data() {
+        return new double[][] {
+            kms.toNativeArray(),
+            height1.toNativeArray()
+        };
+    }
+
+    public double[][] getHeights2Data() {
+        return new double[][] {
+            kms.toNativeArray(),
+            height2.toNativeArray()
+        };
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedHeight.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,109 @@
+package org.dive4elements.river.artifacts.model.minfo;
+
+import org.apache.log4j.Logger;
+
+import gnu.trove.TDoubleArrayList;
+import org.dive4elements.river.artifacts.model.NamedObjectImpl;
+
+public class BedHeight
+extends NamedObjectImpl
+{
+    private static Logger log = Logger.getLogger(BedHeight.class);
+
+    protected TDoubleArrayList heights;
+    protected TDoubleArrayList station;
+
+    public BedHeight() {
+        heights = new TDoubleArrayList();
+        station = new TDoubleArrayList();
+    }
+
+    public BedHeight(String name) {
+        super(name);
+        heights = new TDoubleArrayList();
+        station = new TDoubleArrayList();
+    }
+
+    public BedHeight(int capacity) {
+        this(capacity, "");
+    }
+
+    public BedHeight(int capacity, String name) {
+        super(name);
+        heights = new TDoubleArrayList(capacity);
+        station = new TDoubleArrayList(capacity);
+    }
+
+    public void add(double value, double station) {
+        this.heights.add(value);
+        this.station.add(station);
+    }
+
+    public int size() {
+        return heights.size();
+    }
+
+    public double getHeight(int idx) {
+        return heights.getQuick(idx);
+    }
+
+    public double [] getHeights() {
+        return heights.toNativeArray();
+    }
+
+    public double [] get(int idx) {
+        return get(idx, new double [3]);
+    }
+
+    public double [] get(int idx, double [] dst) {
+        dst[0] = heights.getQuick(idx);
+        dst[1] = station.getQuick(idx);
+        return dst;
+    }
+
+   public double minHeights() {
+        return heights.min();
+    }
+
+    public TDoubleArrayList getStations() {
+        return this.station;
+    }
+
+    public double getHeight(double station) {
+        if (this.station.indexOf(station) >= 0) {
+            return this.heights.get(this.station.indexOf(station));
+        }
+        return Double.NaN;
+    }
+
+
+    public static void removeNaNs(TDoubleArrayList [] arrays) {
+
+        int dest = 0;
+
+        int A = arrays.length;
+        int N = arrays[0].size();
+
+        OUTER: for (int i = 0; i < N; ++i) {
+            for (int j = 0; j < A; ++j) {
+                TDoubleArrayList a = arrays[j];
+                double v = a.getQuick(i);
+                if (Double.isNaN(v)) {
+                    continue OUTER;
+                }
+                a.setQuick(dest, v);
+            }
+            ++dest;
+        }
+
+        if (dest < N) {
+            for (int i = 0; i < A; ++i) {
+                arrays[i].remove(dest, N-dest);
+            }
+        }
+    }
+
+    public void removeNaNs() {
+        removeNaNs(new TDoubleArrayList [] { heights });
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedHeightEpoch.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,42 @@
+package org.dive4elements.river.artifacts.model.minfo;
+
+import java.util.Date;
+
+
+public class BedHeightEpoch
+extends BedHeight
+{
+
+    protected Date start;
+    protected Date end;
+
+    public BedHeightEpoch() {
+        this.start = new Date();
+        this.end = new Date();
+    }
+
+    public BedHeightEpoch(String name) {
+        super(name);
+        this.start = new Date();
+        this.end = new Date();
+    }
+
+    public void add(
+        double value,
+        double station,
+        Date start,
+        Date end
+    ) {
+        super.add(value, station);
+        this.start = start;
+        this.end = end;
+    }
+
+    public Date getStart() {
+        return start;
+    }
+
+    public Date getEnd() {
+        return end;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedHeightFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,50 @@
+package org.dive4elements.river.artifacts.model.minfo;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.access.BedHeightAccess;
+import org.dive4elements.river.artifacts.model.BlackboardDataFacet;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+
+public class BedHeightFacet
+extends      BlackboardDataFacet
+implements   FacetTypes {
+
+    private String type;
+
+    public BedHeightFacet(String name, String description, String type) {
+        this.name = name;
+        this.description = description;
+        this.type = type;
+        this.index = 0;
+    }
+
+    /**
+     * Returns the data this facet requires.
+     *
+     * @param artifact the owner artifact.
+     * @param context  the CallContext (ignored).
+     *
+     * @return the data.
+     */
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        BedHeightAccess access = new BedHeightAccess((FLYSArtifact)artifact, context);
+        if (type.equals("singlevalues")) {
+            return access.getSingleValues();
+        }
+        return access.getHeight();
+    }
+    /**
+     * Create a deep copy of this Facet.
+     * @return a deep copy.
+     */
+    @Override
+    public BedHeightFacet deepCopy() {
+        BedHeightFacet copy = new BedHeightFacet(name, description, type);
+        copy.set(this);
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedHeightFactory.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,178 @@
+package org.dive4elements.river.artifacts.model.minfo;
+
+
+import java.util.Date;
+import java.util.List;
+
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.Element;
+
+import org.apache.log4j.Logger;
+import org.hibernate.SQLQuery;
+import org.hibernate.Session;
+import org.hibernate.type.StandardBasicTypes;
+
+import org.dive4elements.river.artifacts.cache.CacheFactory;
+import org.dive4elements.river.artifacts.model.StaticBedHeightCacheKey;
+import org.dive4elements.river.backend.SessionHolder;
+
+public class BedHeightFactory {
+    /** Private logger to use here. */
+    private static Logger log = Logger.getLogger(BedHeightFactory.class);
+
+    /** Query to get km and ws for wst_id and column_pos. */
+    public static final String SQL_SELECT_SINGLE =
+        "SELECT bhsv.height, bhsv.station, bhsv.data_gap, bhsv.sounding_width, bhs.year " +
+        "   FROM bed_height_single bhs" +
+        "       JOIN bed_height_single_values bhsv on bhsv.bed_height_single_id = bhs.id" +
+        "   WHERE bhs.id = :height_id";
+
+    /** Query to get name for wst_id and column_pos. */
+    public static final String SQL_SELECT_EPOCH =
+        "SELECT bv.height, bv.station, ti.start_time, ti.stop_time" +
+        "   FROM bed_height_epoch b" +
+        "       JOIN bed_height_epoch_values bv ON b.id = bv.bed_height_epoch_id" +
+        "       JOIN time_intervals ti ON b.time_interval_id = ti.id" +
+        "   WHERE b.id = :height_id";
+
+    /** Query to get name (description) for wst_id. */
+    public static final String SQL_SELECT_DESCR_SINGLE =
+        "SELECT description FROM bed_height_single "+
+        "WHERE id = :height_id";
+
+    /** Query to get name (description) for wst_id. */
+    public static final String SQL_SELECT_DESCR_EPOCH =
+        "SELECT description from bed_height_epoch "+
+        "WHERE id = :height_id";
+
+
+    private BedHeightFactory() {
+    }
+
+
+    /**
+     * Get WKms for given column and wst_id, caring about the cache.
+     */
+    public static BedHeight getHeight(String type, int height_id, int time) {
+        log.debug("BedHeightFactory.getHeight");
+        Cache cache = CacheFactory.getCache(StaticBedHeightCacheKey.CACHE_NAME);
+
+        StaticBedHeightCacheKey cacheKey;
+
+        if (cache != null) {
+            cacheKey = new StaticBedHeightCacheKey(height_id, time);
+            Element element = cache.get(cacheKey);
+            if (element != null) {
+                log.debug("Got static bedheight values from cache");
+                return (BedHeight)element.getValue();
+            }
+        }
+        else {
+            cacheKey = null;
+        }
+
+        BedHeight values = getBedHeightUncached(type, height_id, time);
+
+        if (values != null && cacheKey != null) {
+            log.debug("Store static bed height values in cache.");
+            Element element = new Element(cacheKey, values);
+            cache.put(element);
+        }
+        return values;
+    }
+
+    /** Get name for a WKms. */
+    public static String getHeightName(String type, int height_id) {
+        log.debug("BedHeightFactory.getHeightName height_id/" + height_id);
+
+        String name = null;
+        Session session = SessionHolder.HOLDER.get();
+
+        SQLQuery nameQuery = null;
+        if (type.equals("single")) {
+            nameQuery = session.createSQLQuery(SQL_SELECT_DESCR_SINGLE)
+                .addScalar("description", StandardBasicTypes.STRING);
+            nameQuery.setInteger("height_id", height_id);
+        }
+        else if (type.equals("epoch")) {
+            nameQuery = session.createSQLQuery(SQL_SELECT_DESCR_EPOCH)
+                .addScalar("description", StandardBasicTypes.STRING);
+            nameQuery.setInteger("height_id", height_id);
+        }
+        else {
+            return "none";
+        }
+        List<String> names = nameQuery.list();
+        if (!names.isEmpty()) {
+            name = names.get(0);
+        }
+
+        return name;
+    }
+
+
+    /**
+     * Get WKms from db.
+     * @param column the position columns value
+     * @param wst_id database id of the wst
+     * @return according WKms.
+     */
+    public static BedHeight getBedHeightUncached(
+        String type,
+        int height_id,
+        int time)
+    {
+        if (log.isDebugEnabled()) {
+            log.debug("BedHeightFactory.getBedHeightUncached");
+        }
+
+        Session session = SessionHolder.HOLDER.get();
+        SQLQuery sqlQuery = null;
+        if (type.equals("single")) {
+            BedHeightSingle height =
+                new BedHeightSingle(getHeightName(type, height_id));
+            sqlQuery = session.createSQLQuery(SQL_SELECT_SINGLE)
+                .addScalar("height", StandardBasicTypes.DOUBLE)
+                .addScalar("station", StandardBasicTypes.DOUBLE)
+                .addScalar("data_gap", StandardBasicTypes.DOUBLE)
+                .addScalar("sounding_width", StandardBasicTypes.DOUBLE)
+                .addScalar("year", StandardBasicTypes.INTEGER);
+            sqlQuery.setInteger("height_id", height_id);
+            List<Object []> results = sqlQuery.list();
+
+            for (int i = 0; i < results.size(); i++) {
+                Object[] row = results.get(i);
+                log.debug("got station: " + (Double)row[1]);
+                height.add(
+                    (Double) row[0],
+                    (Double) row[1],
+                    (Double) row[2],
+                    (Double) row[3],
+                    (Integer) row[4]);
+            }
+            return height;
+        }
+        else if (type.equals("epoch")) {
+            BedHeightEpoch height =
+                new BedHeightEpoch(getHeightName(type, height_id));
+            sqlQuery = session.createSQLQuery(SQL_SELECT_EPOCH)
+                .addScalar("height", StandardBasicTypes.DOUBLE)
+                .addScalar("station", StandardBasicTypes.DOUBLE)
+                .addScalar("start_time", StandardBasicTypes.DATE)
+                .addScalar("stop_time", StandardBasicTypes.DATE);
+            sqlQuery.setInteger("height_id", height_id);
+            List<Object []> results = sqlQuery.list();
+
+            for (Object [] row: results) {
+                height.add(
+                    (Double) row[0],
+                    (Double) row[1],
+                    (Date)   row[2],
+                    (Date)   row[3]);
+            }
+            return height;
+        }
+        return new BedHeight();
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedHeightSingle.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,70 @@
+package org.dive4elements.river.artifacts.model.minfo;
+
+import gnu.trove.TDoubleArrayList;
+
+
+public class BedHeightSingle
+extends BedHeight
+{
+
+    protected int year;
+    protected TDoubleArrayList data_gap;
+    protected TDoubleArrayList morphWidth;
+
+    public BedHeightSingle() {
+        super();
+        this.year = -1;
+        data_gap = new TDoubleArrayList();
+        morphWidth = new TDoubleArrayList();
+    }
+
+    public BedHeightSingle(String name) {
+        super(name);
+        this.year = -1;
+        data_gap = new TDoubleArrayList();
+        morphWidth = new TDoubleArrayList();
+    }
+
+    public void add(
+        double value,
+        double station,
+        double gap,
+        double width,
+        int year
+    ) {
+        super.add(value, station);
+        this.year = year;
+        this.data_gap.add(gap);
+        this.morphWidth.add(width);
+    }
+
+    public int getYear() {
+        return this.year;
+    }
+
+    public double getMorphWidth(int idx) {
+        return this.morphWidth.get(idx);
+    }
+
+    public double getDataGap(int idx) {
+        return this.data_gap.get(idx);
+    }
+
+    public double getMorphWidth(double station) {
+        if (this.station.indexOf(station) >= 0) {
+            return this.morphWidth.get(this.station.indexOf(station));
+        }
+        return Double.NaN;
+    }
+
+    public double getDataGap(double station) {
+        if (this.station.indexOf(station) >= 0) {
+            return this.getDataGap(this.station.indexOf(station));
+        }
+        return Double.NaN;
+    }
+
+    public double[] getMorphWidths() {
+        return this.morphWidth.toNativeArray();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedOverview.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,200 @@
+package org.dive4elements.river.artifacts.model.minfo;
+
+import java.io.Serializable;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+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.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import org.dive4elements.river.utils.KMIndex;
+
+public class BedOverview
+implements Serializable
+{
+    /**
+     * Serial version UId.
+     */
+    private static final long serialVersionUID = -7967134407371364911L;
+
+    public interface Filter {
+        boolean accept(KMIndex<List<Date>> entry);
+
+    } // interface Filter
+
+
+    public static final Filter ACCEPT = new Filter() {
+        public boolean accept(KMIndex<List<Date>> entry) {
+            return true;
+        }
+    };
+
+    public static class KmFilter implements Filter {
+
+        protected double km;
+
+        public KmFilter (double km) {
+            this.km = km;
+        }
+        public boolean accept(KMIndex<List<Date>> list) {
+            for (KMIndex.Entry<List<Date>> e: list){
+                if (e.getKm() == km) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    };
+
+    public static class DateFilter implements Filter {
+
+        protected Date date;
+
+        public DateFilter (Date date) {
+            this.date = date;
+        }
+
+        public boolean accept(KMIndex<List<Date>> list) {
+            for (KMIndex.Entry<List<Date>> e: list){
+                if (e.getValue().equals(this.date)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    };
+
+    private static Logger log = Logger.getLogger(BedOverview.class);
+
+    public static final double EPSILON = 1e-4;
+
+    public static final String DATE_FORMAT = "dd.MM.yyyy";
+
+    public static final String SQL_SQ =
+        "SELECT" +
+        "    so.km    AS km," +
+        "    so.datum AS datum " +
+        "FROM sohltest so " +
+        "    JOIN station s" +
+        "       ON so.stationid = s.stationid " +
+        "    JOIN gewaesser g " +
+        "       ON s.gewaesserid = g.gewaesserid " +
+        "WHERE" +
+        "    g.name = :name AND" +
+        "    so.km IS NOT NULL " +
+        "ORDER by" +
+        "    so.km, so.datum";
+
+    protected String       riverName;
+
+    protected KMIndex<List<Date>> entries;
+
+    public BedOverview() {
+        entries = new KMIndex<List<Date>>();
+    }
+
+    public BedOverview(String riverName) {
+        this();
+        this.riverName = riverName;
+    }
+
+    private static final boolean epsilonEquals(double a, double b) {
+        return Math.abs(a - b) < EPSILON;
+    }
+
+    protected void loadData(Session session) {
+        SQLQuery query = session.createSQLQuery(SQL_SQ)
+            .addScalar("km",    StandardBasicTypes.DOUBLE)
+            .addScalar("datum", StandardBasicTypes.DATE);
+
+        query.setString("name", riverName);
+
+        List<Object []> list = query.list();
+
+        if (list.isEmpty()) {
+            log.warn("No river '" + riverName + "' found.");
+        }
+
+        Double prevKm = -Double.MAX_VALUE;
+        List<Date> dates = new ArrayList<Date>();
+
+        for (Object [] row: list) {
+            Double km = (Double)row[0];
+            if (!epsilonEquals(km, prevKm) && !dates.isEmpty()) {
+                entries.add(prevKm, dates);
+                dates = new ArrayList<Date>();
+            }
+            dates.add((Date)row[1]);
+            prevKm = km;
+        }
+
+        if (!dates.isEmpty()) {
+            entries.add(prevKm, dates);
+        }
+    }
+
+    public boolean load(Session session) {
+
+        loadData(session);
+
+        return true;
+    }
+
+
+    public void generateOverview(Document document) {
+        generateOverview(document, ACCEPT);
+    }
+
+    public KMIndex<List<Date>> filter(Filter f) {
+        // TODO: Apply filter
+        return entries;
+    }
+
+    public void generateOverview(
+        Document document,
+        Filter   filter
+    ) {
+        KMIndex<List<Date>> filtered = filter(ACCEPT);
+
+        Element sqElement = document.createElement("bed");
+
+        Element riverElement = document.createElement("river");
+
+        riverElement.setAttribute("name", riverName);
+
+        sqElement.appendChild(riverElement);
+
+        SimpleDateFormat df = new SimpleDateFormat(DATE_FORMAT);
+
+        Element kmE = document.createElement("km");
+
+        for (KMIndex.Entry<List<Date>> e: filtered) {
+
+            List<Date> dates = e.getValue();
+
+            if (!dates.isEmpty()) {
+                Element dEs = document.createElement("dates");
+
+                for (Date d: dates) {
+                    Element dE = document.createElement("date");
+
+                    dE.setAttribute("value", df.format(d));
+
+                    dEs.appendChild(dE);
+                }
+
+                kmE.appendChild(dEs);
+            }
+        }
+
+        sqElement.appendChild(kmE);
+
+        document.appendChild(sqElement);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedOverviewFactory.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,70 @@
+package org.dive4elements.river.artifacts.model.minfo;
+
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.Element;
+
+import org.apache.log4j.Logger;
+import org.hibernate.Session;
+
+import org.dive4elements.river.artifacts.cache.CacheFactory;
+import org.dive4elements.river.backend.SedDBSessionHolder;
+
+public class BedOverviewFactory {
+
+    private static Logger log = Logger.getLogger(BedOverviewFactory.class);
+
+    public static final String CACHE_NAME = "sq-overviews";
+
+    private BedOverviewFactory() {
+    }
+
+
+    public static BedOverview getOverview(String river) {
+
+        boolean debug = log.isDebugEnabled();
+
+        if (debug) {
+            log.debug(
+                "Looking for bed overview for river '" + river + "'");
+        }
+
+        Cache cache = CacheFactory.getCache(CACHE_NAME);
+
+        if (cache == null) {
+            if (debug) {
+                log.debug("Cache not configured.");
+            }
+            return getUncached(river);
+        }
+
+        String key = "bed-over-" + river;
+
+        Element element = cache.get(key);
+
+        if (element != null) {
+            if (debug) {
+                log.debug("Overview found in cache");
+            }
+            return (BedOverview)element.getValue();
+        }
+
+        BedOverview overview = getUncached(river);
+
+        if (overview != null) {
+            if (debug) {
+                log.debug("Store overview in cache.");
+            }
+            cache.put(new Element(key, overview));
+        }
+
+        return overview;
+    }
+
+    public static BedOverview getUncached(String river) {
+        BedOverview overview = new BedOverview(river);
+
+        Session session = SedDBSessionHolder.HOLDER.get();
+
+        return overview.load(session) ? overview : null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedParametersResult.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,106 @@
+package org.dive4elements.river.artifacts.model.minfo;
+
+import gnu.trove.TDoubleArrayList;
+
+import java.io.Serializable;
+
+
+public class BedParametersResult
+implements Serializable
+{
+    protected TDoubleArrayList porosityCap;
+    protected TDoubleArrayList porositySub;
+    protected TDoubleArrayList loadDensityCap;
+    protected TDoubleArrayList loadDensitySub;
+    protected TDoubleArrayList kms;
+
+    public BedParametersResult() {
+
+    }
+
+    public BedParametersResult(
+        TDoubleArrayList kms,
+        TDoubleArrayList porosityCap,
+        TDoubleArrayList porositySub,
+        TDoubleArrayList densityCap,
+        TDoubleArrayList densitySub
+    ) {
+        this.kms = kms;
+        this.porosityCap = porosityCap;
+        this.porositySub = porositySub;
+        this.loadDensityCap = densityCap;
+        this.loadDensitySub = densitySub;
+    }
+
+    public double getPorosityCap(int ndx) {
+        return porosityCap.get(ndx);
+    }
+
+    public double getPorositySub(int ndx) {
+        return porositySub.get(ndx);
+    }
+
+    public double getLoadDensityCap(int ndx) {
+        return loadDensityCap.get(ndx);
+    }
+
+    public double getLoadDensitySub(int ndx) {
+        return loadDensitySub.get(ndx);
+    }
+
+    public double getPorosityCap(double km) {
+        if (kms.indexOf(km) >= 0) {
+            return porosityCap.get(kms.indexOf(km));
+        }
+        return Double.NaN;
+    }
+
+    public double getPorositySub(double km) {
+        if (kms.indexOf(km) >= 0) {
+            return porositySub.get(kms.indexOf(km));
+        }
+        return Double.NaN;
+    }
+
+    public double getLoadDensityCap(double km) {
+        if (kms.indexOf(km) >= 0) {
+            return loadDensityCap.get(kms.indexOf(km));
+        }
+        return Double.NaN;
+    }
+
+    public double getLoadDensitySub(double km) {
+        if (kms.indexOf(km) >= 0) {
+            return loadDensitySub.get(kms.indexOf(km));
+        }
+        return Double.NaN;
+    }
+
+    public double[][] getPorosityCapData() {
+        return new double[][] {
+            kms.toNativeArray(),
+            porosityCap.toNativeArray()
+        };
+    }
+
+    public double[][] getPorositySubData() {
+        return new double[][] {
+            kms.toNativeArray(),
+            porositySub.toNativeArray()
+        };
+    }
+
+    public double[][] getDensityCapData() {
+        return new double[][] {
+            kms.toNativeArray(),
+            loadDensityCap.toNativeArray()
+        };
+    }
+
+    public double[][] getDensitySubData() {
+        return new double[][] {
+            kms.toNativeArray(),
+            loadDensitySub.toNativeArray()
+        };
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedPorosityFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,59 @@
+package org.dive4elements.river.artifacts.model.minfo;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.DataFacet;
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+
+/**
+ * Facet for serving bed porosity data.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class BedPorosityFacet extends DataFacet {
+
+    private static final long serialVersionUID = 1L;
+
+    private static Logger logger = Logger.getLogger(BedPorosityFacet.class);
+
+    public BedPorosityFacet() {
+    }
+
+    public BedPorosityFacet(int idx, String name, String description,
+        ComputeType type, String stateId, String hash) {
+        super(idx, name, description, type, hash, stateId);
+    }
+
+    public Object getData(Artifact artifact, CallContext context) {
+        logger.debug("Get data for bed porosity at index: " + index);
+
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        CalculationResult res = (CalculationResult) flys.compute(context, hash,
+            stateId, type, false);
+
+        int ndx = index >> 8;
+        Object[] data = ((BedQualityResult[]) res.getData())[ndx].getParameters(); // TODO CAST TO SPECIFIC CLASS
+
+        int ndy = index & 255;
+        return data != null && data.length > ndy ? data[ndy] : null;
+    }
+
+    /** Copy deeply. */
+    @Override
+    public Facet deepCopy() {
+        BedPorosityFacet copy = new BedPorosityFacet();
+        copy.set(this);
+        copy.type = type;
+        copy.hash = hash;
+        copy.stateId = stateId;
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedQualityCalculation.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,332 @@
+package org.dive4elements.river.artifacts.model.minfo;
+
+import gnu.trove.TDoubleArrayList;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.river.artifacts.access.BedQualityAccess;
+import org.dive4elements.river.artifacts.model.Calculation;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.DateRange;
+import org.dive4elements.river.backend.SedDBSessionHolder;
+
+
+public class BedQualityCalculation extends Calculation {
+
+    private static final Logger logger = Logger
+        .getLogger(BedQualityCalculation.class);
+
+    protected String river;
+    protected double from;
+    protected double to;
+    protected List<String> bedDiameter;
+    protected List<String> bedloadDiameter;
+    protected List<DateRange> ranges;
+
+    public BedQualityCalculation() {
+    }
+
+    public CalculationResult calculate(BedQualityAccess access) {
+        logger.info("BedQualityCalculation.calculate");
+
+        String river = access.getRiver();
+        Double from = access.getFrom();
+        Double to = access.getTo();
+        List<String> bedDiameter = access.getBedDiameter();
+        List<String> bedloadDiameter = access.getBedloadDiameter();
+        List<DateRange> ranges = access.getDateRanges();
+
+        if (river == null) {
+            // TODO: i18n
+            addProblem("minfo.missing.river");
+        }
+
+        if (from == null) {
+            // TODO: i18n
+            addProblem("minfo.missing.from");
+        }
+
+        if (to == null) {
+            // TODO: i18n
+            addProblem("minfo.missing.to");
+        }
+
+        if (ranges == null) {
+            // TODO: i18n
+            addProblem("minfo.missing.periods");
+        }
+
+        if (!hasProblems()) {
+            this.river = river;
+            this.from = from;
+            this.to = to;
+            this.ranges = ranges;
+            this.bedDiameter = bedDiameter;
+            this.bedloadDiameter = bedloadDiameter;
+
+            SedDBSessionHolder.acquire();
+            try {
+                return internalCalculate();
+            }
+            finally {
+                SedDBSessionHolder.release();
+            }
+        }
+
+        return new CalculationResult();
+    }
+
+    protected CalculationResult internalCalculate() {
+
+        List<BedQualityResult> results = new LinkedList<BedQualityResult>();
+        // Calculate for all time periods.
+        for (DateRange dr : ranges) {
+            QualityMeasurements loadMeasurements =
+                QualityMeasurementFactory.getBedloadMeasurements(
+                    river,
+                    from,
+                    to,
+                    dr.getFrom(),
+                    dr.getTo());
+            QualityMeasurements bedMeasurements =
+                QualityMeasurementFactory.getBedMeasurements(
+                    river,
+                    from,
+                    to,
+                    dr.getFrom(),
+                    dr.getTo());
+            BedQualityResult result = new BedQualityResult();
+            result.setDateRange(dr);
+            if (bedDiameter != null) {
+                result.add(calculateBedParameter(bedMeasurements, dr));
+                for (String bd : bedDiameter) {
+                    BedDiameterResult bedResult =
+                        calculateBed(bedMeasurements, bd, dr);
+
+                    // Avoid adding empty result sets.
+                    if (!bedResult.isEmpty()) {
+                        result.add(bedResult);
+                    }
+                }
+            }
+            if (bedloadDiameter != null) {
+                for (String bld : bedloadDiameter) {
+                    BedloadDiameterResult loadResult =
+                        calculateBedload(loadMeasurements, bld, dr);
+                    result.add(loadResult);
+                }
+            }
+            results.add(result);
+        }
+
+        return new CalculationResult(
+            results.toArray(new BedQualityResult[results.size()]), this);
+    }
+
+    private BedParametersResult calculateBedParameter(
+        QualityMeasurements qm,
+        DateRange dr
+    ) {
+        List<Double> kms = qm.getKms();
+        QualityMeasurements capFiltered = filterCapMeasurements(qm);
+        QualityMeasurements subFiltered = filterSubMeasurements(qm);
+        TDoubleArrayList location = new TDoubleArrayList();
+        TDoubleArrayList porosityCap = new TDoubleArrayList();
+        TDoubleArrayList porositySub = new TDoubleArrayList();
+        TDoubleArrayList densityCap = new TDoubleArrayList();
+        TDoubleArrayList densitySub = new TDoubleArrayList();
+
+        for(double km : kms) {
+            double[] pCap = calculatePorosity(capFiltered, km);
+            double[] pSub = calculatePorosity(subFiltered, km);
+            double[] dCap = calculateDensity(capFiltered, pCap);
+            double[] dSub = calculateDensity(subFiltered, pSub);
+
+            double pCapRes = 0d;
+            double pSubRes = 0d;
+            double dCapRes = 0d;
+            double dSubRes = 0d;
+            for (int i = 0; i < pCap.length; i++) {
+                pCapRes += pCap[i];
+                dCapRes += dCap[i];
+            }
+            for (int i = 0; i < pSub.length; i++) {
+                pSubRes += pSub[i];
+                dSubRes += dSub[i];
+            }
+            location.add(km);
+            porosityCap.add((pCapRes / pCap.length) * 100 );
+            porositySub.add((pSubRes / pSub.length) * 100);
+            densityCap.add((dCapRes / dCap.length) / 1000);
+            densitySub.add((dSubRes / dSub.length) / 1000);
+
+        }
+
+        return new BedParametersResult(
+            location,
+            porosityCap,
+            porositySub,
+            densityCap,
+            densitySub);
+    }
+
+    protected BedDiameterResult calculateBed(
+        QualityMeasurements qm,
+        String diameter,
+        DateRange range
+    ) {
+        List<Double> kms = qm.getKms();
+        TDoubleArrayList location = new TDoubleArrayList();
+        TDoubleArrayList avDiameterCap = new TDoubleArrayList();
+        TDoubleArrayList avDiameterSub = new TDoubleArrayList();
+        for (double km : kms) {
+            //Filter cap and sub measurements.
+            QualityMeasurements capFiltered = filterCapMeasurements(qm);
+            QualityMeasurements subFiltered = filterSubMeasurements(qm);
+
+            List<QualityMeasurement> cm = capFiltered.getMeasurements(km);
+            List<QualityMeasurement> sm = subFiltered.getMeasurements(km);
+
+            double avCap = calculateAverage(cm, diameter);
+            double avSub = calculateAverage(sm, diameter);
+            location.add(km);
+            avDiameterCap.add(avCap * 1000);// bring to mm.
+            avDiameterSub.add(avSub * 1000);
+        }
+        return new BedDiameterResult(
+            diameter,
+            avDiameterCap,
+            avDiameterSub,
+            location);
+    }
+
+    private double[] calculateDensity(
+        QualityMeasurements capFiltered,
+        double[] porosity
+    ) {
+        double[] density = new double[porosity.length];
+        for (int i = 0; i < porosity.length; i++) {
+            density[i] = (1 - porosity[i]) * 2650;
+        }
+        return density;
+    }
+
+    private double[] calculatePorosity(
+        QualityMeasurements capFiltered,
+        double km
+    ) {
+        List<QualityMeasurement> list = capFiltered.getMeasurements(km);
+        double[] results = new double[list.size()];
+        int i = 0;
+        for (QualityMeasurement qm : list) {
+            double deviation = calculateDeviation(qm);
+            double p = calculateP(qm);
+            double porosity = 0.353 - 0.068 * deviation + 0.146 * p;
+            results[i] = porosity;
+            i++;
+        }
+
+        return results;
+    }
+
+    protected BedloadDiameterResult calculateBedload(
+        QualityMeasurements qm,
+        String diameter,
+        DateRange range
+    ) {
+        List<Double> kms = qm.getKms();
+        TDoubleArrayList location = new TDoubleArrayList();
+        TDoubleArrayList avDiameter = new TDoubleArrayList();
+        for (double km : kms) {
+            List<QualityMeasurement> measurements = qm.getMeasurements(km);
+            double mid = calculateAverage(measurements, diameter);
+            location.add(km);
+            avDiameter.add(mid);
+        }
+        return new BedloadDiameterResult(
+            diameter,
+            avDiameter,
+            location,
+            range);
+    }
+
+    protected double calculateAverage(
+        List<QualityMeasurement> list,
+        String diameter
+    ) {
+        double av = 0;
+        for (QualityMeasurement qm : list) {
+            av += qm.getDiameter(diameter);
+        }
+        return av/list.size();
+    }
+
+    protected QualityMeasurements filterCapMeasurements(
+        QualityMeasurements qms
+    ) {
+        List<QualityMeasurement> result = new LinkedList<QualityMeasurement>();
+        for (QualityMeasurement qm : qms.getMeasurements()) {
+            if (qm.getDepth1() == 0d && qm.getDepth2() <= 0.3) {
+                result.add(qm);
+            }
+        }
+        return new QualityMeasurements(result);
+    }
+
+    protected QualityMeasurements filterSubMeasurements(
+        QualityMeasurements qms
+    ) {
+        List<QualityMeasurement> result = new LinkedList<QualityMeasurement>();
+        for (QualityMeasurement qm : qms.getMeasurements()) {
+            if (qm.getDepth1() > 0d && qm.getDepth2() <= 0.5) {
+                result.add(qm);
+            }
+        }
+        return new QualityMeasurements(result);
+    }
+
+    public double calculateDeviation(QualityMeasurement qm) {
+        Map<String, Double> dm = qm.getAllDiameter();
+        double phiM = 0;
+        double[] phis = new double[dm.size()];
+        double[] ps = new double[dm.size()];
+        int i = 0;
+        for (String key : dm.keySet()) {
+            double d = dm.get(key);
+            double phi = -Math.log(d)/Math.log(2);
+            phis[i] = phi;
+            double p = calculateWeight(qm, key);
+            ps[i] = p;
+            phiM += phi * p;
+            i++;
+        }
+
+        double sig = 0d;
+        for (i = 0; i < dm.size(); i++) {
+            sig += ps[i] * Math.exp(phis[i] - phiM);
+        }
+        double deviation = Math.sqrt(sig);
+        return deviation;
+    }
+
+    protected double calculateP(QualityMeasurement qm) {
+        return calculateWeight(qm, "dmin");
+    }
+
+    public double calculateWeight(QualityMeasurement qm, String diameter) {
+        Map<String, Double> dm = qm.getAllDiameter();
+        double value = qm.getDiameter(diameter);
+
+        double sum = 0d;
+        for (Double d : dm.values()) {
+            sum =+ d.doubleValue();
+        }
+        double weight = sum/100*value;
+        return weight;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedQualityDiameterResult.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,61 @@
+package org.dive4elements.river.artifacts.model.minfo;
+
+import gnu.trove.TDoubleArrayList;
+
+import java.io.Serializable;
+
+public class BedQualityDiameterResult implements Serializable {
+
+    public static enum DIAMETER_TYPE {
+        D90,
+        D84,
+        D80,
+        D75,
+        D70,
+        D60,
+        D50,
+        D40,
+        D30,
+        D25,
+        D20,
+        D16,
+        D10,
+        DMIN,
+        DMAX
+    }
+
+    protected DIAMETER_TYPE type;
+    protected TDoubleArrayList kms;
+    protected boolean empty;
+
+    public BedQualityDiameterResult () {
+        empty = true;
+    }
+
+    public BedQualityDiameterResult (
+        String type,
+        TDoubleArrayList km
+    ) {
+        if (km.size() > 0) {
+            empty = false;
+        }
+        this.type = DIAMETER_TYPE.valueOf(type.toUpperCase());
+        this.kms = km;
+    }
+
+    public DIAMETER_TYPE getType() {
+        return this.type;
+    }
+
+    public TDoubleArrayList getKms() {
+        return this.kms;
+    }
+
+    public void setType(DIAMETER_TYPE type) {
+        this.type = type;
+    }
+
+    public boolean isEmpty() {
+        return empty;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedQualityResult.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,69 @@
+package org.dive4elements.river.artifacts.model.minfo;
+
+import java.io.Serializable;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.dive4elements.river.artifacts.model.DateRange;
+
+public class BedQualityResult
+implements Serializable
+{
+
+    protected List<BedDiameterResult> bedResults;
+    protected List<BedloadDiameterResult> bedloadResults;
+    protected List<BedParametersResult> bedParameters;
+    protected DateRange dateRange;
+
+    public BedQualityResult () {
+        bedResults = new LinkedList<BedDiameterResult>();
+        bedloadResults = new LinkedList<BedloadDiameterResult>();
+        bedParameters = new LinkedList<BedParametersResult>();
+    };
+
+    public BedQualityResult (
+        List<BedDiameterResult> bedResults,
+        List<BedloadDiameterResult> bedloadResults,
+        List<BedParametersResult> bedParameters,
+        DateRange range
+    ) {
+        this.dateRange = range;
+        this.bedResults = bedResults;
+        this.bedloadResults = bedloadResults;
+        this.bedParameters = bedParameters;
+    }
+
+    public BedParametersResult[] getParameters() {
+        return bedParameters.toArray(
+            new BedParametersResult[bedParameters.size()]);
+    }
+
+    public BedDiameterResult[] getBedResults() {
+        return bedResults.toArray(new BedDiameterResult[bedResults.size()]);
+    }
+
+    public BedloadDiameterResult[] getBedloadResults() {
+        return bedloadResults.toArray(
+            new BedloadDiameterResult[bedloadResults.size()]);
+    }
+
+    public void add(BedloadDiameterResult result) {
+        bedloadResults.add(result);
+    }
+
+    public void add(BedDiameterResult result) {
+        bedResults.add(result);
+    }
+
+    public void add(BedParametersResult result) {
+        bedParameters.add(result);
+    }
+
+    public DateRange getDateRange() {
+        return dateRange;
+    }
+
+    public void setDateRange(DateRange range) {
+        this.dateRange = range;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedloadDiameterFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,61 @@
+package org.dive4elements.river.artifacts.model.minfo;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.DataFacet;
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+
+/**
+ * Facet for serving bedload diameter data.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class BedloadDiameterFacet extends DataFacet {
+
+    private static final long serialVersionUID = 1L;
+
+    private static Logger logger = Logger.getLogger(BedloadDiameterFacet.class);
+
+    public BedloadDiameterFacet() {
+        // required for clone operation deepCopy()
+    }
+
+    public BedloadDiameterFacet(int idx, String name, String description,
+        ComputeType type, String stateId, String hash) {
+        super(idx, name, description, type, hash, stateId);
+    }
+
+    public Object getData(Artifact artifact, CallContext context) {
+        logger.debug("Get data for bedload diameter at index: " + index);
+
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        CalculationResult res = (CalculationResult) flys.compute(context, hash,
+            stateId, type, false);
+
+        int ndx = index >> 8;
+        Object[] data =
+            ((BedQualityResult[]) res.getData())[ndx].getBedloadResults(); // TODO CAST TO SPECIFIC CLASS
+
+        int ndy = index & 255;
+        return data != null && data.length > ndy ? data[ndy] : null;
+    }
+
+    /** Copy deeply. */
+    @Override
+    public Facet deepCopy() {
+        BedloadDiameterFacet copy = new BedloadDiameterFacet();
+        copy.set(this);
+        copy.type = type;
+        copy.hash = hash;
+        copy.stateId = stateId;
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedloadDiameterResult.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,42 @@
+package org.dive4elements.river.artifacts.model.minfo;
+
+import org.dive4elements.river.artifacts.model.DateRange;
+import gnu.trove.TDoubleArrayList;
+
+
+public class BedloadDiameterResult
+extends BedQualityDiameterResult
+{
+    protected TDoubleArrayList diameter;
+
+    public BedloadDiameterResult(
+        String type,
+        TDoubleArrayList diameter,
+        TDoubleArrayList km,
+        DateRange range
+    ) {
+        super (type, km);
+        this.diameter = diameter;
+    }
+
+    public double getDiameter(int ndx) {
+        if (diameter != null) {
+            return this.diameter.get(ndx);
+        }
+        return Double.NaN;
+    }
+
+    public double getDiameter(double km) {
+        if (kms.indexOf(km) >= 0) {
+            return diameter.get(kms.indexOf(km));
+        }
+        return Double.NaN;
+    }
+
+    public double[][] getDiameterData() {
+        return new double[][] {
+            kms.toNativeArray(),
+            diameter.toNativeArray()
+        };
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedloadOverview.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,199 @@
+package org.dive4elements.river.artifacts.model.minfo;
+
+import java.io.Serializable;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+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.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import org.dive4elements.river.utils.KMIndex;
+
+public class BedloadOverview implements Serializable {
+
+    private static Logger log = Logger.getLogger(BedloadOverview.class);
+
+    /**
+     * Serial version UId.
+     */
+    private static final long serialVersionUID = -7607668985959407096L;
+
+    public interface Filter {
+        boolean accept(KMIndex<List<Date>> entry);
+
+    } // interface Filter
+
+
+    public static final Filter ACCEPT = new Filter() {
+        public boolean accept(KMIndex<List<Date>> entry) {
+            return true;
+        }
+    };
+
+    public static class KmFilter implements Filter {
+
+        protected double km;
+
+        public KmFilter (double km) {
+            this.km = km;
+        }
+        public boolean accept(KMIndex<List<Date>> list) {
+            for (KMIndex.Entry<List<Date>> e: list){
+                if (e.getKm() == km) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    };
+
+    public static class DateFilter implements Filter {
+
+        protected Date date;
+
+        public DateFilter (Date date) {
+            this.date = date;
+        }
+
+        public boolean accept(KMIndex<List<Date>> list) {
+            for (KMIndex.Entry<List<Date>> e: list){
+                if (e.getValue().equals(this.date)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    };
+
+    public static final double EPSILON = 1e-4;
+
+    public static final String DATE_FORMAT = "dd.MM.yyyy";
+
+    public static final String SQL_SQ =
+        "SELECT" +
+        "    m.km    AS km," +
+        "    m.datum AS datum " +
+        "FROM messung m " +
+        "    JOIN station s" +
+        "       ON m.stationid = s.stationid " +
+        "    JOIN gewaesser g " +
+        "       ON s.gewaesserid = g.gewaesserid " +
+        "WHERE" +
+        "    g.name = :name AND " +
+        "    m.km IS NOT NULL " +
+        "ORDER by" +
+        "    m.km, m.datum";
+
+    protected String       riverName;
+
+    protected KMIndex<List<Date>> entries;
+
+    public BedloadOverview() {
+        entries = new KMIndex<List<Date>>();
+    }
+
+    public BedloadOverview(String riverName) {
+        this();
+        this.riverName = riverName;
+    }
+
+    private static final boolean epsilonEquals(double a, double b) {
+        return Math.abs(a - b) < EPSILON;
+    }
+
+    protected void loadData(Session session) {
+        SQLQuery query = session.createSQLQuery(SQL_SQ)
+            .addScalar("km",    StandardBasicTypes.DOUBLE)
+            .addScalar("datum", StandardBasicTypes.DATE);
+
+        query.setString("name", riverName);
+
+        List<Object []> list = query.list();
+
+        if (list.isEmpty()) {
+            log.warn("No river '" + riverName + "' found.");
+        }
+
+        Double prevKm = -Double.MAX_VALUE;
+        List<Date> dates = new ArrayList<Date>();
+
+        for (Object [] row: list) {
+            Double km = (Double)row[0];
+            if (!epsilonEquals(km, prevKm) && !dates.isEmpty()) {
+                entries.add(prevKm, dates);
+                dates = new ArrayList<Date>();
+            }
+            dates.add((Date)row[1]);
+            prevKm = km;
+        }
+
+        if (!dates.isEmpty()) {
+            entries.add(prevKm, dates);
+        }
+    }
+
+    public boolean load(Session session) {
+
+        loadData(session);
+
+        return true;
+    }
+
+
+    public void generateOverview(Document document) {
+        generateOverview(document, ACCEPT);
+    }
+
+    public KMIndex<List<Date>> filter(Filter f) {
+        // TODO: Apply filter
+        return entries;
+    }
+
+    public void generateOverview(
+        Document document,
+        Filter   filter
+    ) {
+        KMIndex<List<Date>> filtered = filter(ACCEPT);
+
+        Element sqElement = document.createElement("bedload");
+
+        Element riverElement = document.createElement("river");
+
+        riverElement.setAttribute("name", riverName);
+
+        sqElement.appendChild(riverElement);
+
+        SimpleDateFormat df = new SimpleDateFormat(DATE_FORMAT);
+
+        Element kmE = document.createElement("km");
+
+        for (KMIndex.Entry<List<Date>> e: filtered) {
+
+            List<Date> dates = e.getValue();
+
+            if (!dates.isEmpty()) {
+                Element dEs = document.createElement("dates");
+
+                for (Date d: dates) {
+                    Element dE = document.createElement("date");
+
+                    dE.setAttribute("value", df.format(d));
+
+                    dEs.appendChild(dE);
+                }
+
+                kmE.appendChild(dEs);
+            }
+        }
+
+        sqElement.appendChild(kmE);
+
+        document.appendChild(sqElement);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedloadOverviewFactory.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,70 @@
+package org.dive4elements.river.artifacts.model.minfo;
+
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.Element;
+
+import org.apache.log4j.Logger;
+import org.hibernate.Session;
+
+import org.dive4elements.river.artifacts.cache.CacheFactory;
+import org.dive4elements.river.backend.SedDBSessionHolder;
+
+public class BedloadOverviewFactory {
+
+    private static Logger log = Logger.getLogger(BedloadOverviewFactory.class);
+
+    public static final String CACHE_NAME = "sq-overviews";
+
+    private BedloadOverviewFactory() {
+    }
+
+
+    public static BedloadOverview getOverview(String river) {
+
+        boolean debug = log.isDebugEnabled();
+
+        if (debug) {
+            log.debug(
+                "Looking for bedload overview for river '" + river + "'");
+        }
+
+        Cache cache = CacheFactory.getCache(CACHE_NAME);
+
+        if (cache == null) {
+            if (debug) {
+                log.debug("Cache not configured.");
+            }
+            return getUncached(river);
+        }
+
+        String key = "bedload-over-" + river;
+
+        Element element = cache.get(key);
+
+        if (element != null) {
+            if (debug) {
+                log.debug("Overview found in cache");
+            }
+            return (BedloadOverview)element.getValue();
+        }
+
+        BedloadOverview overview = getUncached(river);
+
+        if (overview != null) {
+            if (debug) {
+                log.debug("Store overview in cache.");
+            }
+            cache.put(new Element(key, overview));
+        }
+
+        return overview;
+    }
+
+    public static BedloadOverview getUncached(String river) {
+        BedloadOverview overview = new BedloadOverview(river);
+
+        Session session = SedDBSessionHolder.HOLDER.get();
+
+        return overview.load(session) ? overview : null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/FlowVelocityMeasurementFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,54 @@
+package org.dive4elements.river.artifacts.model.minfo;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.FlowVelocityMeasurementArtifact;
+import org.dive4elements.river.artifacts.model.BlackboardDataFacet;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+
+
+/** Facet to show measured flow velocity. */
+public class FlowVelocityMeasurementFacet
+extends      BlackboardDataFacet
+implements   FacetTypes {
+
+    public FlowVelocityMeasurementFacet(String description) {
+        this(FLOW_VELOCITY_MEASUREMENT, description);
+    }
+
+
+    public FlowVelocityMeasurementFacet(String name, String description) {
+        this.name = name;
+        this.description = description;
+        this.index = 0;
+    }
+
+
+    /**
+     * Returns the data this facet requires.
+     *
+     * @param artifact the owner artifact.
+     * @param context  the CallContext (ignored).
+     *
+     * @return the data.
+     */
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        FlowVelocityMeasurementArtifact staticData =
+            (FlowVelocityMeasurementArtifact) artifact;
+        return staticData.getFlowVelocityMeasurementValue();
+    }
+
+
+    /**
+     * Create a deep copy of this Facet.
+     * @return a deep copy.
+     */
+    @Override
+    public FlowVelocityMeasurementFacet deepCopy() {
+        FlowVelocityMeasurementFacet copy = new FlowVelocityMeasurementFacet(description);
+        copy.set(this);
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/FlowVelocityMeasurementFactory.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,53 @@
+package org.dive4elements.river.artifacts.model.minfo;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.hibernate.SQLQuery;
+import org.hibernate.Session;
+
+import org.dive4elements.river.model.FlowVelocityMeasurementValue;
+import org.dive4elements.river.backend.SessionHolder;
+
+
+public class FlowVelocityMeasurementFactory
+{
+    /** Private logger to use here. */
+    private static Logger log = Logger.getLogger(FlowVelocityMeasurementFactory.class);
+
+    /** Query to get description and start year, given name and a km range. */
+    public static final String SQL_SELECT_ONE =
+        "SELECT station, datetime, w, q, v, description " +
+        "   FROM flow_velocity_measure_values" +
+        "   WHERE id = :id";
+
+
+    private FlowVelocityMeasurementFactory() {
+    }
+
+
+    public static FlowVelocityMeasurementValue.FastFlowVelocityMeasurementValue
+        getFlowVelocityMeasurement(int id)
+    {
+        log.debug("FlowVelocityMeasurementFactory.getFlowVelocityMeasurementValue");
+        Session session = SessionHolder.HOLDER.get();
+        SQLQuery sqlQuery = null;
+        sqlQuery = session.createSQLQuery(SQL_SELECT_ONE);
+        sqlQuery.setParameter("id", id);
+
+        List<Object []> results = sqlQuery.list();
+        if (results.size() > 0) {
+            Object[] row = results.get(0);
+            if (row == null || row.length < 6) {
+                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());
+        }
+        return null;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/MorphologicWidth.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,48 @@
+package org.dive4elements.river.artifacts.model.minfo;
+
+import java.util.HashMap;
+import java.util.Map;
+
+
+public class MorphologicWidth
+{
+
+    private Map<Double, Double> pairs;
+
+
+    public MorphologicWidth() {
+        pairs = new HashMap<Double, Double>();
+    }
+
+    public void add(double station, double width) {
+        this.pairs.put(station, width);
+    }
+
+    public Map<Double, Double> getAll() {
+        return this.pairs;
+    }
+
+    public double[][] getAsArray() {
+        double [][] array = new double[2][pairs.size()];
+        Double[] kms = pairs.keySet().toArray(new Double[pairs.size()]);
+        Double[] width = pairs.values().toArray(new Double[pairs.size()]);
+        int realIndex = 0;
+        for (int i = 0; i < kms.length; i++) {
+            if (kms[i] == null || width[i] == null) {
+                continue;
+            }
+            array[0][realIndex] = kms[i];
+            array[1][realIndex] = width[i];
+            realIndex++;
+        }
+        return array;
+    }
+
+
+    public Double getWidth(double station) {
+        if (this.pairs.containsKey(station)) {
+            return this.pairs.get(station);
+        }
+        return null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/MorphologicWidthFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,54 @@
+package org.dive4elements.river.artifacts.model.minfo;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.model.DataFacet;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+
+public class MorphologicWidthFacet
+extends DataFacet
+implements   FacetTypes
+{
+    private static Logger logger = Logger.getLogger(SedimentLoadFacet.class);
+
+
+    public MorphologicWidthFacet() {
+    }
+
+    public MorphologicWidthFacet(String facetName, String description) {
+        super(facetName, description);
+    }
+
+    public MorphologicWidthFacet(int idx, String name, String description,
+        ComputeType type, String stateId, String hash) {
+        super(idx, name, description, type, hash, stateId);
+    }
+
+    public Object getData(Artifact artifact, CallContext context) {
+
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+        String width_id = flys.getDataAsString("width_id");
+
+        MorphologicWidth width =
+            MorphologicWidthFactory.getWidth(Integer.valueOf(width_id));
+
+        return width;
+    }
+
+    /** Copy deeply. */
+    @Override
+    public Facet deepCopy() {
+        MorphologicWidthFacet copy = new MorphologicWidthFacet();
+        copy.set(this);
+        copy.type = type;
+        copy.hash = hash;
+        copy.stateId = stateId;
+        return copy;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/MorphologicWidthFactory.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,87 @@
+package org.dive4elements.river.artifacts.model.minfo;
+
+import java.util.List;
+
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.Element;
+
+import org.apache.log4j.Logger;
+import org.hibernate.SQLQuery;
+import org.hibernate.Session;
+import org.hibernate.type.StandardBasicTypes;
+
+import org.dive4elements.river.artifacts.cache.CacheFactory;
+import org.dive4elements.river.artifacts.model.StaticMorphoWidthCacheKey;
+import org.dive4elements.river.backend.SessionHolder;
+
+
+public class MorphologicWidthFactory
+{
+    /** Private logger to use here. */
+    private static Logger log = Logger.getLogger(MorphologicWidthFactory.class);
+
+    public static final String SQL_SELECT =
+        "SELECT mwv.station AS station, mwv.width AS width " +
+        "   FROM morphologic_width mw" +
+        "       JOIN morphologic_width_values mwv on mwv.morphologic_width_id = mw.id" +
+        "   WHERE mw.id = :width_id";
+
+    private MorphologicWidthFactory() {
+    }
+
+
+    /**
+     * Get WKms for given column and wst_id, caring about the cache.
+     */
+    public static MorphologicWidth getWidth(int width_id) {
+        log.debug("MorphologicWidthFactory.getWidth");
+        Cache cache = CacheFactory.getCache(StaticMorphoWidthCacheKey.CACHE_NAME);
+
+        StaticMorphoWidthCacheKey cacheKey;
+
+        if (cache != null) {
+            cacheKey = new StaticMorphoWidthCacheKey(width_id);
+            Element element = cache.get(cacheKey);
+            if (element != null) {
+                log.debug("Got static bedheight values from cache");
+                return (MorphologicWidth)element.getValue();
+            }
+        }
+        else {
+            cacheKey = null;
+        }
+
+        MorphologicWidth values = getWidthUncached(width_id);
+
+        if (values != null && cacheKey != null) {
+            log.debug("Store static morphologic width values in cache.");
+            Element element = new Element(cacheKey, values);
+            cache.put(element);
+        }
+        return values;
+    }
+
+    private static MorphologicWidth getWidthUncached(int width_id) {
+        if (log.isDebugEnabled()) {
+            log.debug("MorphologicWidthFactory.getWidthUncached");
+        }
+
+        Session session = SessionHolder.HOLDER.get();
+        SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT)
+                .addScalar("station", StandardBasicTypes.DOUBLE)
+                .addScalar("width", StandardBasicTypes.DOUBLE);
+        sqlQuery.setInteger("width_id", width_id);
+        List<Object []> results = sqlQuery.list();
+
+        MorphologicWidth widths = new MorphologicWidth();
+        for (int i = 0; i < results.size(); i++) {
+            Object[] row = results.get(i);
+            log.debug("got station: " + (Double)row[0]);
+            widths.add(
+                (Double) row[0],
+                (Double) row[1]);
+        }
+        return widths;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/QualityMeasurement.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,79 @@
+package org.dive4elements.river.artifacts.model.minfo;
+
+import java.util.Date;
+import java.util.Map;
+
+
+public class QualityMeasurement {
+
+    private double              km;
+    private Date                date;
+    private double              depth1;
+    private double              depth2;
+    private Map<String, Double> charDiameter;
+
+    public QualityMeasurement() {
+
+    }
+
+    public QualityMeasurement(
+        double km,
+        Date date,
+        double depth1,
+        double depth2,
+        Map<String, Double> diameter) {
+        this.setKm(km);
+        this.setDate(date);
+        this.depth1 = depth1;
+        this.depth2 = depth2;
+        this.setDiameter(diameter);
+    }
+
+    public double getKm() {
+        return km;
+    }
+
+    public void setKm(double km) {
+        this.km = km;
+    }
+
+    public Date getDate() {
+        return date;
+    }
+
+    public void setDate(Date date) {
+        this.date = date;
+    }
+
+    public Map<String, Double> getAllDiameter() {
+        return charDiameter;
+    }
+
+    public void setDiameter(Map<String, Double> charDiameter) {
+        this.charDiameter = charDiameter;
+    }
+
+    public double getDiameter(String key) {
+        return charDiameter.get(key);
+    }
+
+    public void setDiameter(String key, double value) {
+        charDiameter.put(key, value);
+    }
+
+    public double getDepth1() {
+        return depth1;
+    }
+
+    public void setDepth1(double depth1) {
+        this.depth1 = depth1;
+    }
+
+    public double getDepth2() {
+        return depth2;
+    }
+
+    public void setDepth2(double depth2) {
+        this.depth2 = depth2;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/QualityMeasurementFactory.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,204 @@
+package org.dive4elements.river.artifacts.model.minfo;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+import org.hibernate.SQLQuery;
+import org.hibernate.Session;
+import org.hibernate.transform.BasicTransformerAdapter;
+import org.hibernate.type.StandardBasicTypes;
+
+import org.dive4elements.river.backend.SedDBSessionHolder;
+
+
+public class QualityMeasurementFactory {
+
+    private static Logger logger = Logger.getLogger(QualityMeasurementFactory.class);
+
+    private static final String SQL_BED_MEASUREMENT =
+        "SELECT st.km       as km," +
+        "       st.datum    as datum," +
+        "       sp.tiefevon as depth1," +
+        "       sp.tiefebis as depth2," +
+        "       sa.d10      as d10," +
+        "       sa.d16      as d16," +
+        "       sa.d20      as d20," +
+        "       sa.d25      as d25," +
+        "       sa.d30      as d30," +
+        "       sa.d40      as d40," +
+        "       sa.d50      as d50," +
+        "       sa.d60      as d60," +
+        "       sa.d70      as d70," +
+        "       sa.d75      as d75," +
+        "       sa.d80      as d80," +
+        "       sa.d84      as d84," +
+        "       sa.d90      as d90," +
+        "       sa.dmin     as dmin," +
+        "       sa.dmax     as dmax " +
+        "FROM sohltest st " +
+        "    JOIN station sn ON sn.stationid = st.stationid " +
+        "    JOIN gewaesser gw ON gw.gewaesserid = sn.gewaesserid " +
+        "    JOIN sohlprobe sp ON sp.sohltestid = st.sohltestid " +
+        "    JOIN siebanalyse sa ON sa.sohlprobeid = sp.sohlprobeid " +
+        "WHERE gw.name = :name AND " +
+        "      st.km IS NOT NULL AND " +
+        "      sp.tiefevon IS NOT NULL AND " +
+        "      sp.tiefebis IS NOT NULL AND " + // TODO: Test if char diameter ist null.
+        "      st.km BETWEEN :from - 0.001 AND :to + 0.001 AND " +
+        "      st.datum BETWEEN :start AND :end";
+
+    private static final String SQL_BEDLOAD_MEASUREMENT =
+        "SELECT m.km    as km," +
+        "       m.datum as datum," +
+        "       m.d10   as d10," +
+        "       m.d16   as d16," +
+        "       m.d20   as d20," +
+        "       m.d25   as d25," +
+        "       m.d30   as d30," +
+        "       m.d40   as d40," +
+        "       m.d50   as d50," +
+        "       m.d60   as d60," +
+        "       m.d70   as d70," +
+        "       m.d75   as d75," +
+        "       m.d80   as d80," +
+        "       m.d84   as d84," +
+        "       m.d90   as d90," +
+        "       m.dmin  as dmin," +
+        "       m.dmax  as dmax " +
+        "FROM messung m" +
+        "    JOIN station sn ON sn.stationid = m.stationid" +
+        "    JOIN gewaesser gw ON gw.gewaesserid = sn.gewaesserid " +
+        "WHERE gw.name = :name AND " +
+        "      m.km IS NOT NULL AND " +
+        "      m.d10 IS NOT NULL AND" + //TODO: Add all other char. diameter.
+        "      m.km BETWEEN :from - 0.001 AND :to + 0.001 AND" +
+        "      m.datum BETWEEN :start AND :end";
+
+    public static final class QualityMeasurementResultTransformer
+    extends BasicTransformerAdapter {
+
+        public static QualityMeasurementResultTransformer INSTANCE = new QualityMeasurementResultTransformer();
+
+        public QualityMeasurementResultTransformer() {
+        }
+
+        @Override
+        public Object transformTuple(Object[] tuple, String[] aliases) {
+            Map<String, Double> map = new HashMap<String, Double>();
+            double km = 0;
+            Date d = null;
+            double depth1 = Double.NaN;
+            double depth2 = Double.NaN;
+            for (int i = 0; i < tuple.length; ++i) {
+                if (tuple[i] != null) {
+                    if (aliases[i].equals("km")) {
+                        km = ((Number) tuple[i]).doubleValue();
+                    }
+                    else if (aliases[i].equals("datum")) {
+                        d = (Date) tuple[i];
+                    }
+                    else if (aliases[i].equals("depth1")) {
+                        depth1 = ((Number) tuple[i]).doubleValue();
+                    }
+                    else if (aliases[i].equals("depth2")) {
+                        depth2 = ((Number) tuple[i]).doubleValue();
+                    }
+                    else {
+                        map.put(aliases[i], ((Double) tuple[i])/1000);
+                    }
+                }
+            }
+            return new QualityMeasurement(km, d, depth1, depth2, map);
+        }
+    } // class BasicTransformerAdapter
+
+    private QualityMeasurementFactory() {
+    }
+
+    protected static QualityMeasurements load(
+        Session session,
+        String river,
+        double from,
+        double to,
+        Date start,
+        Date end,
+        String statement
+    ) {
+        SQLQuery query = session.createSQLQuery(statement)
+            .addScalar("km", StandardBasicTypes.DOUBLE)
+            .addScalar("datum", StandardBasicTypes.DATE)
+            .addScalar("d10", StandardBasicTypes.DOUBLE)
+            .addScalar("d16", StandardBasicTypes.DOUBLE)
+            .addScalar("d20", StandardBasicTypes.DOUBLE)
+            .addScalar("d25", StandardBasicTypes.DOUBLE)
+            .addScalar("d30", StandardBasicTypes.DOUBLE)
+            .addScalar("d40", StandardBasicTypes.DOUBLE)
+            .addScalar("d50", StandardBasicTypes.DOUBLE)
+            .addScalar("d60", StandardBasicTypes.DOUBLE)
+            .addScalar("d70", StandardBasicTypes.DOUBLE)
+            .addScalar("d75", StandardBasicTypes.DOUBLE)
+            .addScalar("d80", StandardBasicTypes.DOUBLE)
+            .addScalar("d84", StandardBasicTypes.DOUBLE)
+            .addScalar("d90", StandardBasicTypes.DOUBLE)
+            .addScalar("dmin", StandardBasicTypes.DOUBLE)
+            .addScalar("dmax", StandardBasicTypes.DOUBLE);
+
+        if (statement.equals(SQL_BED_MEASUREMENT)) {
+            query.addScalar("depth1", StandardBasicTypes.DOUBLE);
+            query.addScalar("depth2", StandardBasicTypes.DOUBLE);
+        }
+
+        query.setString("name", river);
+        query.setDouble("from", from);
+        query.setDouble("to", to);
+        query.setDate("start", start);
+        query.setDate("end", end);
+
+        query.setResultTransformer(
+            QualityMeasurementResultTransformer.INSTANCE);
+
+        return new QualityMeasurements(query.list());
+    }
+
+    public static QualityMeasurements getBedMeasurements(
+        String river,
+        double from,
+        double to,
+        Date start,
+        Date end) {
+        Session session = SedDBSessionHolder.HOLDER.get();
+        try {
+            return load(session, river, from, to, start, end,
+            SQL_BED_MEASUREMENT);
+        }
+        finally {
+            //session.close();
+        }
+    }
+
+    public static QualityMeasurements getBedloadMeasurements(
+        String river,
+        double from,
+        double to,
+        Date start,
+        Date end
+    ) {
+        Session session = SedDBSessionHolder.HOLDER.get();
+        try {
+            return load(
+                session,
+                river,
+                from,
+                to,
+                start,
+                end,
+                SQL_BEDLOAD_MEASUREMENT);
+        }
+        finally {
+            //session.close();
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/QualityMeasurements.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,50 @@
+package org.dive4elements.river.artifacts.model.minfo;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+public class QualityMeasurements {
+    private static Logger logger = Logger.getLogger(QualityMeasurements.class);
+    private List<QualityMeasurement> measurements;
+
+    public QualityMeasurements() {
+    }
+
+    public QualityMeasurements(List<QualityMeasurement> list) {
+        measurements = list;
+    }
+
+    public List<QualityMeasurement> getMeasurements() {
+        return measurements;
+    }
+
+    public List<QualityMeasurement> getMeasurements(double km) {
+        List<QualityMeasurement> res = new LinkedList<QualityMeasurement>();
+        for (QualityMeasurement qm: measurements) {
+            if (qm.getKm() == km) {
+                res.add(qm);
+            }
+        }
+        return res;
+    }
+
+    public List<Double> getKms() {
+        List<Double> result = new LinkedList<Double>();
+        for (QualityMeasurement qm : measurements) {
+            if (result.indexOf(qm.getKm()) < 0) {
+                result.add(qm.getKm());
+            }
+        }
+        return result;
+    }
+
+    public void setMeasurements(List<QualityMeasurement> list) {
+        this.measurements = list;
+    }
+
+    public void addMeasurement(QualityMeasurement qm) {
+        this.measurements.add(qm);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentDensity.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,158 @@
+package org.dive4elements.river.artifacts.model.minfo;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.log4j.Logger;
+
+
+public class SedimentDensity
+{
+    private static final Logger logger = Logger
+        .getLogger(SedimentDensity.class);
+
+    private Map<Integer, List<SedimentDensityValue>> densities;
+    private List<Integer> years;
+
+    public SedimentDensity() {
+        this.densities = new HashMap<Integer, List<SedimentDensityValue>>();
+        this.years = new ArrayList<Integer>();
+    }
+
+    public Map<Integer, List<SedimentDensityValue>> getDensities() {
+        return densities;
+    }
+
+    public void setDensities(Map<Integer, List<SedimentDensityValue>> densities) {
+        this.densities = densities;
+    }
+
+    public void addDensity(double km, double density, int year) {
+        logger.debug("adding " + year);
+        if (this.densities.containsKey(year)) {
+            List<SedimentDensityValue> list = this.densities.get(year);
+            list.add(new SedimentDensityValue(km, density, year));
+        }
+        else {
+            List<SedimentDensityValue> list =
+                new ArrayList<SedimentDensityValue>();
+            list.add(new SedimentDensityValue(km, density, year));
+            densities.put(year, list);
+        }
+        if (!this.years.contains(new Integer(year))) {
+            logger.debug("new year");
+            years.add(new Integer(year));
+        }
+    }
+
+    public List<Integer> getYears() {
+        return years;
+    }
+
+    public void setYears(List<Integer> years) {
+        this.years = years;
+    }
+
+    public double getDensity(double km, int year) {
+        Collections.sort(this.years);
+        if (this.years.size() == 1) {
+            return getDensityAtKm(densities.get(year), km);
+        }
+        else {
+            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) {
+                    return getDensityAtKm(densities.get(y1), km);
+                }
+                else if (i == years.size() -1) {
+                    continue;
+                }
+                else {
+                    return getDensityAtKm(densities.get(y2), km);
+                }
+            }
+        }
+        return 1.8d;
+    }
+
+    private double getDensityAtKm(
+        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;
+            }
+            if (sdv.getKm() > km) {
+                next = sdv;
+                break;
+            }
+            prev = sdv;
+        }
+        if (found) {
+            return prev.getDensity();
+        }
+        else {
+            return spline(prev, next, km);
+        }
+    }
+
+    private double spline(
+        SedimentDensityValue prev,
+        SedimentDensityValue next,
+        double km
+    ) {
+        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;
+    }
+
+    public void cleanUp() {
+        Set<Integer> keys = densities.keySet();
+        for (Integer key : keys) {
+            List<SedimentDensityValue> list = densities.get(key);
+            if (list.size() == 0) {
+                return;
+            }
+            List<SedimentDensityValue> cleaned =
+                new ArrayList<SedimentDensityValue>();
+            double prevkm = list.get(0).getKm();
+            int counter = 0;
+            double sum = 0d;
+            for (SedimentDensityValue value : list) {
+                if (value.getKm() == prevkm) {
+                    sum += value.getDensity();
+                    counter++;
+                }
+                else {
+                    cleaned.add(new SedimentDensityValue(
+                        prevkm,
+                        sum / counter,
+                        value.getYear()));
+                    sum = value.getDensity();
+                    counter = 1;
+                }
+                prevkm = value.getKm();
+            }
+            this.densities.put(key, cleaned);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentDensityFactory.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,92 @@
+package org.dive4elements.river.artifacts.model.minfo;
+
+import java.util.List;
+
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.Element;
+
+import org.apache.log4j.Logger;
+import org.hibernate.SQLQuery;
+import org.hibernate.Session;
+import org.hibernate.type.StandardBasicTypes;
+
+import org.dive4elements.river.artifacts.cache.CacheFactory;
+import org.dive4elements.river.backend.SessionHolder;
+
+
+public class SedimentDensityFactory
+{
+    /** Private logger to use here. */
+    private static Logger log = Logger.getLogger(SedimentDensityFactory.class);
+
+    private static final String DENSITY_CACHE_NAME = "sedimentdensity";
+
+    /**Query to get sediment density values and kms. */
+    private static final String SQL_SELECT_DENSITY =
+        "SELECT sdv.station AS km, " +
+        "       sdv.density AS density," +
+        "       sdv.year AS year " +
+        "   FROM sediment_density sd" +
+        "       JOIN rivers r ON sd.river_id = r.id " +
+        "       JOIN sediment_density_values sdv ON sd.id = sdv.sediment_density_id" +
+        "   WHERE r.name = :name";
+
+    private SedimentDensityFactory() {}
+
+    public static SedimentDensity getSedimentDensity(
+        String river,
+        double startKm,
+        double endKm,
+        int year
+    ) {
+        log.debug("getSedimentDensity");
+        Cache cache = CacheFactory.getCache(DENSITY_CACHE_NAME);
+
+        if (cache == null) {
+            log.debug("Cache not configured.");
+            return getSedimentDensityUncached(river, startKm, endKm, year);
+        }
+
+        String key = river + startKm + endKm;
+        Element element = cache.get(key);
+        if (element != null) {
+            log.debug("SedimentDensity found in cache!");
+            return (SedimentDensity)element.getValue();
+        }
+        SedimentDensity value =
+            getSedimentDensityUncached(river, startKm, endKm, year);
+
+        if (value != null && key != null) {
+            log.debug("Store sediment density values in cache.");
+            element = new Element(key, value);
+            cache.put(element);
+        }
+        return value;
+    }
+
+    private static SedimentDensity getSedimentDensityUncached(
+        String river,
+        double startKm,
+        double endKm,
+        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();
+        SedimentDensity density = new SedimentDensity();
+        for (Object[] row : results) {
+            if (row[0] != null && row[1] != null && row[2] != null) {
+                density.addDensity((Double)row[0], (Double)row[1], (Integer)row[2]);
+            }
+        }
+
+        density.cleanUp();
+        return density;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentDensityValue.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,46 @@
+package org.dive4elements.river.artifacts.model.minfo;
+
+
+public class SedimentDensityValue
+{
+
+    private double km;
+    private double density;
+    private int year;
+
+    public SedimentDensityValue() {
+        this.km = 0d;
+        this.density = 0d;
+        this.year = 0;
+    }
+
+    public SedimentDensityValue(double km, double density, int year) {
+        this.km = km;
+        this.density = density;
+        this.year = year;
+    }
+
+    public double getKm() {
+        return km;
+    }
+
+    public void setKm(double km) {
+        this.km = km;
+    }
+
+    public double getDensity() {
+        return density;
+    }
+
+    public void setDensity(double density) {
+        this.density = density;
+    }
+
+    public int getYear() {
+        return year;
+    }
+
+    public void setYear(int year) {
+        this.year = year;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentLoad.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,226 @@
+package org.dive4elements.river.artifacts.model.minfo;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Set;
+
+import org.dive4elements.river.artifacts.model.NamedObjectImpl;
+
+/** Gives access to Fractions (at kms). */
+public class SedimentLoad
+extends NamedObjectImpl
+{
+    protected String description;
+    protected Date start;
+    protected Date end;
+    protected boolean isEpoch;
+
+    protected HashMap<Double, SedimentLoadFraction> kms;
+
+    public SedimentLoad() {
+        kms = new HashMap<Double, SedimentLoadFraction>();
+    }
+
+    public SedimentLoad(
+        String description,
+        Date start,
+        Date end,
+        boolean isEpoch
+    ) {
+        this();
+        this.description = description;
+        this.start = start;
+        this.end = end;
+        this.isEpoch = isEpoch;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public Date getStart() {
+        return start;
+    }
+
+    public void setStart(Date start) {
+        this.start = start;
+    }
+
+    public Date getEnd() {
+        return end;
+    }
+
+    public void setEnd(Date end) {
+        this.end = end;
+    }
+
+    public boolean isEpoch() {
+        return isEpoch;
+    }
+
+    public void setEpoch(boolean isEpoch) {
+        this.isEpoch = isEpoch;
+    }
+
+    public Set<Double> getKms() {
+        return kms.keySet();
+    }
+
+    public void addKm(double km, SedimentLoadFraction fraction) {
+        kms.put(km, fraction);
+    }
+
+    public SedimentLoadFraction getFraction(double km) {
+        if (kms.get(km) == null) {
+            return new SedimentLoadFraction();
+        }
+        return kms.get(km);
+    }
+
+    public void setCoarse(double km, double coarse) {
+        if (kms.containsKey(km)) {
+            kms.get(km).setCoarse(coarse);
+        }
+        else {
+            SedimentLoadFraction f = new SedimentLoadFraction();
+            f.setCoarse(coarse);
+            kms.put(km, f);
+        }
+    }
+
+    public void setFineMiddle(double km, double fine_middle) {
+        if (kms.containsKey(km)) {
+            kms.get(km).setFine_middle(fine_middle);
+        }
+        else {
+            SedimentLoadFraction f = new SedimentLoadFraction();
+            f.setFine_middle(fine_middle);
+            kms.put(km, f);
+        }
+    }
+
+    public void setSand(double km, double sand) {
+        if (kms.containsKey(km)) {
+            kms.get(km).setSand(sand);
+        }
+        else {
+            SedimentLoadFraction f = new SedimentLoadFraction();
+            f.setSand(sand);
+            kms.put(km, f);
+        }
+    }
+
+    public void setSuspSand(double km, double susp_sand) {
+        if (kms.containsKey(km)) {
+            kms.get(km).setSusp_sand(susp_sand);
+        }
+        else {
+            SedimentLoadFraction f = new SedimentLoadFraction();
+            f.setSusp_sand(susp_sand);
+            kms.put(km, f);
+        }
+    }
+
+    public void setSuspSandBed(double km, double susp_sand_bed) {
+        if (kms.containsKey(km)) {
+            kms.get(km).setSusp_sand_bed(susp_sand_bed);
+        }
+        else {
+            SedimentLoadFraction f = new SedimentLoadFraction();
+            f.setSusp_sand_bed(susp_sand_bed);
+            kms.put(km, f);
+        }
+    }
+
+    public void setSuspSediment(double km, double susp_sediment) {
+        if (kms.containsKey(km)) {
+            kms.get(km).setSusp_sediment(susp_sediment);
+        }
+        else {
+            SedimentLoadFraction f = new SedimentLoadFraction();
+            f.setSusp_sediment(susp_sediment);
+            kms.put(km, f);
+        }
+    }
+
+    public void setLoadTotal(double km, double total) {
+        if (kms.containsKey(km)) {
+            kms.get(km).setLoadTotal(total);
+        }
+        else {
+            SedimentLoadFraction f = new SedimentLoadFraction();
+            f.setLoadTotal(total);
+            kms.put(km, f);
+        }
+    }
+
+    public void setTotal(double km, double total) {
+        if (kms.containsKey(km)) {
+            kms.get(km).setTotal(total);
+        }
+        else {
+            SedimentLoadFraction f = new SedimentLoadFraction();
+            f.setTotal(total);
+            kms.put(km, f);
+        }
+    }
+
+    public boolean hasCoarse() {
+        for (SedimentLoadFraction slf : kms.values()) {
+            if (slf.getCoarse() > 0d) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public boolean hasFineMiddle() {
+        for (SedimentLoadFraction slf : kms.values()) {
+            if (slf.getFine_middle() > 0d) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public boolean hasSand() {
+        for (SedimentLoadFraction slf : kms.values()) {
+            if (slf.getSand() > 0d) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public boolean hasSuspSand() {
+        for (SedimentLoadFraction slf : kms.values()) {
+            if (slf.getSusp_sand() > 0d) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public boolean hasSuspSediment() {
+        for (SedimentLoadFraction slf : kms.values()) {
+            if (slf.getSusp_sediment() > 0d) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public boolean hasTotalLoad() {
+        for (SedimentLoadFraction slf : kms.values()) {
+            if (slf.getLoadTotal() > 0d) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentLoadCalculation.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,343 @@
+package org.dive4elements.river.artifacts.model.minfo;
+
+import gnu.trove.TDoubleArrayList;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.river.artifacts.access.SedimentLoadAccess;
+import org.dive4elements.river.artifacts.model.Calculation;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+
+
+/** Calculate sediment load. */
+public class SedimentLoadCalculation
+extends Calculation
+{
+
+    /** Private logger. */
+    private static final Logger logger = Logger
+        .getLogger(SedimentLoadCalculation.class);
+
+    protected String river;
+    protected String yearEpoch;
+    protected double kmUp;
+    protected double kmLow;
+    protected int[] period;
+    protected int[][] epoch;
+    protected String unit;
+
+    public SedimentLoadCalculation() {
+    }
+
+    public CalculationResult calculate(SedimentLoadAccess access) {
+        logger.info("SedimentLoadCalculation.calculate");
+
+        String river = access.getRiver();
+        String yearEpoch = access.getYearEpoch();
+        String unit = access.getUnit();
+        int[] period = null;
+        int[][] epoch = null;
+        double kmUp = access.getUpperKM();
+        double kmLow = access.getLowerKM();
+        if (yearEpoch.equals("year")) {
+            period = access.getPeriod();
+            epoch = null;
+        }
+        else if (yearEpoch.equals("epoch") || yearEpoch.equals("off_epoch")) {
+            epoch = access.getEpochs();
+            period = null;
+        }
+        else {
+            addProblem("minfo.missing.year_epoch");
+        }
+
+        if (river == null) {
+            // TODO: i18n
+            addProblem("minfo.missing.river");
+        }
+
+        if (period == null && epoch == null) {
+            addProblem("minfo.missing.time");
+        }
+
+        if (!hasProblems()) {
+            this.river = river;
+            this.yearEpoch = yearEpoch;
+            this.unit = unit;
+            this.period = period;
+            this.epoch = epoch;
+            this.kmUp = kmUp;
+            this.kmLow = kmLow;
+            return internalCalculate();
+        }
+
+        return new CalculationResult();
+    }
+
+    private CalculationResult internalCalculate() {
+        logger.debug("internalCalulate; mode:" + yearEpoch);
+        if (yearEpoch.equals("year")) {
+            List<SedimentLoadResult> results =
+                new ArrayList<SedimentLoadResult>();
+            for (int i = period[0]; i <= period[1]; i++) {
+                SedimentLoadResult res = calculateYear(i);
+                results.add(res);
+            }
+            return new CalculationResult(
+                results.toArray(new SedimentLoadResult[results.size()]), this);
+        }
+        else if (yearEpoch.equals("epoch")) {
+            List<SedimentLoadResult> results =
+                new ArrayList<SedimentLoadResult>();
+            for (int i = 0; i < epoch.length; i++) {
+                SedimentLoadResult res = calculateEpoch(i);
+                results.add(res);
+            }
+            return new CalculationResult(
+                results.toArray(new SedimentLoadResult[results.size()]), this);
+        }
+        else if (yearEpoch.equals("off_epoch")) {
+            List<SedimentLoadResult> results =
+                new ArrayList<SedimentLoadResult>();
+            for (int i = 0; i < epoch.length; i++) {
+                SedimentLoadResult res = calculateOffEpoch(i);
+                results.add(res);
+            }
+            return new CalculationResult(
+                results.toArray(new SedimentLoadResult[results.size()]), this);
+        }
+        return null;
+    }
+
+    private SedimentLoadResult calculateEpoch(int i) {
+        List<SedimentLoad> epochLoads = new ArrayList<SedimentLoad>();
+        for (int j = epoch[i][0]; j < epoch[i][1]; j++) {
+            epochLoads.add(SedimentLoadFactory.getLoadWithData(
+                this.river,
+                this.yearEpoch,
+                this.kmLow,
+                this.kmUp,
+                j,
+                j));
+        }
+
+        SedimentLoad resLoad = new SedimentLoad();
+        TDoubleArrayList kms = new TDoubleArrayList();
+
+        for (SedimentLoad load : epochLoads) {
+            for (double km : load.getKms()) {
+                if (!kms.contains(km)) {
+                    kms.add(km);
+                }
+            }
+        }
+
+        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());
+                    cSum++;
+                }
+                if (f.getFine_middle() > 0d) {
+                    double fm = resLoad.getFraction(km).getFine_middle();
+                    resLoad.setFineMiddle(km, fm + f.getFine_middle());
+                    fmSum++;
+                }
+                if (f.getSand() > 0d) {
+                    double s = resLoad.getFraction(km).getSand();
+                    resLoad.setSand(km, s + f.getSand());
+                    sSum++;
+                }
+                if (f.getSusp_sand() > 0d) {
+                    double s = resLoad.getFraction(km).getSusp_sand();
+                    resLoad.setSuspSand(km, s + f.getSusp_sand());
+                    ssSum++;
+                }
+                if (f.getSusp_sand_bed() > 0d) {
+                    double s = resLoad.getFraction(km).getSusp_sand_bed();
+                    resLoad.setSuspSandBed(km, s + f.getSusp_sand_bed());
+                    ssbSum++;
+                }
+                if (f.getSusp_sediment() > 0d) {
+                    double s = resLoad.getFraction(km).getSusp_sediment();
+                    resLoad.setSuspSediment(km, s + f.getSusp_sediment());
+                    sseSum++;
+                }
+            }
+            SedimentLoadFraction fr = resLoad.getFraction(km);
+            resLoad.setCoarse(km, fr.getCoarse()/cSum);
+            resLoad.setFineMiddle(km, fr.getFine_middle()/fmSum);
+            resLoad.setSand(km, fr.getSand()/sSum);
+            resLoad.setSuspSand(km, fr.getSusp_sand()/ssSum);
+            resLoad.setSuspSandBed(km, fr.getSusp_sand_bed()/ssbSum);
+            resLoad.setSuspSediment(km, fr.getSusp_sediment()/sseSum);
+        }
+        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);
+        }
+
+        return result;
+    }
+
+    private SedimentLoadResult calculateOffEpoch(int i) {
+        SedimentLoad load = SedimentLoadFactory.getLoadWithData(
+            this.river,
+            this.yearEpoch,
+            kmLow,
+            kmUp,
+            this.epoch[i][0],
+            this.epoch[i][1]);
+        SedimentLoadResult result;
+        SedimentLoad sl = calculateTotalLoad(load, this.epoch[i][0]);
+        if (unit.equals("m3_per_a")) {
+            SedimentLoad slu = calculateUnit(sl, 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);
+        }
+
+        return result;
+    }
+
+    /** Fetch loads for a single year, calculate total and
+     * return the result containing both. */
+    private SedimentLoadResult calculateYear(int y) {
+        SedimentLoad load = SedimentLoadFactory.getLoadWithData(
+            this.river,
+            this.yearEpoch,
+            this.kmLow,
+            this.kmUp,
+            y,
+            y);
+
+        SedimentLoadResult result;
+        SedimentLoad sl = calculateTotalLoad(load, y);
+        if (unit.equals("m3_per_a")) {
+            SedimentLoad slu = calculateUnit(sl, y);
+            result = new SedimentLoadResult(y, 0, slu);
+        }
+        else {
+            result = new SedimentLoadResult(y, 0, sl);
+        }
+        return result;
+    }
+
+    private SedimentLoad calculateTotalLoad(SedimentLoad load, int year) {
+        logger.debug("calculateTotalLoad");
+        boolean problemThisYear = false;
+        if (!load.hasCoarse()) {
+            addProblem("missing.fraction.coarse", Integer.toString(year));
+            problemThisYear = true;
+        }
+        if (!load.hasFineMiddle()) {
+            addProblem("missing.fraction.fine_middle", Integer.toString(year));
+            problemThisYear = true;
+        }
+        if (!load.hasSand()) {
+            addProblem("missing.fraction.sand", Integer.toString(year));
+            problemThisYear = true;
+        }
+        if (!load.hasSuspSand()) {
+            addProblem("missing.fraction.susp_sand", Integer.toString(year));
+            problemThisYear = true;
+        }
+        if (!load.hasSuspSediment()) {
+            addProblem("missing.fraction.susp_sediment", Integer.toString(year));
+            problemThisYear = true;
+        }
+        if (problemThisYear) {
+            logger.warn("Some problem, not calculating total load.");
+            return load;
+        }
+        for(double km : load.getKms()) {
+            SedimentLoadFraction fraction = load.getFraction(km);
+            double total = 0d;
+            if ((fraction.getCoarse() <= 0d && load.hasCoarse())){
+                addProblem(km, "missing.data.coarse");
+                continue;
+            }
+            if (fraction.getFine_middle() <= 0d && load.hasFineMiddle()) {
+                addProblem(km, "missing.data.fine_middle");
+                continue;
+            }
+            if (fraction.getSand() <= 0d && load.hasSand()) {
+                addProblem(km, "missing data.sand");
+                continue;
+            }
+            if (fraction.getSusp_sand() <= 0d && load.hasSuspSand()) {
+                addProblem(km, "missing.data.susp_sand");
+                continue;
+            }
+            if (fraction.getSusp_sediment() <= 0d && load.hasSuspSediment()) {
+                addProblem(km, "missing.data.susp_sediment");
+                continue;
+            }
+            total += fraction.getCoarse() +
+                fraction.getFine_middle() +
+                fraction.getSand() +
+                fraction.getSusp_sand() +
+                fraction.getSusp_sediment();
+            load.setTotal(km, total);
+        }
+        return load;
+    }
+
+    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);
+            double coarse = fraction.getCoarse();
+            double fineMiddle = fraction.getFine_middle();
+            double sand = fraction.getSand();
+            double suspSand = fraction.getSusp_sand();
+            double bedSand = fraction.getSusp_sand_bed();
+            double sediment = fraction.getSusp_sediment();
+            double total = fraction.getTotal();
+            load.setCoarse(km, (coarse * dens));
+            load.setFineMiddle(km, (fineMiddle * dens));
+            load.setSand(km, (sand * dens));
+            load.setSuspSand(km, (suspSand * dens));
+            load.setSuspSandBed(km, (bedSand * dens));
+            load.setSuspSediment(km, (sediment * dens));
+            load.setTotal(km, (total * dens));
+        }
+        return load;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentLoadFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,178 @@
+package org.dive4elements.river.artifacts.model.minfo;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.DataFacet;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+import org.dive4elements.river.model.MeasurementStation;
+
+import org.dive4elements.river.utils.FLYSUtils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.apache.log4j.Logger;
+
+
+/** Facet to access various sediment loads. */
+public class SedimentLoadFacet
+extends DataFacet
+{
+    /** Very own logger. */
+    private static Logger logger = Logger.getLogger(SedimentLoadFacet.class);
+
+    /** Used as tolerance value when fetching measurement stations. */
+    private static double EPSILON = 1e-5;
+
+
+    public SedimentLoadFacet() {
+    }
+
+    public SedimentLoadFacet(int idx, String name, String description,
+        ComputeType type, String stateId, String hash) {
+        super(idx, name, description, type, hash, stateId);
+    }
+
+    public Object getData(Artifact artifact, CallContext context) {
+        logger.debug("Get data for sediment load at index: " + index);
+
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        CalculationResult res = (CalculationResult) flys.compute(context, hash,
+            stateId, type, false);
+
+        Object[] data =
+            (SedimentLoadResult[]) res.getData(); // TODO CAST TO SPECIFIC CLASS
+
+        List<MeasurementStation> allStations = FLYSUtils.getRiver(flys).getMeasurementStations();
+        SedimentLoadResult result = data != null && data.length > index ? (SedimentLoadResult)data[index] : null;
+        if (result == null) {
+            return null;
+        }
+
+        List<Double> sortedStarts = new ArrayList<Double>();
+        // Filter stations according to type.
+        List<MeasurementStation> stations = new ArrayList<MeasurementStation>();
+        for (MeasurementStation station: allStations) {
+            if (station.getRange() == null || station.getMeasurementType() == null) {
+                continue;
+            }
+            if (FacetTypes.IS.SEDIMENT_LOAD_NO_FLOAT(this.getName())
+                && station.getMeasurementType().equals("Geschiebe")) {
+                stations.add(station);
+                sortedStarts.add(station.getStation());
+            }
+            else if (!FacetTypes.IS.SEDIMENT_LOAD_NO_FLOAT(this.getName())
+                && station.getMeasurementType().equals("Schwebstoff")) {
+                stations.add(station);
+                sortedStarts.add(station.getStation());
+            }
+        }
+        Collections.sort(sortedStarts);
+
+        // Access data according to type.
+        double[][] sd = getLoadData(result);
+
+        // Sort by km.
+        TreeMap<Double, Double> sortData = new TreeMap<Double,Double>();
+
+        double[] km   = sd[0];
+        double[] load = sd[1];
+
+        for (int i = 0 ; i < km.length; i++) {
+            sortData.put(km[i], load[i]);
+        }
+
+        double[][] values = new double[2][];
+        values[0] = new double[km.length*3];
+        values[1] = new double[km.length*3];
+
+        List<double[]> kmWithoutStation = new ArrayList<double[]>();
+
+        // Find station via its station (km).
+        // TODO use a binarySearch instead of linear absdiff approach
+        int i = 0;
+        for (Map.Entry<Double, Double> entry: sortData.entrySet()) {
+            boolean matchFound = false;
+            // For now, ignore overlaps like (B> next A)
+            for (MeasurementStation station: stations) {
+                if (Math.abs(station.getStation() - entry.getKey()) < EPSILON ||
+                    station.getRange().containsTolerant(entry.getKey())) {
+                    // TODO: In rare cases, two matches can be found.
+                    values[0][i*3] = station.getRange().getA().doubleValue() + EPSILON;
+                    values[1][i*3] = entry.getValue();
+                    values[0][i*3+1] = station.getRange().getB().doubleValue() - EPSILON;
+                    values[1][i*3+1] = entry.getValue();
+                    values[0][i*3+2] = station.getRange().getB().doubleValue();
+                    values[1][i*3+2] = entry.getValue();
+                    matchFound = true;
+                }
+            }
+            // Store points without match for later assessment.
+            if (!matchFound) {
+                logger.warn("measurement without station ("+entry.getKey()+")!");
+            }
+            i++;
+        }
+
+        for (int x = 0; x < values[0].length-1; x++) {
+            // Introduce gaps where no data in measurement station.
+            if (Math.abs(values[0][x+1] - values[0][x]) > 3*EPSILON
+                    && values[1][x+1] != values[1][x]) {
+                values[0][x] = Double.NaN;
+                values[1][x] = Double.NaN;
+            }
+        }
+
+        return values;
+    }
+
+
+    /** Get data according to type of facet. */
+    private double[][] getLoadData(SedimentLoadResult result) {
+        if (getName().equals(FacetTypes.SEDIMENT_LOAD_SAND))
+            return result.getSandData();
+        else if (getName().equals(FacetTypes.SEDIMENT_LOAD_COARSE))
+            return result.getCoarseData();
+        else if (getName().equals(FacetTypes.SEDIMENT_LOAD_FINEMIDDLE))
+            return result.getFineMiddleData();
+        else if (getName().equals(FacetTypes.SEDIMENT_LOAD_SUSP_SAND))
+            return result.getSuspSandData();
+        else if (getName().equals(FacetTypes.SEDIMENT_LOAD_SUSP_SAND_BED))
+            return result.getSuspSandBedData();
+        else if (getName().equals(FacetTypes.SEDIMENT_LOAD_SUSP_SEDIMENT))
+            return result.getSuspSedimentData();
+        else if (getName().equals(FacetTypes.SEDIMENT_LOAD_TOTAL_LOAD))
+            return result.getTotalLoadData();
+        else if (getName().equals(FacetTypes.SEDIMENT_LOAD_TOTAL))
+            return result.getTotalData();
+        else {
+            logger.error("SedimentLoadFacet " + getName() + " cannot determine data type.");
+            return null;
+        }
+    }
+
+    /** Copy deeply. */
+    @Override
+    public Facet deepCopy() {
+        SedimentLoadFacet copy = new SedimentLoadFacet();
+        copy.set(this);
+        copy.type = type;
+        copy.hash = hash;
+        copy.stateId = stateId;
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentLoadFactory.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,383 @@
+package org.dive4elements.river.artifacts.model.minfo;
+
+import gnu.trove.TDoubleArrayList;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.Element;
+
+import org.apache.log4j.Logger;
+import org.hibernate.SQLQuery;
+import org.hibernate.Session;
+import org.hibernate.type.StandardBasicTypes;
+
+import org.dive4elements.river.artifacts.cache.CacheFactory;
+import org.dive4elements.river.artifacts.model.StaticSedimentLoadCacheKey;
+import org.dive4elements.river.backend.SessionHolder;
+
+/** Pull Sediment Loads out of db. */
+public class SedimentLoadFactory
+{
+    /** Private logger to use here. */
+    private static Logger log = Logger.getLogger(SedimentLoadFactory.class);
+
+    public static final String LOADS_CACHE_NAME = "sedimentloads";
+    public static final String LOAD_DATA_CACHE_NAME = "sedimentload-data";
+
+    /** Query to get km and ws for wst_id and column_pos. */
+    public static final String SQL_SELECT_SINGLES =
+        "SELECT DISTINCT " +
+        "       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 = :name " +
+        "       AND ti.stop_time IS NULL " +
+        "       AND syv.station BETWEEN :startKm AND :endKm";
+
+    /** Query to get name for wst_id and column_pos. */
+    public static final String SQL_SELECT_EPOCHS =
+        "SELECT DISTINCT " +
+        "       sy.description AS description, " +
+        "       ti.start_time AS start, " +
+        "       ti.stop_time AS end " +
+        "   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 = :name " +
+        "       AND ti.stop_time IS NOT NULL " +
+        "       AND syv.station BETWEEN :startKm AND :endKm";
+
+    public static final String SQL_SELECT_SINGLES_DATA =
+        "SELECT" +
+        "       sy.description AS description, " +
+        "       ti.start_time AS year, " +
+        "       syv.value AS load, " +
+        "       syv.station AS km " +
+        "   FROM     sediment_yield sy " +
+        "       JOIN rivers r ON sy.river_id = r.id " +
+        "       JOIN time_intervals ti ON sy.time_interval_id = ti.id " +
+        "       JOIN sediment_yield_values syv ON sy.id = syv.sediment_yield_id " +
+        "       JOIN grain_fraction gf ON sy.grain_fraction_id = gf.id " +
+        "   WHERE   r.name = :name " +
+        "       AND ti.start_time BETWEEN :begin AND :end " +
+        "       AND ti.stop_time IS NULL " +
+        "       AND gf.name = :grain " +
+        "       AND syv.station BETWEEN :startKm AND :endKm";
+
+    public static final String SQL_SELECT_EPOCHS_DATA =
+        "SELECT" +
+        "       sy.description AS description, " +
+        "       ti.start_time AS startYear, " +
+        "       syv.value AS load, " +
+        "       syv.station AS km," +
+        "       ti.stop_time AS endYear " +
+        "   FROM     sediment_yield sy " +
+        "       JOIN rivers r ON sy.river_id = r.id " +
+        "       JOIN time_intervals ti ON sy.time_interval_id = ti.id " +
+        "       JOIN sediment_yield_values syv ON sy.id = syv.sediment_yield_id " +
+        "       JOIN grain_fraction gf ON sy.grain_fraction_id = gf.id " +
+        "   WHERE   r.name = :name " +
+        "       AND ti.start_time BETWEEN :sbegin AND :send " +
+        "       AND ti.stop_time BETWEEN :ebegin AND :eend " +
+        "       AND gf.name = :grain " +
+        "       AND syv.station BETWEEN :startKm AND :endKm";
+
+    private SedimentLoadFactory() {
+    }
+
+    /**
+     *
+     */
+    public static SedimentLoad[] getLoads(
+        String river,
+        String type,
+        double startKm,
+        double endKm
+    ) {
+        log.debug("SedimentLoadFactory.getLoads");
+        Cache cache = CacheFactory.getCache(LOADS_CACHE_NAME);
+
+        if (cache == null) {
+            log.debug("Cache not configured.");
+            return getSedimentLoadsUncached(river, type, startKm, endKm);
+        }
+
+        StaticSedimentLoadCacheKey key =
+            new StaticSedimentLoadCacheKey(river, startKm, endKm, 0, 0);
+
+        Element element = cache.get(key);
+
+        if (element != null) {
+            log.debug("SedimentLoad found in cache");
+            return (SedimentLoad[])element.getValue();
+        }
+
+        SedimentLoad[] values =
+            getSedimentLoadsUncached(river, type, startKm, endKm);
+
+        if (values != null && key != null) {
+            log.debug("Store static sediment load values in cache.");
+            element = new Element(key, values);
+            cache.put(element);
+        }
+        return values;
+    }
+
+    public static SedimentLoad getLoadWithData(
+        String river,
+        String type,
+        double startKm,
+        double endKm,
+        int syear,
+        int eyear
+    ) {
+        log.debug("SedimentLoadFactory.getLoadWithData");
+        Cache cache = CacheFactory.getCache(LOAD_DATA_CACHE_NAME);
+
+        if (cache == null) {
+            log.debug("Cache not configured.");
+            return getSedimentLoadWithDataUncached(
+                river,
+                type,
+                startKm,
+                endKm,
+                syear,
+                eyear);
+        }
+
+        StaticSedimentLoadCacheKey key =
+            new StaticSedimentLoadCacheKey(river, startKm, endKm, syear, eyear);
+
+        Element element = cache.get(key);
+
+        if (element != null) {
+            log.debug("SedimentLoad found in cache");
+            return (SedimentLoad)element.getValue();
+        }
+
+        SedimentLoad values = getSedimentLoadWithDataUncached(
+            river,
+            type,
+            startKm,
+            endKm,
+            syear,
+            eyear);
+
+        if (values != null && key != null) {
+            log.debug("Store static bed height values in cache.");
+            element = new Element(key, values);
+            cache.put(element);
+        }
+        return values;
+    }
+
+    /**
+     * Get sediment loads from db.
+     * @param river the river
+     * @param type the sediment load type (year or epoch)
+     * @return according sediment loads.
+     */
+    public static SedimentLoad[] getSedimentLoadsUncached(
+        String river,
+        String type,
+        double startKm,
+        double endKm
+    ) {
+        log.debug("SedimentLoadFactory.getSedimentLoadsUncached");
+
+        Session session = SessionHolder.HOLDER.get();
+        SQLQuery sqlQuery = null;
+
+        if (type.equals("single")) {
+            sqlQuery = session.createSQLQuery(SQL_SELECT_SINGLES)
+                .addScalar("description", StandardBasicTypes.STRING)
+                .addScalar("year", StandardBasicTypes.DATE);
+            sqlQuery.setString("name", river);
+            sqlQuery.setDouble("startKm", startKm);
+            sqlQuery.setDouble("endKm", endKm);
+            List<Object []> results = sqlQuery.list();
+            SedimentLoad[] loads = new SedimentLoad[results.size()];
+            for (int i = 0; i < results.size(); i++) {
+                Object[] row = results.get(i);
+                loads[i] = new SedimentLoad(
+                    (String) row[0],
+                    (Date) row[1],
+                    null,
+                    false);
+            }
+            return loads;
+        }
+        else if (type.equals("epoch")) {
+            sqlQuery = session.createSQLQuery(SQL_SELECT_EPOCHS)
+                .addScalar("description", StandardBasicTypes.STRING)
+                .addScalar("start", StandardBasicTypes.DATE)
+                .addScalar("end", StandardBasicTypes.DATE);
+            sqlQuery.setString("name", river);
+            sqlQuery.setDouble("startKm", startKm);
+            sqlQuery.setDouble("endKm", endKm);
+            List<Object []> results = sqlQuery.list();
+
+            SedimentLoad[] loads = new SedimentLoad[results.size()];
+            for (int i = 0; i < results.size(); i++) {
+                Object[] row = results.get(i);
+                loads[i] = new SedimentLoad(
+                    (String) row[0],
+                    (Date) row[1],
+                    (Date) row[2],
+                    true);
+            }
+            return loads;
+        }
+        return new SedimentLoad[0];
+    }
+
+    /**
+     * Get sediment loads from db.
+     * @param river the river
+     * @param type the sediment load type (year or epoch)
+     * @return according sediment loads.
+     */
+    public static SedimentLoad getSedimentLoadWithDataUncached(
+        String river,
+        String type,
+        double startKm,
+        double endKm,
+        int syear,
+        int eyear
+    ) {
+        log.debug("SedimentLoadFactory.getSedimentLoadWithDataUncached");
+        Session session = SessionHolder.HOLDER.get();
+        SQLQuery sqlQuery = null;
+
+        Calendar start = Calendar.getInstance();
+        start.set(syear - 1, 11, 31);
+        Calendar end = Calendar.getInstance();
+        end.set(syear, 11, 30);
+
+        if (type.equals("year") || type.equals("epoch")) {
+            sqlQuery = session.createSQLQuery(SQL_SELECT_SINGLES_DATA)
+                .addScalar("description", StandardBasicTypes.STRING)
+                .addScalar("year", StandardBasicTypes.DATE)
+                .addScalar("load", StandardBasicTypes.DOUBLE)
+                .addScalar("km", StandardBasicTypes.DOUBLE);
+            sqlQuery.setString("name", river);
+            sqlQuery.setDouble("startKm", startKm);
+            sqlQuery.setDouble("endKm", endKm);
+            sqlQuery.setDate("begin", start.getTime());
+            sqlQuery.setDate("end", end.getTime());
+            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);
+            getValues("coarse", sqlQuery, load);
+            getValues("fine_middle", sqlQuery, load);
+            getValues("sand", sqlQuery, load);
+            getValues("suspended_sediment", sqlQuery, load);
+            getValues("susp_sand_bed", sqlQuery, load);
+            getValues("susp_sand", sqlQuery, load);
+
+            return load;
+        }
+        else if (type.equals("off_epoch")) {
+            Calendar toStart = Calendar.getInstance();
+            toStart.set(eyear - 1, 11, 31);
+            Calendar toEnd = Calendar.getInstance();
+            toEnd.set(eyear, 11, 30);
+            sqlQuery = session.createSQLQuery(SQL_SELECT_EPOCHS_DATA)
+                .addScalar("description", StandardBasicTypes.STRING)
+                .addScalar("startYear", StandardBasicTypes.DATE)
+                .addScalar("load", StandardBasicTypes.DOUBLE)
+                .addScalar("km", StandardBasicTypes.DOUBLE)
+                .addScalar("endYear", StandardBasicTypes.DATE);
+            sqlQuery.setString("name", river);
+            sqlQuery.setDouble("startKm", startKm);
+            sqlQuery.setDouble("endKm", endKm);
+            sqlQuery.setDate("sbegin", start.getTime());
+            sqlQuery.setDate("send", end.getTime());
+            sqlQuery.setDate("ebegin",toStart.getTime());
+            sqlQuery.setDate("eend", toEnd.getTime());
+            sqlQuery.setString("grain", "total");
+
+            List<Object[]> results = null;
+            results = sqlQuery.list();
+
+            SedimentLoad load = new SedimentLoad();
+            Object[] row = results.get(0);
+            load = new SedimentLoad(
+                    (String) row[0],
+                    (Date) row[1],
+                    (Date) row[4],
+                    true);
+            TDoubleArrayList kms = new TDoubleArrayList();
+            for (int i = 0; i < results.size(); i++) {
+                row = results.get(i);
+                kms.add((Double)row[3]);
+                load.setLoadTotal((Double)row[3], (Double)row[2]);
+            }
+            getValues("coarse", sqlQuery, load);
+            getValues("fine_middle", sqlQuery, load);
+            getValues("sand", sqlQuery, load);
+            getValues("suspended_sediment", sqlQuery, load);
+            getValues("susp_sand_bed", sqlQuery, load);
+            getValues("susp_sand", sqlQuery, load);
+            return load;
+        }
+        return new SedimentLoad();
+    }
+
+
+    /**
+     * Run query with grain parameter set to fraction, feed result into
+     * load.
+     * @param fraction value to set 'grain' parameter in query to.
+     * @param query query in which to set 'grain' parameter and run.
+     * @param load[out] SedimentLoad which to populate with values.
+     */
+    protected static void getValues (
+        String fraction,
+        SQLQuery query,
+        SedimentLoad load
+    ) {
+        query.setString("grain", fraction);
+        List<Object[]> results = query.list();
+        for (int i = 0; i < results.size(); i++) {
+            Object[] row = results.get(i);
+            double km = (Double)row[3];
+            double v = -1;
+            if (row[2] != null) {
+                v = ((Double)row[2]).doubleValue();
+            }
+            if (fraction.equals("coarse")) {
+                load.setCoarse(km, v);
+            }
+            else if (fraction.equals("sand")) {
+                load.setSand(km, v);
+            }
+            else if (fraction.equals("fine_middle")) {
+                load.setFineMiddle(km, v);
+            }
+            else if (fraction.equals("suspended_sediment")) {
+                load.setSuspSediment(km, v);
+            }
+            else if (fraction.equals("susp_sand")) {
+                load.setSuspSand(km, v);
+            }
+            else if (fraction.equals("susp_sand_bed")) {
+                load.setSuspSandBed(km, v);
+            }
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentLoadFraction.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,91 @@
+package org.dive4elements.river.artifacts.model.minfo;
+
+import org.dive4elements.river.artifacts.model.NamedObjectImpl;
+
+
+public class SedimentLoadFraction
+extends NamedObjectImpl
+{
+    double sand;
+    double fine_middle;
+    double coarse;
+    double susp_sand;
+    double susp_sand_bed;
+    double susp_sediment;
+    double loadTotal;
+    double total;
+
+    public SedimentLoadFraction() {
+        sand = 0d;
+        fine_middle = 0d;
+        coarse = 0d;
+        susp_sand = 0d;
+        susp_sand_bed = 0d;
+        susp_sediment = 0d;
+        loadTotal = 0d;
+    }
+
+    public double getSand() {
+        return sand;
+    }
+
+    public void setSand(double sand) {
+        this.sand = sand;
+    }
+
+    public double getFine_middle() {
+        return fine_middle;
+    }
+
+    public void setFine_middle(double fine_middle) {
+        this.fine_middle = fine_middle;
+    }
+
+    public double getCoarse() {
+        return coarse;
+    }
+
+    public void setCoarse(double coarse) {
+        this.coarse = coarse;
+    }
+
+    public double getSusp_sand() {
+        return susp_sand;
+    }
+
+    public void setSusp_sand(double susp_sand) {
+        this.susp_sand = susp_sand;
+    }
+
+    public double getSusp_sand_bed() {
+        return susp_sand_bed;
+    }
+
+    public void setSusp_sand_bed(double susp_sand_bed) {
+        this.susp_sand_bed = susp_sand_bed;
+    }
+
+    public double getSusp_sediment() {
+        return susp_sediment;
+    }
+
+    public void setSusp_sediment(double susp_sediment) {
+        this.susp_sediment = susp_sediment;
+    }
+
+    public double getTotal() {
+        return total;
+    }
+
+    public void setTotal(double total) {
+        this.total = total;
+    }
+
+    public double getLoadTotal() {
+        return loadTotal;
+    }
+
+    public void setLoadTotal(double total) {
+        this.loadTotal = total;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentLoadResult.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,213 @@
+package org.dive4elements.river.artifacts.model.minfo;
+
+import gnu.trove.TDoubleArrayList;
+
+import java.io.Serializable;
+import java.util.Set;
+
+import org.apache.log4j.Logger;
+
+
+/** Result from a SedimentLoadCalculation. */
+public class SedimentLoadResult
+implements Serializable
+{
+    private static final Logger logger = Logger
+        .getLogger(SedimentLoadResult.class);
+    protected int startYear;
+    protected int endYear;
+    protected SedimentLoad load;
+
+    public SedimentLoadResult() {
+    }
+
+    public SedimentLoadResult(
+        int startYear,
+        int endYear,
+        SedimentLoad load
+    ) {
+        this.startYear = startYear;
+        this.endYear = endYear;
+        this.load = load;
+    }
+
+    public int getStartYear() {
+        return this.startYear;
+    }
+
+    public void setStartYear(int year) {
+        this.startYear = year;
+    }
+
+    public int getEndYear() {
+        return this.endYear;
+    }
+
+    public void setEndYear(int year) {
+        this.endYear = year;
+    }
+
+    public double[][] getTotalData () {
+        Set<Double> kms = this.load.getKms();
+        TDoubleArrayList k = new TDoubleArrayList();
+        TDoubleArrayList total = new TDoubleArrayList();
+        for (double km : kms) {
+            if (load.getFraction(km).getTotal() > 0d) {
+                k.add(km);
+                total.add(load.getFraction(km).getTotal());
+            }
+        }
+        return new double [][] {
+            k.toNativeArray(),
+            total.toNativeArray()
+        };
+    }
+
+    /** Search all SedimenLoads fractions for sand and returns
+     * an array [[km1, km2][sand1, sand2]]. */
+    public double[][] getSandData() {
+        Set<Double> kms = this.load.getKms();
+        TDoubleArrayList k = new TDoubleArrayList();
+        TDoubleArrayList sand = new TDoubleArrayList();
+        for(double km : kms) {
+            if (load.getFraction(km).getSand() > 0d) {
+                k.add(km);
+                sand.add(load.getFraction(km).getSand());
+            }
+        }
+        return new double [][] {
+            k.toNativeArray(),
+            sand.toNativeArray()
+        };
+    }
+
+    public double[][] getFineMiddleData() {
+        Set<Double> kms = this.load.getKms();
+        TDoubleArrayList k = new TDoubleArrayList();
+        TDoubleArrayList fm = new TDoubleArrayList();
+        for (double km : kms) {
+            if (load.getFraction(km).getFine_middle() > 0d) {
+                k.add(km);
+                fm.add(load.getFraction(km).getFine_middle());
+            }
+        }
+        return new double [][] {
+            k.toNativeArray(),
+            fm.toNativeArray()
+        };
+    }
+
+    public double[][] getCoarseData() {
+        Set<Double> kms = this.load.getKms();
+        TDoubleArrayList k = new TDoubleArrayList();
+        TDoubleArrayList coarse = new TDoubleArrayList();
+        for (double km : kms) {
+            if (load.getFraction(km).getCoarse() > 0d) {
+                k.add(km);
+                coarse.add(load.getFraction(km).getCoarse());
+            }
+        }
+        return new double [][] {
+            k.toNativeArray(),
+            coarse.toNativeArray()
+        };
+    }
+
+    public double[][] getSuspSandData() {
+        Set<Double> kms = this.load.getKms();
+        TDoubleArrayList k = new TDoubleArrayList();
+        TDoubleArrayList ss = new TDoubleArrayList();
+        for (double km : kms) {
+            if (load.getFraction(km).getSusp_sand() > 0d) {
+                k.add(km);
+                ss.add(load.getFraction(km).getSusp_sand());
+            }
+        }
+        return new double [][] {
+            k.toNativeArray(),
+            ss.toNativeArray()
+        };
+    }
+
+    public double[][] getSuspSandBedData() {
+        Set<Double> kms = this.load.getKms();
+        TDoubleArrayList k = new TDoubleArrayList();
+        TDoubleArrayList ss = new TDoubleArrayList();
+        for (double km : kms) {
+            if (load.getFraction(km).getSusp_sand_bed() > 0d) {
+                k.add(km);
+                ss.add(load.getFraction(km).getSusp_sand_bed());
+            }
+        }
+        return new double [][] {
+            k.toNativeArray(),
+            ss.toNativeArray()
+        };
+    }
+
+    public double[][] getSuspSedimentData() {
+        Set<Double> kms = this.load.getKms();
+        TDoubleArrayList k = new TDoubleArrayList();
+        TDoubleArrayList ss = new TDoubleArrayList();
+        for (double km : kms) {
+            if (load.getFraction(km).getSusp_sediment() > 0d) {
+                k.add(km);
+                ss.add(load.getFraction(km).getSusp_sediment());
+            }
+        }
+        return new double [][] {
+            k.toNativeArray(),
+            ss.toNativeArray()
+        };
+    }
+
+    public double[][] getTotalLoadData() {
+        Set<Double> kms = this.load.getKms();
+        TDoubleArrayList k = new TDoubleArrayList();
+        TDoubleArrayList ss = new TDoubleArrayList();
+        for (double km : kms) {
+            if (load.getFraction(km).getLoadTotal() > 0d) {
+                k.add(km);
+                ss.add(load.getFraction(km).getLoadTotal());
+            }
+        }
+        return new double [][] {
+            k.toNativeArray(),
+            ss.toNativeArray()
+        };
+    }
+
+    public boolean hasCoarseData() {
+        return getCoarseData()[0].length > 0;
+    }
+
+    public boolean hasFineMiddleData() {
+        return getFineMiddleData()[0].length > 0;
+    }
+
+    public boolean hasSandData() {
+        return getSandData()[0].length > 0;
+    }
+
+    public boolean hasSuspSandData() {
+        return getSuspSandData()[0].length > 0;
+    }
+
+    public boolean hasSuspSandBedData() {
+        return getSuspSandBedData()[0].length > 0;
+    }
+
+    public boolean hasSuspSedimentData() {
+        return getSuspSedimentData()[0].length > 0;
+    }
+
+    public boolean hasTotalLoadData() {
+        return getTotalLoadData()[0].length > 0;
+    }
+
+    public boolean hasTotalData() {
+        return getTotalData()[0].length > 0;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/Fitting.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,149 @@
+package org.dive4elements.river.artifacts.model.sq;
+
+import org.dive4elements.river.artifacts.math.fitting.Function;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.math.MathException;
+
+import org.apache.commons.math.optimization.fitting.CurveFitter;
+
+import org.apache.commons.math.optimization.general.LevenbergMarquardtOptimizer;
+
+import org.apache.log4j.Logger;
+
+public class Fitting
+implements   Outlier.Callback
+{
+    private static Logger log = Logger.getLogger(Fitting.class);
+
+    public interface Callback {
+
+        void afterIteration(
+            double [] parameters,
+            SQ []     measurements,
+            SQ []     outliers,
+            double    standardDeviation,
+            double    chiSqr);
+    } // interfacte
+
+    protected Function function;
+
+    protected double [] coeffs;
+
+    protected org.dive4elements.river.artifacts.math.Function instance;
+
+    protected double stdDevFactor;
+    protected double chiSqr;
+
+    protected Callback callback;
+
+    public Fitting() {
+    }
+
+    public Fitting(Function function, double stdDevFactor) {
+        this();
+        this.function     = function;
+        this.stdDevFactor = stdDevFactor;
+    }
+
+    public Function getFunction() {
+        return function;
+    }
+
+    public void setFunction(Function function) {
+        this.function = function;
+    }
+
+    public double getStdDevFactor() {
+        return stdDevFactor;
+    }
+
+    public void setStdDevFactor(double stdDevFactor) {
+        this.stdDevFactor = stdDevFactor;
+    }
+
+    @Override
+    public void initialize(List<SQ> sqs) throws MathException {
+
+        LevenbergMarquardtOptimizer lmo =
+            new LevenbergMarquardtOptimizer();
+
+        CurveFitter cf = new CurveFitter(lmo);
+        for (SQ sq: sqs) {
+            cf.addObservedPoint(sq.getQ(), sq.getS());
+        }
+
+        coeffs = cf.fit(
+            function, function.getInitialGuess());
+
+        instance = function.instantiate(coeffs);
+
+        chiSqr = lmo.getChiSquare();
+    }
+
+    @Override
+    public double eval(SQ sq) {
+        double s = instance.value(sq.q);
+        return sq.s - s;
+    }
+
+    @Override
+    public void iterationFinished(
+        double   standardDeviation,
+        SQ       outlier,
+        List<SQ> remainings
+    ) {
+        if (log.isDebugEnabled()) {
+            log.debug("iterationFinished ----");
+            log.debug(" num remainings: " + remainings.size());
+            log.debug(" has outlier: " + outlier != null);
+            log.debug(" standardDeviation: " + standardDeviation);
+            log.debug(" Chi^2: " + chiSqr);
+            log.debug("---- iterationFinished");
+        }
+        callback.afterIteration(
+            coeffs,
+            remainings.toArray(new SQ[remainings.size()]),
+            outlier != null ? new SQ [] { outlier} : new SQ [] {},
+            standardDeviation,
+            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);
+
+        if (sqs.size() < 2) {
+            log.warn("Too less points for fitting.");
+            return false;
+        }
+
+        this.callback = callback;
+
+        try {
+            Outlier.detectOutliers(this, sqs, stdDevFactor, method);
+        }
+        catch (MathException me) {
+            log.warn(me);
+            return false;
+        }
+
+        return true;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/Measurement.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,202 @@
+package org.dive4elements.river.artifacts.model.sq;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+public class Measurement
+{
+    private static final Log log =
+        LogFactory.getLog(Measurement.class);
+
+    public static final double LOG_10_8 = Math.log(10) - Math.log(8);
+    public static final double SCALE_8 = Math.log(10) - Math.log(6.3);
+
+    public static final double LOG_8_6 = Math.log(8) - Math.log(6.3);
+    public static final double SCALE_4 = Math.log(10) - Math.log(6.3);
+
+    protected Map<String, Object> data;
+
+    protected List<Sieve> sieves;
+
+    protected SieveArray sieveArray;
+
+    public Measurement() {
+    }
+
+    public Measurement(Map<String, Object> data, List<Sieve> sieves) {
+        this.data = data;
+        this.sieves = sieves;
+    }
+
+    protected double get(String name) {
+        Number value = (Number)data.get(name);
+        return value != null ? value.doubleValue() : Double.NaN;
+    }
+
+    protected void set(String name, double value) {
+        data.put(name, Double.valueOf(value));
+    }
+
+    public Object getData(String name) {
+        return data.get(name);
+    }
+
+    public Map<String, Object> getData() {
+        return data;
+    }
+
+    protected void putData(String name, Object value) {
+        data.put(name, value);
+    }
+
+    public double S_SS() {
+        return get("TSAND");
+    }
+
+    public double S_SF() {
+        return get("TSCHWEB") - get("TSAND");
+    }
+
+    public double Q() {
+        return get("Q_BPEGEL");
+    }
+
+    public double TOTAL_BL() {
+        return get("TGESCHIEBE");
+    }
+
+    public double BL_G() {
+        return get("BL_G");
+    }
+
+    public double BL_C() {
+        return get("BL_C");
+    }
+
+    public double BL_S() {
+        return get("BL_S");
+    }
+
+    public double S_BL_S() {
+        return TOTAL_BL() * BL_S();
+    }
+
+    public double S_BL_FG() {
+        return TOTAL_BL() * BL_G();
+    }
+
+    public double S_BL_CG() {
+        return TOTAL_BL() * BL_C();
+    }
+
+    public double S_BL_1() {
+        return S_BL_S() + S_BL_FG() + S_BL_CG();
+    }
+
+    public double S_BL_2() {
+        return S_SS() + S_BL_S() + S_BL_FG() + S_BL_CG();
+    }
+
+    @Override
+    public String toString() {
+        return "Measurement: " + data;
+    }
+
+    /**
+     * Gets the sieves for this instance.
+     *
+     * @return The sieves.
+     */
+    public List<Sieve> getSieves() {
+        return this.sieves;
+    }
+
+    /**
+     * Gets the sieveArray for this instance.
+     *
+     * @return The sieveArray.
+     */
+    public SieveArray getSieveArray() {
+        if (sieveArray == null) {
+            sieveArray = calculateSieveArray();
+        }
+        adjustSieves();
+        return sieveArray;
+    }
+
+    protected Sieve findSieve(double diameter) {
+        for (Sieve s: sieves) {
+            if (s.matchesDiameter(diameter)) {
+                return s;
+            }
+        }
+        return null;
+    }
+
+    protected void deleteSieve(double diameter) {
+        for (int i = sieves.size()-1; i >= 0; --i) {
+            if (sieves.get(i).matchesDiameter(diameter)) {
+                sieves.remove(i);
+                break;
+            }
+        }
+    }
+
+    public void adjustSieves() {
+
+        // If we already have an 8mm diameter sieve
+        // we dont need to 'invent' it.
+        if (findSieve(8d) != null) {
+            return;
+        }
+
+        // create a new 8mm sieve.
+        // delete 6.3mm sieve.
+        // modify 4mm sieve.
+
+        Sieve six  = findSieve(6.3d);
+        Sieve ten  = findSieve(10d);
+        Sieve four = findSieve(4d);
+
+        if (six == null || ten == null || four == null) {
+            log.warn("missing diameter");
+            return;
+        }
+
+        double sixValue  = six.getLoad();
+        double tenValue  = ten.getLoad();
+        double fourValue = four.getLoad();
+
+        deleteSieve(6.3);
+
+        double eightValue = ((LOG_10_8 / SCALE_8*sixValue) + tenValue);
+        double newFourValue = ((LOG_8_6 / SCALE_4*sixValue) + fourValue);
+
+        deleteSieve(4.0);
+        sieves.add(new Sieve(8d, eightValue));
+        sieves.add(new Sieve(4d, newFourValue));
+        sieveArray.adjust(
+            eightValue/sieveArray.totalLoad(),
+            newFourValue/sieveArray.totalLoad());
+        log.debug("Adjusted loads: " + Arrays.toString(sieveArray.getLoads()));
+        log.debug("Adjusted norm loads:" + Arrays.toString(sieveArray.getNormLoads()));
+    }
+
+    protected SieveArray calculateSieveArray() {
+
+        SieveArray sa = new SieveArray();
+
+        for (Sieve s: sieves) {
+            sa.doSieving(s);
+        }
+
+        sa.calculateNormLoads();
+
+        return sa;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/MeasurementFactory.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,511 @@
+package org.dive4elements.river.artifacts.model.sq;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.apache.log4j.Logger;
+
+import org.hibernate.SQLQuery;
+import org.hibernate.Session;
+
+import org.hibernate.transform.BasicTransformerAdapter;
+
+import org.hibernate.type.StandardBasicTypes;
+
+import org.dive4elements.river.artifacts.model.DateRange;
+
+import org.dive4elements.river.backend.SedDBSessionHolder;
+
+public class MeasurementFactory
+{
+    private static final Logger log =
+        Logger.getLogger(MeasurementFactory.class);
+
+    public static final String SQL_TOTALS =
+        "SELECT " +
+            "m.Q_BPEGEL AS Q_BPEGEL,"+
+            "m.TSCHWEB  AS TSCHWEB," +
+            "m.TSAND    AS TSAND " +
+        "FROM MESSUNG m " +
+            "JOIN STATION   s ON m.STATIONID   = s.STATIONID " +
+            "JOIN GEWAESSER r ON s.GEWAESSERID = r.GEWAESSERID " +
+        "WHERE " +
+            "r.NAME = :river_name " +
+            "AND m.Q_BPEGEL IS NOT NULL " +
+            "AND s.KM BETWEEN :location - 0.001 AND :location + 0.001 " +
+            "AND m.DATUM BETWEEN :from AND :to " +
+            "AND m.DATUM IS NOT NULL";
+
+    public static final String SQL_FACTIONS =
+        "SELECT " +
+            "m.datum        AS DATUM," +
+            "m.Q_BPEGEL     AS Q_BPEGEL,"+
+            "g.GLOTRECHTEID AS GLOTRECHTEID," +
+            "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 " +
+        "FROM MESSUNG m " +
+            "JOIN STATION    s ON m.STATIONID    = s.STATIONID " +
+            "JOIN GEWAESSER  r ON s.GEWAESSERID  = r.GEWAESSERID " +
+            "JOIN GLOTRECHTE g ON m.MESSUNGID    = g.MESSUNGID " +
+            "JOIN GPROBE    gp ON g.GLOTRECHTEID = gp.GLOTRECHTEID " +
+            "JOIN GSIEBUNG  gs ON g.GLOTRECHTEID = gs.GLOTRECHTEID " +
+            "JOIN GSIEBSATZ si ON m.GSIEBSATZID  = si.GSIEBSATZID " +
+        "WHERE " +
+            "r.NAME = :river_name " +
+            "AND m.Q_BPEGEL IS NOT NULL " +
+            "AND s.KM BETWEEN :location - 0.001 AND :location + 0.001 " +
+            "AND m.DATUM BETWEEN :from AND :to " +
+            "AND m.TGESCHIEBE IS NOT NULL " +
+            "AND m.DATUM IS NOT NULL " +
+            "AND (" +
+                "COALESCE(gs.RSIEB01, 0) + COALESCE(gs.RSIEB02, 0) +" +
+                "COALESCE(gs.RSIEB03, 0) + COALESCE(gs.RSIEB04, 0) +" +
+                "COALESCE(gs.RSIEB05, 0) + COALESCE(gs.RSIEB06, 0) +" +
+                "COALESCE(gs.RSIEB07, 0) + COALESCE(gs.RSIEB08, 0) +" +
+                "COALESCE(gs.RSIEB09, 0) + COALESCE(gs.RSIEB10, 0) +" +
+                "COALESCE(gs.RSIEB11, 0) + COALESCE(gs.RSIEB12, 0) +" +
+                "COALESCE(gs.RSIEB13, 0) + COALESCE(gs.RSIEB14, 0) +" +
+                "COALESCE(gs.RSIEB15, 0) + COALESCE(gs.RSIEB16, 0) +" +
+                "COALESCE(gs.RSIEB17, 0) + COALESCE(gs.RSIEB18, 0) +" +
+                "COALESCE(gs.RSIEB19, 0) + COALESCE(gs.RSIEB20, 0) +" +
+                "COALESCE(gs.RSIEB21, 0) + COALESCE(gs.REST, 0)) >= 0 " +
+        "ORDER BY " +
+            "m.DATUM, g.UFERABST, g.GLOTRECHTEID, gp.LFDNR";
+
+    public static final BasicTransformerAdapter TOTALS_TRANSFORMER =
+        new BasicTransformerAdapter() {
+			private static final long serialVersionUID = 1L;
+
+			@Override
+            public Object transformTuple(Object [] tuple, String [] aliases) {
+                Map<String, Object> map = new HashMap<String, Object>();
+                for (int i = 0; i < tuple.length; ++i) {
+                    Object value = tuple[i];
+                    if (value != null) {
+                        map.put(aliases[i], value);
+                    }
+                }
+                return new Measurement(map, Collections.<Sieve>emptyList());
+            }
+        };
+
+    private static final int index(String s) {
+        return Integer.parseInt(s.substring(s.length()-2))-1;
+    }
+
+    public static final BasicTransformerAdapter FRACTIONS_TRANSFORMER =
+        new BasicTransformerAdapter() {
+			private static final long serialVersionUID = 1L;
+
+            @Override
+            public Object transformTuple(Object [] tuple, String [] aliases) {
+                Map<String, Object> map = new HashMap<String, Object>();
+
+                Sieve [] sieves = new Sieve[21];
+
+                List<Sieve> validSieves = new ArrayList<Sieve>(21);
+
+                for (int i = 0; i < tuple.length; ++i) {
+                    Object value = tuple[i];
+                    if (value == null) {
+                        continue;
+                    }
+                    String alias = aliases[i];
+                    if (alias.startsWith("SIEB")
+                    ||  alias.startsWith("RSIEB")) {
+                        int idx = index(alias);
+                        Sieve s = sieves[idx];
+                        double v = (Double)value;
+                        if (s == null) {
+                            s = new Sieve();
+                            sieves[idx] = s;
+                        }
+                        if (alias.startsWith("SIEB")) {
+                            s.setDiameter(v);
+                        }
+                        else {
+                            s.setLoad(v);
+                        }
+                    }
+                    else if (alias.equals("REST")) {
+                        Sieve s = new Sieve(0d, (Double)value);
+                        validSieves.add(s);
+                    }
+                    else {
+                        map.put(alias, value);
+                    }
+
+                }
+                for (Sieve s: sieves) {
+                    if (s != null) {
+                        validSieves.add(s);
+                    }
+                }
+                return new Measurement(map, validSieves);
+            }
+        };
+
+    private MeasurementFactory() {
+    }
+
+    public static Measurements getMeasurements(
+        String    river,
+        double    location,
+        DateRange dateRange
+    ) {
+        Session session = SedDBSessionHolder.HOLDER.get();
+        try {
+            List<Measurement> totals = loadTotals(
+                session, river, location, dateRange);
+
+            List<Measurement> accumulated = loadFractions(
+                session, river, location, dateRange);
+
+            return new Measurements(totals, accumulated);
+        }
+        finally {
+            session.close();
+        }
+    }
+
+    protected static List<Measurement> loadTotals(
+        Session   session,
+        String    river,
+        double    location,
+        DateRange dateRange
+    ) {
+        SQLQuery query = session.createSQLQuery(SQL_TOTALS)
+            .addScalar("Q_BPEGEL", StandardBasicTypes.DOUBLE)
+            .addScalar("TSCHWEB",  StandardBasicTypes.DOUBLE)
+            .addScalar("TSAND",    StandardBasicTypes.DOUBLE);
+
+        query.setString("river_name", river);
+        query.setDouble("location", location);
+        query.setDate("from", dateRange.getFrom());
+        query.setDate("to", dateRange.getTo());
+
+        query.setResultTransformer(TOTALS_TRANSFORMER);
+
+        @SuppressWarnings("unchecked")
+        List<Measurement> result = (List<Measurement>)query.list();
+        return result;
+    }
+
+    protected static List<Measurement> loadFractions(
+        Session   session,
+        String    river,
+        double    location,
+        DateRange dateRange
+    ) {
+        boolean debug = log.isDebugEnabled();
+
+        SQLQuery query = session.createSQLQuery(SQL_FACTIONS)
+            .addScalar("Q_BPEGEL",     StandardBasicTypes.DOUBLE)
+            .addScalar("DATUM",        StandardBasicTypes.DATE)
+            .addScalar("GLOTRECHTEID", StandardBasicTypes.INTEGER)
+            .addScalar("LFDNR",        StandardBasicTypes.INTEGER)
+            .addScalar("UFERABST",     StandardBasicTypes.DOUBLE)
+            .addScalar("UFERABLINKS",  StandardBasicTypes.DOUBLE)
+            .addScalar("TSCHWEB",      StandardBasicTypes.DOUBLE)
+            .addScalar("TSAND",        StandardBasicTypes.DOUBLE)
+            .addScalar("GTRIEB",       StandardBasicTypes.DOUBLE)
+            .addScalar("TGESCHIEBE",   StandardBasicTypes.DOUBLE)
+            .addScalar("RSIEB01",      StandardBasicTypes.DOUBLE)
+            .addScalar("RSIEB02",      StandardBasicTypes.DOUBLE)
+            .addScalar("RSIEB03",      StandardBasicTypes.DOUBLE)
+            .addScalar("RSIEB04",      StandardBasicTypes.DOUBLE)
+            .addScalar("RSIEB05",      StandardBasicTypes.DOUBLE)
+            .addScalar("RSIEB06",      StandardBasicTypes.DOUBLE)
+            .addScalar("RSIEB07",      StandardBasicTypes.DOUBLE)
+            .addScalar("RSIEB08",      StandardBasicTypes.DOUBLE)
+            .addScalar("RSIEB09",      StandardBasicTypes.DOUBLE)
+            .addScalar("RSIEB10",      StandardBasicTypes.DOUBLE)
+            .addScalar("RSIEB11",      StandardBasicTypes.DOUBLE)
+            .addScalar("RSIEB12",      StandardBasicTypes.DOUBLE)
+            .addScalar("RSIEB13",      StandardBasicTypes.DOUBLE)
+            .addScalar("RSIEB14",      StandardBasicTypes.DOUBLE)
+            .addScalar("RSIEB15",      StandardBasicTypes.DOUBLE)
+            .addScalar("RSIEB16",      StandardBasicTypes.DOUBLE)
+            .addScalar("RSIEB17",      StandardBasicTypes.DOUBLE)
+            .addScalar("RSIEB18",      StandardBasicTypes.DOUBLE)
+            .addScalar("RSIEB19",      StandardBasicTypes.DOUBLE)
+            .addScalar("RSIEB20",      StandardBasicTypes.DOUBLE)
+            .addScalar("RSIEB21",      StandardBasicTypes.DOUBLE)
+            .addScalar("REST",         StandardBasicTypes.DOUBLE)
+            .addScalar("SIEB01",       StandardBasicTypes.DOUBLE)
+            .addScalar("SIEB02",       StandardBasicTypes.DOUBLE)
+            .addScalar("SIEB03",       StandardBasicTypes.DOUBLE)
+            .addScalar("SIEB04",       StandardBasicTypes.DOUBLE)
+            .addScalar("SIEB05",       StandardBasicTypes.DOUBLE)
+            .addScalar("SIEB06",       StandardBasicTypes.DOUBLE)
+            .addScalar("SIEB07",       StandardBasicTypes.DOUBLE)
+            .addScalar("SIEB08",       StandardBasicTypes.DOUBLE)
+            .addScalar("SIEB09",       StandardBasicTypes.DOUBLE)
+            .addScalar("SIEB10",       StandardBasicTypes.DOUBLE)
+            .addScalar("SIEB11",       StandardBasicTypes.DOUBLE)
+            .addScalar("SIEB12",       StandardBasicTypes.DOUBLE)
+            .addScalar("SIEB13",       StandardBasicTypes.DOUBLE)
+            .addScalar("SIEB14",       StandardBasicTypes.DOUBLE)
+            .addScalar("SIEB15",       StandardBasicTypes.DOUBLE)
+            .addScalar("SIEB16",       StandardBasicTypes.DOUBLE)
+            .addScalar("SIEB17",       StandardBasicTypes.DOUBLE)
+            .addScalar("SIEB18",       StandardBasicTypes.DOUBLE)
+            .addScalar("SIEB19",       StandardBasicTypes.DOUBLE)
+            .addScalar("SIEB20",       StandardBasicTypes.DOUBLE)
+            .addScalar("SIEB21",       StandardBasicTypes.DOUBLE);
+
+        query.setString("river_name", river);
+        query.setDouble("location", location);
+        query.setDate("from", dateRange.getFrom());
+        query.setDate("to", dateRange.getTo());
+
+        query.setResultTransformer(FRACTIONS_TRANSFORMER);
+
+        @SuppressWarnings("unchecked")
+        List<Measurement> measuments = (List<Measurement>)query.list();
+
+        if (debug) {
+            log.debug("num fraction results: " + measuments.size());
+        }
+
+        List<Measurement> same = new ArrayList<Measurement>();
+
+        Integer lastLR = null;
+
+        List<Measurement> accumulated = new ArrayList<Measurement>();
+
+        for (Measurement m: measuments) {
+            Integer currentLR = (Integer)m.getData("GLOTRECHTEID");
+
+            boolean newDS = lastLR == null
+                || (currentLR != null && !lastLR.equals(currentLR));
+
+            if (newDS && !same.isEmpty()) {
+                accumulated.add(accumulate(same));
+                same.clear();
+            }
+
+            same.add(m);
+
+            lastLR = currentLR;
+        }
+
+        if (!same.isEmpty()) {
+            accumulated.add(accumulate(same));
+        }
+
+        if (debug) {
+            log.debug("Before date separation: " + accumulated.size());
+        }
+
+        accumulated = separateByDate(accumulated);
+
+        if (debug) {
+            log.debug("After date separation: " + accumulated.size());
+        }
+
+        return accumulated;
+    }
+
+    protected static List<Measurement> separateByDate(List<Measurement> measurements) {
+
+        List<Measurement> result = new ArrayList<Measurement>();
+
+        List<Measurement> same = new ArrayList<Measurement>();
+
+        Date lastDate = null;
+
+        for (Measurement m: measurements) {
+            Date currentDate = (Date)m.getData("DATUM");
+            if ((lastDate == null
+            || !equalDate(currentDate, lastDate))
+            && !same.isEmpty()
+            ) {
+                result.add(processSameDate(same));
+                same.clear();
+            }
+            same.add(m);
+            lastDate = currentDate;
+        }
+
+        if (!same.isEmpty()) {
+            result.add(processSameDate(same));
+        }
+
+        return result;
+    }
+
+
+    protected static Measurement processSameDate(List<Measurement> measurements) {
+        int N = measurements.size();
+
+        boolean debug = log.isDebugEnabled();
+        if (debug && N > 0) {
+            log.debug("process same date for Q: " + measurements.get(0).Q());
+        }
+        if (N == 1) {
+            Measurement current = measurements.get(0);
+            double left = current.get("UFERABLINKS");
+            double right = current.get("UFERABST");
+            current.set("EFFWIDTH", left + right);
+        }
+        else {
+            for (int i = 0; i < N; ++i) {
+                Measurement current = measurements.get(i);
+
+                if (i == 0) {
+                    Measurement next = measurements.get(i+1);
+                    double distCurrent = current.get("UFERABST");
+                    double distNext = next.get("UFERABST");
+                    current.set("EFFWIDTH", distNext - distCurrent);
+                }
+                else if (i == N-1) {
+                    Measurement prev = measurements.get(i-1);
+                    double distCurrent = current.get("UFERABST");
+                    double distPrev = prev.get("UFERABST");
+                    current.set("EFFWIDTH", distCurrent - distPrev);
+                }
+                else {
+                    Measurement prev = measurements.get(i-1);
+                    Measurement next = measurements.get(i+1);
+                    double distPrev = prev.get("UFERABST");
+                    double distNext = next.get("UFERABST");
+                    current.set("EFFWIDTH", 0.5*(distNext - distPrev));
+                }
+                if (debug) {
+                    log.debug("effective width: " + current.get("EFFWIDTH"));
+                }
+            }
+        }
+
+        double sumSandF   = 0d;
+        double sumCoarseF = 0d;
+        double sumGravelF = 0d;
+        double sumNorm    = 0d;
+
+        for (Measurement m: measurements) {
+            SieveArray sa   = m.getSieveArray();
+            if (sa.totalLoad() < SieveArray.EPSILON) {
+                continue;
+            }
+            double sandF    = sa.sandNormFraction();
+            double coarseF  = sa.coarseNormFraction();
+            double gravelF  = sa.gravelNormFraction();
+            double effWidth = m.get("EFFWIDTH");
+            double gt       = m.get("GTRIEB");
+            double scale    = effWidth*gt;
+            sumSandF   += scale*sandF;
+            sumCoarseF += scale*coarseF;
+            sumGravelF += scale*gravelF;
+            sumNorm    += scale;
+            if (debug) {
+                log.debug("fractions - s: " +
+                    sandF + " c: " +
+                    coarseF + " g: " +
+                    gravelF);
+                log.debug("scale: " + scale + " = " + effWidth + " * " + gt);
+            }
+        }
+
+        Map<String, Object> data =
+            new HashMap<String, Object>(measurements.get(0).getData());
+
+        Measurement m = new Measurement(data, Collections.<Sieve>emptyList());
+
+        sumNorm = 1d/sumNorm;
+
+        m.set("BL_S", sumNorm*sumSandF);
+        m.set("BL_G", sumNorm*sumGravelF);
+        m.set("BL_C", sumNorm*sumCoarseF);
+        if (debug) {
+            log.debug(
+                "BL_S: " + m.get("BL_S") +
+                " BL_G: " + m.get("BL_G") +
+                " BL_C: " + m.get("BL_C"));
+        }
+        return m;
+    }
+
+
+    private static final boolean equalDate(Date a, Date b) {
+        Calendar ca = Calendar.getInstance();
+        Calendar cb = Calendar.getInstance();
+        ca.setTime(a);
+        cb.setTime(b);
+        return ca.get(Calendar.YEAR)         == cb.get(Calendar.YEAR)
+            && ca.get(Calendar.MONTH)        == cb.get(Calendar.MONTH)
+            && ca.get(Calendar.DAY_OF_MONTH) == cb.get(Calendar.DAY_OF_MONTH);
+    }
+
+
+    protected static Measurement accumulate(List<Measurement> measuments) {
+
+        int N = measuments.size();
+        if (N == 1) {
+            return measuments.get(0);
+        }
+        TreeMap<Double, double []> diameters =
+            new TreeMap<Double, double []>(Sieve.DIAMETER_CMP);
+
+        double sumGTrieb = 0d;
+        for (Measurement m: measuments) {
+            for (Sieve s: m.getSieves()) {
+                Double key = s.getDiameter();
+                double [] sum = diameters.get(key);
+                if (sum == null) {
+                    sum = new double[1];
+                    diameters.put(key, sum);
+                }
+                sum[0] += s.getLoad();
+            }
+            // calculate 'Geschiebetrieb'
+            sumGTrieb += m.get("GTRIEB");
+        }
+        List<Sieve> accumulatedSieves = new ArrayList<Sieve>(diameters.size());
+        for (Map.Entry<Double, double []> entry: diameters.entrySet()) {
+            accumulatedSieves.add(
+                new Sieve(entry.getKey(),
+                    entry.getValue()[0]/N));
+        }
+        Map<String, Object> data =
+            new HashMap<String, Object>(measuments.get(0).getData());
+
+        data.put("GTRIEB", sumGTrieb/N);
+
+        return new Measurement(data, accumulatedSieves);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/Measurements.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,164 @@
+package org.dive4elements.river.artifacts.model.sq;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+public class Measurements
+{
+    private static final Logger log = Logger.getLogger(Measurements.class);
+
+    public interface SExtractor {
+        double getS(Measurement measument);
+    } // interface SExtractor
+
+    public static final SExtractor S_SF_EXTRACTOR = new SExtractor() {
+        @Override
+        public double getS(Measurement measument) {
+            return measument.S_SF();
+        }
+    };
+
+    public static final SExtractor S_SS_EXTRACTOR = new SExtractor() {
+        @Override
+        public double getS(Measurement measument) {
+            return measument.S_SS();
+        }
+    };
+
+    public static final SExtractor S_BL_S_EXTRACTOR = new SExtractor() {
+        @Override
+        public double getS(Measurement measument) {
+            return measument.S_BL_S();
+        }
+    };
+
+    public static final SExtractor S_BL_FG_EXTRACTOR = new SExtractor() {
+        @Override
+        public double getS(Measurement measument) {
+            return measument.S_BL_FG();
+        }
+    };
+
+    public static final SExtractor S_BL_CG_EXTRACTOR = new SExtractor() {
+        @Override
+        public double getS(Measurement measument) {
+            return measument.S_BL_CG();
+        }
+    };
+
+    public static final SExtractor S_BL_EXTRACTOR = new SExtractor() {
+        @Override
+        public double getS(Measurement measument) {
+            return measument.BL_S();
+        }
+    };
+
+    protected List<Measurement> measuments;
+    protected List<Measurement> accumulated;
+
+    public Measurements() {
+    }
+
+    public Measurements(
+        List<Measurement> measuments,
+        List<Measurement> accumulated
+    ) {
+        if (log.isDebugEnabled()) {
+            log.debug("number of measuments: " + measuments.size());
+            log.debug("number of accumulated: " + accumulated.size());
+        }
+        this.measuments = measuments;
+        this.accumulated = accumulated;
+    }
+
+    public static 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());
+            if (sq.isValid()) {
+                result.add(sq);
+            }
+            else {
+                ++invalid;
+            }
+        }
+        if (log.isDebugEnabled()) {
+            log.debug("Removed num invalid: " + invalid
+                + " of " + measuments.size());
+        }
+        return result;
+    }
+
+    public List<SQ> S_SF() {
+        return extractSQ(measuments, S_SF_EXTRACTOR);
+    }
+
+    public List<SQ> S_SS() {
+        return extractSQ(measuments, S_SS_EXTRACTOR);
+    }
+
+    public List<SQ> S_BL_S() {
+        return extractSQ(accumulated, S_BL_S_EXTRACTOR);
+    }
+
+    public List<SQ> S_BL_FG() {
+        return extractSQ(accumulated, S_BL_FG_EXTRACTOR);
+    }
+
+    public List<SQ> S_BL_CG() {
+        return extractSQ(accumulated, S_BL_CG_EXTRACTOR);
+    }
+
+    public List<SQ> S_BL() {
+        return extractSQ(accumulated, S_BL_EXTRACTOR);
+    }
+
+    public List<SQ> getSQs(int index) {
+        switch (index) {
+            case 0: return S_SF();
+            case 1: return S_SS();
+            case 2: return S_BL_S();
+            case 3: return S_BL_FG();
+            case 4: return S_BL_CG();
+            case 5: return S_BL();
+        }
+        log.error("THIS SHOULD NOT HAPPEN: Tried to access SQ[" + index + "]");
+        return new ArrayList<SQ>(0);
+    }
+
+    /**
+     * Gets the accumulated for this instance.
+     *
+     * @return The accumulated.
+     */
+    public List<Measurement> getAccumulated() {
+        return this.accumulated;
+    }
+
+    /**
+     * Sets the accumulated for this instance.
+     *
+     * @param accumulated The accumulated.
+     */
+    public void setAccumulated(List<Measurement> accumulated) {
+        this.accumulated = accumulated;
+    }
+
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder("Measurements [");
+        for (int i = 0, M = measuments.size(); i < M; ++i) {
+            if (i > 0) sb.append(", ");
+            sb.append(measuments.get(i));
+        }
+        return sb.append(']').toString();
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/Outlier.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,93 @@
+package org.dive4elements.river.artifacts.model.sq;
+
+import org.dive4elements.river.artifacts.math.GrubbsOutlier;
+import org.dive4elements.river.artifacts.math.StdDevOutlier;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.math.MathException;
+
+import org.apache.log4j.Logger;
+
+public class Outlier
+{
+    private static Logger log = Logger.getLogger(Outlier.class);
+
+    private static final String GRUBBS = "outlier.method.grubbs";
+
+    //private static final String STD_DEV = "std-dev";
+
+    public interface Callback {
+
+        void initialize(List<SQ> sqs) throws MathException;
+
+        double eval(SQ sq);
+
+        void iterationFinished(
+            double   stdDev,
+            SQ       outlier,
+            List<SQ> remaining);
+
+    } // interface Callback
+
+    public static void detectOutliers(
+        Callback callback,
+        List<SQ> sqs,
+        double   stdDevFactor,
+        String   method
+    )
+    throws MathException
+    {
+        boolean debug = log.isDebugEnabled();
+
+        if (method == null) {
+            method = "std-dev";
+        }
+
+        if (debug) {
+            log.debug("stdDevFactor: " + stdDevFactor);
+            log.debug("method: " + method);
+        }
+
+        List<SQ> data = new ArrayList<SQ>(sqs);
+
+        double [] stdDev = new double[1];
+
+        boolean useGrubbs = method.equals(GRUBBS);
+
+        if (useGrubbs) {
+            stdDevFactor = Math.max(0d, Math.min(stdDevFactor/100d, 1d));
+        }
+
+        List<Double> values = new ArrayList<Double>(data.size());
+
+        while (data.size() > 2) {
+
+            callback.initialize(data);
+
+            for (SQ sq: data) {
+                values.add(callback.eval(sq));
+            }
+
+            Integer ndx = useGrubbs
+                ? GrubbsOutlier.findOutlier(values, stdDevFactor, stdDev)
+                : StdDevOutlier.findOutlier(values, stdDevFactor, stdDev);
+
+            if (ndx == null) {
+                callback.iterationFinished(stdDev[0], null, data);
+                break;
+            }
+
+            SQ outlier = data.remove(ndx.intValue());
+            if (debug) {
+                log.debug("stdDev: " + stdDev[0]);
+                log.debug("removed " + ndx +
+                    "; S: " + outlier.getS() + " Q: " + outlier.getQ());
+            }
+            callback.iterationFinished(stdDev[0], outlier, data);
+            values.clear();
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/SQ.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,41 @@
+package org.dive4elements.river.artifacts.model.sq;
+
+import java.io.Serializable;
+
+
+public class SQ implements Serializable {
+
+    protected double s;
+    protected double q;
+
+    public SQ() {
+    }
+
+    public SQ(double s, double q) {
+        this.s = s;
+        this.q = q;
+    }
+
+
+    public double getS() {
+        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);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/SQCurveFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,103 @@
+package org.dive4elements.river.artifacts.model.sq;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.math.fitting.Function;
+import org.dive4elements.river.artifacts.math.fitting.FunctionFactory;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.DataFacet;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.Parameters;
+
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+
+/**
+ * Facet to show the curve in a sq relation.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class SQCurveFacet extends DataFacet implements FacetTypes {
+
+    private static final Logger log = Logger.getLogger(SQCurveFacet.class);
+
+
+    public static final String FUNCTION = "sq-pow";
+
+
+    private int fractionIdx;
+
+
+    public SQCurveFacet() {
+    }
+
+
+    public SQCurveFacet(
+        int    idx,
+        int    fractionIdx,
+        String name,
+        String description,
+        String hash,
+        String stateId
+    ) {
+        super(idx, name, description, ComputeType.ADVANCE, hash, stateId);
+        this.fractionIdx = fractionIdx;
+    }
+
+
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        log.debug("SQCurveFacet.getData");
+
+        if (!(artifact instanceof FLYSArtifact)) {
+            return null;
+        }
+
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        CalculationResult res = (CalculationResult) flys.compute(
+            context, ComputeType.ADVANCE, false);
+
+        SQResult[]       results = (SQResult[]) res.getData();
+        SQFractionResult result  = results[index].getFraction(fractionIdx);
+
+        Parameters params = result.getParameters();
+
+        if (params == null) {
+            log.debug("no parameters found");
+            return null;
+        }
+
+        Function func = FunctionFactory.getInstance().getFunction(FUNCTION);
+        String[] paramNames = func.getParameterNames();
+
+        double [] coeffs = params.get(0, paramNames);
+
+        if (log.isDebugEnabled()) {
+            for (int i = 0, N = paramNames.length; i < N; i++) {
+                log.debug("retrieved parameter " + paramNames[i] +
+                          " = " + coeffs[i]);
+            }
+        }
+
+        org.dive4elements.river.artifacts.math.Function mf =
+            func.instantiate(coeffs);
+
+        double [] extent = result.getQExtent();
+        return new SQFunction(mf, extent[0], extent[1]);
+    }
+
+
+    @Override
+    public SQCurveFacet deepCopy() {
+        SQCurveFacet copy = new SQCurveFacet();
+        copy.set(this);
+
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/SQFractionResult.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,166 @@
+package org.dive4elements.river.artifacts.model.sq;
+
+import org.dive4elements.river.artifacts.model.Parameters;
+
+import java.io.Serializable;
+
+import java.util.List;
+
+public class SQFractionResult
+implements   Serializable
+{
+    public static class Iteration
+    implements          Serializable
+    {
+        protected Parameters parameters;
+        protected SQ []      measurements;
+        protected SQ []      outliers;
+
+        public Iteration() {
+        }
+
+        public Iteration(
+            Parameters parameters,
+            SQ []      measurements,
+            SQ []      outliers
+        ) {
+            this.parameters   = parameters;
+            this.measurements = measurements;
+            this.outliers     = outliers;
+        }
+
+        public Parameters getParameters() {
+            return parameters;
+        }
+
+        public void setParameters(Parameters parameters) {
+            this.parameters = parameters;
+        }
+
+        public SQ [] getMeasurements() {
+            return measurements;
+        }
+
+        public void setMeasurements(SQ [] measurements) {
+            this.measurements = measurements;
+        }
+
+        public SQ [] getOutliers() {
+            return outliers;
+        }
+
+        public void setOutliers(SQ [] outliers) {
+            this.outliers = outliers;
+        }
+
+        public boolean isValid() {
+            return parameters   != null
+                && measurements != null
+                && outliers     != null;
+        }
+
+        public int numOutliers() {
+            return outliers != null
+                ? outliers.length
+                : 0;
+        }
+
+        public int numMeasurements() {
+            return measurements != null
+                ? measurements.length
+                : 0;
+        }
+    } // class Iteration
+
+    protected SQ []           measurements;
+    protected List<Iteration> iterations;
+
+    public SQFractionResult() {
+    }
+
+    public SQFractionResult(
+        SQ []           measurements,
+        List<Iteration> iterations
+    ) {
+        this.measurements = measurements;
+        this.iterations   = iterations;
+    }
+
+    public SQ [] getMeasurements() {
+        return measurements;
+    }
+
+    public void setMeasurements(SQ [] measurements) {
+        this.measurements = measurements;
+    }
+
+    public List<Iteration> getIterations() {
+        return iterations;
+    }
+
+    public void setIterations(List<Iteration> iterations) {
+        this.iterations = iterations;
+    }
+
+    public double [] getQExtent() {
+        return getQExtent(new double[2]);
+    }
+
+    public double [] getQExtent(double extent[]) {
+        extent[0] =  Double.MAX_VALUE;
+        extent[1] = -Double.MIN_VALUE;
+
+        for (SQ sq: measurements) {
+            double q = sq.getQ();
+            if (q < extent[0]) extent[0] = q;
+            if (q > extent[1]) extent[1] = q;
+        }
+
+        return extent;
+    }
+
+    public int numIterations() {
+        return iterations != null ? iterations.size() : 0;
+    }
+
+    public Parameters getParameters() {
+        return iterations != null && !iterations.isEmpty()
+            ? iterations.get(iterations.size()-1).getParameters()
+            : null;
+    }
+
+    public SQ [] getOutliers(int index) {
+        return index >= 0 && index < iterations.size()
+            ? iterations.get(index).getOutliers()
+            : null;
+    }
+
+    public Parameters getParameters(int index) {
+        return index >= 0 && index < iterations.size()
+            ? iterations.get(index).getParameters()
+            : null;
+    }
+
+    public SQ [] getMeasurements(int index) {
+        return index >= 0 && index < iterations.size()
+            ? iterations.get(index).getMeasurements()
+            : null;
+    }
+
+    public int totalNumOutliers() {
+        int sum = 0;
+        if (iterations != null) {
+            for (Iteration iteration: iterations) {
+                sum += iteration.numOutliers();
+            }
+        }
+        return sum;
+    }
+
+    public int numMeasurements() {
+        return measurements != null
+            ? measurements.length
+            : 0;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/SQFunction.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,36 @@
+package org.dive4elements.river.artifacts.model.sq;
+
+import org.dive4elements.river.artifacts.math.Function;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class SQFunction {
+
+    private Function function;
+
+    private double minQ;
+    private double maxQ;
+
+
+    public SQFunction(Function function, double minQ, double maxQ) {
+        this.function = function;
+        this.minQ     = minQ;
+        this.maxQ     = maxQ;
+    }
+
+
+    public Function getFunction() {
+        return function;
+    }
+
+    public double getMinQ() {
+        return minQ;
+    }
+
+    public double getMaxQ() {
+        return maxQ;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/SQMeasurementFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,73 @@
+package org.dive4elements.river.artifacts.model.sq;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.DataFacet;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+
+/**
+ * Facet to show the outliers in a sq relation.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class SQMeasurementFacet extends DataFacet implements FacetTypes {
+
+    private static final Logger log = Logger.getLogger(SQMeasurementFacet.class);
+
+
+    private int fractionIdx;
+
+
+    public SQMeasurementFacet() {
+    }
+
+
+    public SQMeasurementFacet(
+        int    idx,
+        int    fractionIdx,
+        String name,
+        String description,
+        String hash,
+        String stateId
+    ) {
+        super(idx, name, description, ComputeType.ADVANCE, hash, stateId);
+        this.fractionIdx = fractionIdx;
+    }
+
+
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        log.debug("SQMeasurementFacet.getData");
+
+        if (!(artifact instanceof FLYSArtifact)) {
+            return null;
+        }
+
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        CalculationResult res = (CalculationResult) flys.compute(
+            context, ComputeType.ADVANCE, false);
+
+        SQResult[]       result  = (SQResult[]) res.getData();
+        SQFractionResult fResult = result[index].getFraction(fractionIdx);
+
+        return fResult.getMeasurements();
+    }
+
+
+    @Override
+    public SQMeasurementFacet deepCopy() {
+        SQMeasurementFacet copy = new SQMeasurementFacet();
+        copy.set(this);
+
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/SQOutlierCurveFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,102 @@
+package org.dive4elements.river.artifacts.model.sq;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+import org.dive4elements.river.artifacts.math.fitting.Function;
+import org.dive4elements.river.artifacts.math.fitting.FunctionFactory;
+
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.DataFacet;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.Parameters;
+
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+import org.apache.log4j.Logger;
+
+public class SQOutlierCurveFacet
+extends      DataFacet
+implements   FacetTypes
+{
+    private static final Logger log =
+        Logger.getLogger(SQOutlierCurveFacet.class);
+
+    public static final String FUNCTION = "sq-pow";
+
+    public static final int BITMASK_ITERATION = (1 << 16) - 1;
+
+    private int fractionIdx;
+
+    public SQOutlierCurveFacet() {
+    }
+
+    public SQOutlierCurveFacet(
+        int    idx,
+        int    fractionIdx,
+        String name,
+        String description,
+        String hash,
+        String stateId
+    ) {
+        super(idx, name, description, ComputeType.ADVANCE, hash, stateId);
+        this.fractionIdx = fractionIdx;
+    }
+
+
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        log.debug("SQOutlierCurveFacet.getData");
+
+        if (!(artifact instanceof FLYSArtifact)) {
+            return null;
+        }
+
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        CalculationResult res = (CalculationResult) flys.compute(
+            context, ComputeType.ADVANCE, false);
+
+        int idx  = this.index >> 16;
+        int iter = this.index & BITMASK_ITERATION;
+
+        SQResult[]       results = (SQResult[]) res.getData();
+        SQFractionResult result  = results[idx].getFraction(fractionIdx);
+
+        Parameters params = result.getParameters(iter);
+
+        if (params == null) {
+            return null;
+        }
+
+        Function func = FunctionFactory.getInstance().getFunction(FUNCTION);
+        String [] paramNames = func.getParameterNames();
+
+        double [] coeffs = params.get(0, paramNames);
+
+        if (log.isDebugEnabled()) {
+            for (int i = 0; i < paramNames.length; i++) {
+                log.debug("retrieved parameter " + paramNames[i] +
+                          " = " + coeffs[i]);
+            }
+        }
+
+        org.dive4elements.river.artifacts.math.Function mf =
+            func.instantiate(coeffs);
+
+        double [] extent = result.getQExtent();
+        return new SQFunction(mf, extent[0], extent[1]);
+    }
+
+    @Override
+    public Facet deepCopy() {
+        SQOutlierCurveFacet copy = new SQOutlierCurveFacet();
+        copy.set(this);
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/SQOutlierFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,97 @@
+package org.dive4elements.river.artifacts.model.sq;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.DataFacet;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+import org.apache.log4j.Logger;
+
+/**
+ * Facet to show the curve in a sq relation.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class SQOutlierFacet extends DataFacet implements FacetTypes {
+
+    private static final Logger log = Logger.getLogger(SQOutlierFacet.class);
+
+    public static final int BITMASK_ITERATION = (1 << 16) - 1;
+
+    private int fractionIdx;
+
+    public SQOutlierFacet() {
+    }
+
+    public SQOutlierFacet(
+        int    idx,
+        int    fractionIdx,
+        String name,
+        String description,
+        String hash,
+        String stateId
+    ) {
+        super(idx, name, description, ComputeType.ADVANCE, hash, stateId);
+        this.fractionIdx = fractionIdx;
+    }
+
+
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        log.debug("SQOutlierFacet.getData");
+
+        if (!(artifact instanceof FLYSArtifact)) {
+            return null;
+        }
+
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        CalculationResult res = (CalculationResult) flys.compute(
+            context, ComputeType.ADVANCE, false);
+
+        int idx  = this.index >> 16;
+        int iter = this.index & BITMASK_ITERATION;
+
+        boolean debug = log.isDebugEnabled();
+
+        if (debug) {
+            log.debug("Fetch data for index : " + this.index);
+            log.debug("  > index:       " + idx);
+            log.debug("  > fraction:    " + fractionIdx);
+            log.debug("  > iteration:   " + iter);
+        }
+
+        SQResult[]       result  = (SQResult[]) res.getData();
+        SQFractionResult fResult = result[idx].getFraction(fractionIdx);
+
+        if (fResult == null) {
+            log.warn("No SQFractionResult at " + idx + "|" + fractionIdx);
+            return null;
+        }
+
+        SQ [] outliers = fResult.getOutliers(iter);
+
+        if (debug) {
+            int num = outliers != null ? outliers.length : 0;
+            log.debug("Found " + num + " outliers for iteration " + iter);
+        }
+
+        return outliers;
+    }
+
+
+    @Override
+    public SQOutlierFacet deepCopy() {
+        SQOutlierFacet copy = new SQOutlierFacet();
+        copy.set(this);
+
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/SQOutlierMeasurementFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,73 @@
+package org.dive4elements.river.artifacts.model.sq;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.DataFacet;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+import org.apache.log4j.Logger;
+
+public class SQOutlierMeasurementFacet
+extends      DataFacet
+implements   FacetTypes
+{
+    private static final Logger log =
+        Logger.getLogger(SQOutlierMeasurementFacet.class);
+
+    private int fractionIdx;
+
+    public static final int BITMASK_ITERATION = (1 << 16) - 1;
+
+    public SQOutlierMeasurementFacet() {
+    }
+
+    public SQOutlierMeasurementFacet(
+        int    idx,
+        int    fractionIdx,
+        String name,
+        String description,
+        String hash,
+        String stateId
+    ) {
+        super(idx, name, description, ComputeType.ADVANCE, hash, stateId);
+        this.fractionIdx = fractionIdx;
+    }
+
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        log.debug("SQOutlierMeasurementFacet.getData");
+
+        if (!(artifact instanceof FLYSArtifact)) {
+            return null;
+        }
+
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        CalculationResult res = (CalculationResult) flys.compute(
+            context, ComputeType.ADVANCE, false);
+
+        int idx  = this.index >> 16;
+        int iter = this.index & BITMASK_ITERATION;
+
+        SQResult[]       result  = (SQResult[]) res.getData();
+        SQFractionResult fResult = result[idx].getFraction(fractionIdx);
+
+        return fResult.getMeasurements(iter);
+    }
+
+    @Override
+    public Facet deepCopy() {
+        SQOutlierMeasurementFacet copy = new SQOutlierMeasurementFacet();
+        copy.set(this);
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/SQOverviewFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,61 @@
+package org.dive4elements.river.artifacts.model.sq;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.model.DataFacet;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+
+public class SQOverviewFacet
+extends      DataFacet
+implements   FacetTypes
+{
+    private static final Logger logger = Logger.getLogger(SQOverviewFacet.class);
+
+    protected int fractionIdx;
+
+    public SQOverviewFacet() {
+    }
+
+    public SQOverviewFacet(
+        int    idx,
+        int    fractionIdx,
+        String name,
+        String description,
+        String hash,
+        String stateId
+    ) {
+        super(idx, name, description, ComputeType.ADVANCE, hash, stateId);
+        this.fractionIdx = fractionIdx;
+    }
+
+
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        logger.debug("SQOverviewFacet.getData");
+
+        switch(fractionIdx) {
+            case 0: return "sq_relation_a";
+            case 1: return "sq_relation_b";
+            case 2: return "sq_relation_c";
+            case 3: return "sq_relation_d";
+            case 4: return "sq_relation_e";
+            case 5: return "sq_relation_f";
+            default: return null;
+        }
+    }
+
+
+    @Override
+    public SQOverviewFacet deepCopy() {
+        SQOverviewFacet copy = new SQOverviewFacet();
+        copy.set(this);
+
+        return copy;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/SQRelationCalculation.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,194 @@
+package org.dive4elements.river.artifacts.model.sq;
+
+import org.dive4elements.river.artifacts.access.SQRelationAccess;
+
+import org.dive4elements.river.artifacts.math.fitting.Function;
+import org.dive4elements.river.artifacts.math.fitting.FunctionFactory;
+
+import org.dive4elements.river.artifacts.model.Calculation;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.DateRange;
+import org.dive4elements.river.artifacts.model.Parameters;
+
+import org.dive4elements.river.backend.SedDBSessionHolder;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+public class SQRelationCalculation extends Calculation {
+
+    private static final Logger log =
+        Logger.getLogger(SQRelationCalculation.class);
+
+    public static final String SQ_FUNCTION_NAME = "sq-pow";
+
+    protected String    river;
+    protected double    location;
+    protected DateRange period;
+    protected double    outliers;
+    private   String    method;
+
+    public SQRelationCalculation() {
+    }
+
+    public SQRelationCalculation(SQRelationAccess access) {
+
+        String    river    = access.getRiver();
+        Double    location = access.getLocation();
+        DateRange period   = access.getPeriod();
+        Double    outliers = access.getOutliers();
+        String    method   = access.getOutlierMethod();
+
+        //river = "Rhein";
+
+        if (river == null) {
+            // TODO: i18n
+            addProblem("sq.missing.river");
+        }
+
+        if (location == null) {
+            // TODO: i18n
+            addProblem("sq.missing.location");
+        }
+
+        if (period == null) {
+            // TODO: i18n
+            addProblem("sq.missing.periods");
+        }
+
+        if (outliers == null) {
+            // TODO: i18n
+            addProblem("sq.missing.outliers");
+        }
+
+        if (method == null) {
+            //TODO: i18n
+            addProblem("sq.missing.method");
+        }
+
+        if (!hasProblems()) {
+            this.river    = river;
+            this.location = location;
+            this.period   = period;
+            this.outliers = outliers;
+            this.method   = method;
+        }
+    }
+
+
+    public CalculationResult calculate() {
+        log.debug("SQRelationCalculation.calculate");
+
+        if (hasProblems()) {
+            return new CalculationResult(this);
+        }
+
+        SedDBSessionHolder.acquire();
+        try {
+            return internalCalculate();
+        }
+        finally {
+            SedDBSessionHolder.release();
+        }
+    }
+
+    protected CalculationResult internalCalculate() {
+
+        Function function = FunctionFactory
+            .getInstance()
+            .getFunction(SQ_FUNCTION_NAME);
+
+        if (function == null) {
+            log.error("No '" + SQ_FUNCTION_NAME + "' function found.");
+            // TODO: i18n
+            addProblem("sq.missing.sq.function");
+        }
+
+        Measurements measurements =
+            MeasurementFactory.getMeasurements(river, location, period);
+
+        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);
+
+            if (iterations == null) {
+                // TODO: i18n
+                addProblem("sq.fitting.failed." + i);
+                fractionResult = new SQFractionResult();
+            }
+            else {
+                fractionResult = new SQFractionResult(
+                    sqs.toArray(new SQ[sqs.size()]),
+                    iterations);
+            }
+
+            fractionResults[i] = fractionResult;
+        }
+
+        return new CalculationResult(
+            new SQResult[] { new SQResult(location, fractionResults) },
+            this);
+    }
+
+    protected List<SQFractionResult.Iteration> doFitting(
+        final Function function,
+        List<SQ> sqs
+    ) {
+        final List<SQFractionResult.Iteration> iterations =
+            new ArrayList<SQFractionResult.Iteration>();
+
+        boolean success = new Fitting(function, outliers).fit(
+            sqs,
+            method,
+            new Fitting.Callback() {
+                @Override
+                public void afterIteration(
+                    double [] coeffs,
+                    SQ []     measurements,
+                    SQ []     outliers,
+                    double    standardDeviation,
+                    double    chiSqr
+                ) {
+                    Parameters parameters = createParameters(
+                        function.getParameterNames(),
+                        coeffs,
+                        standardDeviation,
+                        chiSqr);
+                    iterations.add(new SQFractionResult.Iteration(
+                        parameters,
+                        measurements,
+                        outliers));
+                }
+            });
+
+        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;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/SQResult.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,60 @@
+package org.dive4elements.river.artifacts.model.sq;
+
+import java.io.Serializable;
+
+
+
+public class SQResult implements Serializable {
+
+    public static final int NUMBER_FRACTIONS = 6;
+
+    public static final int FRACTION_A = 0;
+    public static final int FRACTION_B = 1;
+    public static final int FRACTION_C = 2;
+    public static final int FRACTION_D = 3;
+    public static final int FRACTION_E = 4;
+    public static final int FRACTION_F = 5;
+
+    protected double km;
+    protected SQFractionResult[] fractions;
+
+    public SQResult() {
+        this(0d, new SQFractionResult[NUMBER_FRACTIONS]);
+    }
+
+    public SQResult(double km, SQFractionResult [] fractions) {
+        this.km        = km;
+        this.fractions = fractions;
+    }
+
+    public SQFractionResult getFraction(int idx) {
+        return idx >= 0 && idx < fractions.length
+            ? fractions[idx]
+            : null;
+    }
+
+    public void setFraction(int idx, SQFractionResult fraction) {
+        if (idx >= 0 && idx < fractions.length) {
+            this.fractions[idx] = fraction;
+        }
+    }
+
+    public static final String [] FRACTION_NAMES = {
+        "A", "B", "C", "D", "E", "F"
+    };
+
+    public String getFractionName(int idx) {
+        return idx >= 0 && idx < FRACTION_NAMES.length
+            ? FRACTION_NAMES[idx]
+            : "";
+    }
+
+    public double getKm() {
+        return km;
+    }
+
+    public void setKm(double km) {
+        this.km = km;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/Sieve.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,79 @@
+package org.dive4elements.river.artifacts.model.sq;
+
+import java.util.Comparator;
+
+public class Sieve
+{
+    public static final double EPSILON = 1e-6;
+
+    public static final Comparator<Double> DIAMETER_CMP =
+        new Comparator<Double>() {
+            @Override
+            public int compare(Double a, Double b) {
+                double diff = a - b;
+                if (diff < -EPSILON) return -1;
+                if (diff >  EPSILON) return +1;
+                return 0;
+            }
+        };
+
+    protected double diameter;
+    protected double load;
+
+    /**
+     * Constructs a new instance.
+     */
+    public Sieve() {
+        this(Double.NaN, Double.NaN);
+    }
+
+    public Sieve(double diameter, double load) {
+        this.diameter = diameter;
+        this.load = load;
+    }
+
+    /**
+     * Gets the diameter for this instance.
+     *
+     * @return The diameter.
+     */
+    public double getDiameter() {
+        return this.diameter;
+    }
+
+    /**
+     * Sets the diameter for this instance.
+     *
+     * @param diameter The diameter.
+     */
+    public void setDiameter(double diameter) {
+        this.diameter = diameter;
+    }
+
+    /**
+     * Gets the load for this instance.
+     *
+     * @return The load.
+     */
+    public double getLoad() {
+        return this.load;
+    }
+
+    /**
+     * Sets the load for this instance.
+     *
+     * @param load The load.
+     */
+    public void setLoad(double load) {
+        this.load = load;
+    }
+
+    public boolean matchesDiameter(double diameter) {
+        return Math.abs(diameter - this.diameter) < EPSILON;
+    }
+
+    public boolean hasDiameter() {
+        return !Double.isNaN(diameter);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/SieveArray.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,129 @@
+package org.dive4elements.river.artifacts.model.sq;
+
+import java.util.Arrays;
+
+import org.apache.log4j.Logger;
+
+public class SieveArray
+{
+    private static final Logger log =
+        Logger.getLogger(SieveArray.class);
+
+    public static final double EPSILON = 1e-8;
+
+    public static final double [] SIEVE_DIAMETERS = {
+        100d,   63d,  31.5d,    16d,
+          8d,    4d,     2d,     1d,
+        0.5d, 0.25d, 0.125d, 0.063d
+    };
+
+    protected double [] loads;
+    protected double [] normLoads;
+
+    public SieveArray() {
+        loads = new double[SIEVE_DIAMETERS.length+1];
+        normLoads = new double[SIEVE_DIAMETERS.length+1];
+    }
+
+    public void doSieving(Sieve s) {
+
+        double diameter = s.getDiameter();
+
+        for (int i = 0; i < SIEVE_DIAMETERS.length; ++i) {
+            if (diameter >= SIEVE_DIAMETERS[i]) {
+                loads[i] += s.getLoad();
+                return;
+            }
+        }
+        loads[loads.length-1] += s.getLoad();
+    }
+
+    public double totalLoad() {
+        double sum = 0d;
+        for (double load: loads) {
+            sum += load;
+        }
+        return sum;
+    }
+
+    public void calculateNormLoads() {
+        double total = totalLoad();
+        if (Math.abs(total) < EPSILON) {
+            System.arraycopy(loads, 0, normLoads, 0, loads.length);
+            return;
+        }
+        total = 1d/total;
+        for (int i = 0; i < normLoads.length; ++i) {
+            normLoads[i] = total*loads[i];
+        }
+        log.debug("calculated norm loads: " + Arrays.toString(normLoads));
+    }
+
+    public void adjust(double eight, double four) {
+        this.normLoads[4] = eight;
+        this.normLoads[5] = four;
+    }
+
+    /**
+     * Gets the loads for this instance.
+     *
+     * @return The loads.
+     */
+    public double[] getLoads() {
+        return this.loads;
+    }
+
+    /**
+     * Gets the loads for this instance.
+     *
+     * @param index The index to get.
+     * @return The loads.
+     */
+    public double getLoads(int index) {
+        return this.loads[index];
+    }
+
+    /**
+     * Gets the normLoads for this instance.
+     *
+     * @return The normLoads.
+     */
+    public double[] getNormLoads() {
+        return this.normLoads;
+    }
+
+    /**
+     * Gets the normLoads for this instance.
+     *
+     * @param index The index to get.
+     * @return The normLoads.
+     */
+    public double getNormLoads(int index) {
+        return this.normLoads[index];
+    }
+
+    public double sandNormFraction() {
+        double sum = 0d;
+        for (int i = 7; i < normLoads.length; ++i) {
+            sum += normLoads[i];
+        }
+        return sum;
+    }
+
+    public double coarseNormFraction() {
+        double sum = 0d;
+        for (int i = 0; i < 4; ++i) {
+            sum += normLoads[i];
+        }
+        return sum;
+    }
+
+    public double gravelNormFraction() {
+        double sum = 0d;
+        for (int i = 4; i < 7; ++i) {
+            sum += normLoads[i];
+        }
+        return sum;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/StaticSQCacheKey.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,30 @@
+package org.dive4elements.river.artifacts.model.sq;
+
+import java.io.Serializable;
+
+
+public class StaticSQCacheKey
+implements Serializable
+{
+    public static final String CACHE_NAME = "static-sq-relation";
+
+    private String river;
+    private int measurementId;
+
+    public StaticSQCacheKey(String river, int measurementId) {
+        this.river = river;
+        this.measurementId = measurementId;
+    }
+
+    public int hashCode() {
+        return this.river.hashCode() | measurementId;
+    }
+
+    public boolean equals(Object other) {
+        if (!(other instanceof StaticSQCacheKey)) {
+            return false;
+        }
+        StaticSQCacheKey o = (StaticSQCacheKey) other;
+        return this.river == o.river && this.measurementId == o.measurementId;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/StaticSQContainer.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,85 @@
+package org.dive4elements.river.artifacts.model.sq;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class StaticSQContainer
+{
+    private String description;
+    private String stationName;
+    private double km;
+
+    private List<StaticSQRelation> relations;
+
+
+    public StaticSQContainer() {
+        relations = new ArrayList<StaticSQRelation>();
+    }
+
+    public StaticSQContainer(
+        String stationName,
+        String description,
+        double km
+    ) {
+        this.stationName = stationName;
+        this.description = description;
+        this.km = km;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public String getStationName() {
+        return stationName;
+    }
+
+    public void setStationName(String stationName) {
+        this.stationName = stationName;
+    }
+
+    public double getKm() {
+        return km;
+    }
+
+    public void setKm(double km) {
+        this.km = km;
+    }
+
+    public List<StaticSQRelation> getSQRelations() {
+        return relations;
+    }
+
+    public void setSQRelations(List<StaticSQRelation> relations) {
+        this.relations = relations;
+    }
+
+    public void addSQRelation(StaticSQRelation relation) {
+        this.relations.add(relation);
+    }
+
+    public StaticSQRelation getSQRelation(int ndx) {
+        return this.relations.get(ndx);
+    }
+
+    public int size() {
+        return this.relations.size();
+    }
+
+    public List<StaticSQRelation> getRelationsByParameter(
+        StaticSQRelation.Parameter parameter
+    ) {
+        List<StaticSQRelation> result = new ArrayList<StaticSQRelation>();
+        for (StaticSQRelation relation : relations) {
+            if (relation.getParameter() == parameter) {
+                result.add(relation);
+            }
+        }
+        return result;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/StaticSQFactory.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,123 @@
+package org.dive4elements.river.artifacts.model.sq;
+
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.Element;
+
+import org.apache.log4j.Logger;
+import org.hibernate.Query;
+import org.hibernate.Session;
+
+import org.dive4elements.river.artifacts.cache.CacheFactory;
+import org.dive4elements.river.backend.SessionHolder;
+
+
+public class StaticSQFactory
+{
+    private static final Logger log =
+        Logger.getLogger(StaticSQFactory.class);
+
+    public static final String SQL_SQ =
+        "SELECT " +
+            "sq.description AS description,"+
+            "ti.start_time  AS start_time," +
+            "ti.stop_time    AS stop_time, " +
+            "ms.name AS station_name, " +
+            "ms.station AS station_km, " +
+            "ms.measurement_type AS measurement_type, " +
+            "sqv.parameter AS parameter, " +
+            "sqv.a AS a, " +
+            "sqv.b AS b, " +
+            "sqv.qmax AS qmax " +
+        "FROM sq_relation sq " +
+            "JOIN time_intervals ti ON ti.id   = sq.time_interval_id " +
+            "JOIN rivers r ON r.id = sq.river_id " +
+            "JOIN sq_relation_value sqv ON sqv.sq_relation_id = sq.id " +
+            "JOIN measurement_station ms ON sqv.measurement_station_id = ms.id " +
+        "WHERE " +
+            "r.name = :river " +
+            "AND ms.id = :ms_id ";
+
+
+    private StaticSQFactory() {
+    }
+
+    public static StaticSQContainer getSQRelations(
+        String river,
+        int measurementStation
+    ) {
+        Cache cache = CacheFactory.getCache(StaticSQCacheKey.CACHE_NAME);
+
+        StaticSQCacheKey cacheKey;
+
+        if (cache != null) {
+            cacheKey = new StaticSQCacheKey(river, measurementStation);
+            Element element = cache.get(cacheKey);
+            if (element != null) {
+                log.debug("Got static bedheight values from cache");
+                return (StaticSQContainer)element.getValue();
+            }
+        }
+        else {
+            cacheKey = null;
+        }
+
+        StaticSQContainer values = getUncached(river, measurementStation);
+
+        if (values != null && cacheKey != null) {
+            log.debug("Store static sq relations in cache.");
+            Element element = new Element(cacheKey, values);
+            cache.put(element);
+        }
+        return values;
+    }
+
+    private static StaticSQContainer getUncached(
+        String river,
+        int measurementStation
+    ) {
+        Session session = SessionHolder.HOLDER.get();
+
+        Query query = session.createSQLQuery(SQL_SQ)
+            .addScalar("description")
+            .addScalar("start_time")
+            .addScalar("stop_time")
+            .addScalar("station_name")
+            .addScalar("station_km")
+            .addScalar("measurement_type")
+            .addScalar("parameter")
+            .addScalar("a")
+            .addScalar("b")
+            .addScalar("qmax");
+
+        query.setParameter("river", river);
+        query.setParameter("ms_id", measurementStation);
+
+        List<Object []> list = query.list();
+
+        if (list.isEmpty()) {
+            return new StaticSQContainer();
+        }
+
+        StaticSQContainer sq = new StaticSQContainer();
+        sq.setDescription((String)list.get(0)[0]);
+        sq.setStationName((String)list.get(0)[3]);
+        sq.setKm(((BigDecimal)list.get(0)[4]).doubleValue());
+
+        for (Object[] row : list) {
+            StaticSQRelation relation = new StaticSQRelation();
+            relation.setStartTime((Date)row[1]);
+            relation.setStopTime((Date)row[2]);
+            relation.setType((String)row[5]);
+            relation.setParameter((String)row[6]);
+            relation.setA(((BigDecimal)row[7]).doubleValue());
+            relation.setB(((BigDecimal)row[8]).doubleValue());
+            relation.setQmax(((BigDecimal)row[9]).doubleValue());
+            sq.addSQRelation(relation);
+        }
+        return sq;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/StaticSQRelation.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,103 @@
+package org.dive4elements.river.artifacts.model.sq;
+
+import java.io.Serializable;
+import java.util.Date;
+
+
+public class StaticSQRelation implements Serializable{
+
+    private Date startTime;
+    private Date stopTime;
+    private String type;
+    private Parameter parameter;
+    private double a;
+    private double b;
+    private double qmax;
+
+    public static enum Parameter {
+        A, B, C, D, E, F
+    }
+
+
+    public StaticSQRelation() {
+    }
+
+    public StaticSQRelation(
+        Date startTime,
+        Date stopTime,
+        String type,
+        Parameter parameter,
+        double a,
+        double b
+    ) {
+        this.startTime = startTime;
+        this.stopTime = stopTime;
+        this.type = type;
+        this.parameter = parameter;
+        this.a = a;
+        this.b = b;
+    }
+
+    public Date getStartTime() {
+        return startTime;
+    }
+
+    public void setStartTime(Date startTime) {
+        this.startTime = startTime;
+    }
+
+    public Date getStopTime() {
+        return stopTime;
+    }
+
+    public void setStopTime(Date stopTime) {
+        this.stopTime = stopTime;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public Parameter getParameter() {
+        return parameter;
+    }
+
+    public void setParameter(Parameter parameter) {
+        this.parameter = parameter;
+    }
+
+    public void setParameter(String parameter) {
+        if (parameter == null) {
+            return;
+        }
+        this.parameter = Parameter.valueOf(parameter);
+    }
+
+    public double getA() {
+        return a;
+    }
+
+    public void setA(double a) {
+        this.a = a;
+    }
+
+    public double getB() {
+        return b;
+    }
+
+    public void setB(double b) {
+        this.b = b;
+    }
+
+    public double getQmax() {
+        return qmax;
+    }
+
+    public void setQmax(double qmax) {
+        this.qmax = qmax;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/resources/Resources.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,179 @@
+package org.dive4elements.river.artifacts.resources;
+
+import java.text.MessageFormat;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.CallMeta;
+
+/**
+ * This class provides methods for i18n.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class Resources {
+
+    /** The logger that is used in this class.*/
+    private static Logger logger = Logger.getLogger(Resources.class);
+
+    /** The singleton instance.*/
+    private static Resources INSTANCE;
+
+    /** The locales supported by this server.*/
+    protected Locale[] locales;
+
+    /**
+     * No instance of this class is necessary.
+     */
+    private Resources() {
+    }
+
+
+    /**
+     * Returns the locales supported by this server.
+     *
+     * @return the supported locales.
+     */
+    public synchronized Locale [] getLocales() {
+        if (locales == null) {
+            readLocales();
+        }
+
+        return locales;
+    }
+
+
+    /**
+     * Read the locales configured for this server.
+     */
+    protected void readLocales() {
+        // TODO IMPLEMENT ME
+
+        locales = new Locale[2];
+        locales[0] = Locale.GERMANY;
+        locales[1] = Locale.ENGLISH;
+    }
+
+
+    private static synchronized void ensureInstance() {
+        if (INSTANCE == null) {
+            INSTANCE = new Resources();
+        }
+    }
+
+
+    public static Locale getLocale(CallMeta meta) {
+        ensureInstance();
+
+        Locale[] locales = INSTANCE.getLocales();
+        return meta.getPreferredLocale(locales);
+    }
+
+
+    /**
+     * This method returns the translated value for <i>key</i> or <i>def</i> if
+     * <i>key</i> is not existing in the resource bundle.
+     *
+     * @param meta The CallMeta object of the request that contains the
+     * preferred locale.
+     * @param key The key that should be translated.
+     * @param def A default value that is returned, if <i>key</i> was not found.
+     *
+     * @return the translated message.
+     */
+    public static String getMsg(CallMeta meta, String key, String def) {
+        ensureInstance();
+
+        Locale[] locales = INSTANCE.getLocales();
+        Locale   locale  = meta.getPreferredLocale(locales);
+
+        return getMsg(locale, key, def);
+    }
+
+    public static String getMsg(
+            CallMeta meta,
+            String   key,
+            Object[] args
+            ) {
+        return getMsg(meta, key, key, args);
+    }
+
+    /**
+     * Returns a translated message based on a template specified by <i>key</i>
+     * that has necessary values to be filled in.
+     *
+     * @param meta The CallMeta object.
+     * @param key The key of the template in the resource bundle.
+     * @param def the default value if no template was found with <i>key</i>.
+     * @param args The arguments that are necessary for the template.
+     *
+     * @return a translated string.
+     */
+    public static String getMsg(
+            CallMeta meta,
+            String   key,
+            String   def,
+            Object[] args)
+    {
+        String template = getMsg(meta, key, (String)null);
+
+        if (template == null) {
+            return def;
+        }
+
+        return format(meta, template, args);
+    }
+
+    public static String format(
+            CallMeta   meta,
+            String     key,
+            String     def,
+            Object ... args
+            ) {
+        String template = getMsg(meta, key, (String)null);
+
+        if (template == null) {
+            template = def;
+        }
+
+        return format(meta, template, args);
+    }
+
+    /**
+     * Formats the given template using the arguments with respect of the
+     * appropriate locale given by the CallMeta instance.
+     */
+    public static String format(CallMeta meta, String templ, Object ... args) {
+        Locale locale = getLocale(meta);
+        MessageFormat mf = new MessageFormat(templ, locale);
+
+        return mf.format(args, new StringBuffer(), null).toString();
+    }
+
+    /**
+     * This method returns the translated value for <i>key</i> or <i>def</i> if
+     * <i>key</i> is not existing in the resource bundle.
+     *
+     * @param locale The locale.
+     * @param key The key that should be translated.
+     * @param def A default value that is returned, if <i>key</i> was not found.
+     *
+     * @return the translated message.
+     */
+    public static String getMsg(Locale locale, String key, String def) {
+        ResourceBundle bundle = ResourceBundle.getBundle("messages", locale);
+
+        try {
+            return bundle.getString(key);
+        }
+        catch (MissingResourceException mre) {
+            logger.warn("No message found for key: " + key);
+
+            return def;
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/AbstractChartService.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,162 @@
+package org.dive4elements.river.artifacts.services;
+
+import java.awt.Dimension;
+import java.awt.Transparency;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import javax.imageio.ImageIO;
+
+import org.apache.log4j.Logger;
+import org.jfree.chart.JFreeChart;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+import org.dive4elements.artifactdatabase.DefaultService;
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.artifacts.GlobalContext;
+import org.dive4elements.artifacts.Service;
+
+/** Serve chart. */
+public abstract class AbstractChartService extends DefaultService {
+
+    public static final int DEFAULT_WIDTH = 240;
+    public static final int DEFAULT_HEIGHT = 180;
+
+    public static final String DEFAULT_FORMAT = "png";
+
+    private static final Logger log = Logger
+        .getLogger(AbstractChartService.class);
+
+    // TODO: Load fancy image from resources.
+    public static final byte[] EMPTY = { (byte) 0x89, (byte) 0x50, (byte) 0x4e,
+        (byte) 0x47, (byte) 0x0d, (byte) 0x0a, (byte) 0x1a, (byte) 0x0a,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x0d, (byte) 0x49,
+        (byte) 0x48, (byte) 0x44, (byte) 0x52, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x01, (byte) 0x08, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x3a, (byte) 0x7e, (byte) 0x9b, (byte) 0x55,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x73,
+        (byte) 0x52, (byte) 0x47, (byte) 0x42, (byte) 0x00, (byte) 0xae,
+        (byte) 0xce, (byte) 0x1c, (byte) 0xe9, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x09, (byte) 0x70, (byte) 0x48, (byte) 0x59,
+        (byte) 0x73, (byte) 0x00, (byte) 0x00, (byte) 0x0b, (byte) 0x13,
+        (byte) 0x00, (byte) 0x00, (byte) 0x0b, (byte) 0x13, (byte) 0x01,
+        (byte) 0x00, (byte) 0x9a, (byte) 0x9c, (byte) 0x18, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x07, (byte) 0x74, (byte) 0x49,
+        (byte) 0x4d, (byte) 0x45, (byte) 0x07, (byte) 0xdc, (byte) 0x04,
+        (byte) 0x04, (byte) 0x10, (byte) 0x30, (byte) 0x15, (byte) 0x7d,
+        (byte) 0x77, (byte) 0x36, (byte) 0x0b, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x08, (byte) 0x74, (byte) 0x45, (byte) 0x58,
+        (byte) 0x74, (byte) 0x43, (byte) 0x6f, (byte) 0x6d, (byte) 0x6d,
+        (byte) 0x65, (byte) 0x6e, (byte) 0x74, (byte) 0x00, (byte) 0xf6,
+        (byte) 0xcc, (byte) 0x96, (byte) 0xbf, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x0a, (byte) 0x49, (byte) 0x44, (byte) 0x41,
+        (byte) 0x54, (byte) 0x08, (byte) 0xd7, (byte) 0x63, (byte) 0xf8,
+        (byte) 0x0f, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x01,
+        (byte) 0x00, (byte) 0x1b, (byte) 0xb6, (byte) 0xee, (byte) 0x56,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x49,
+        (byte) 0x45, (byte) 0x4e, (byte) 0x44, (byte) 0xae, (byte) 0x42,
+        (byte) 0x60, (byte) 0x82 };
+
+    private static final Output empty() {
+        return new Output(EMPTY, "image/png");
+    }
+
+    protected abstract JFreeChart createChart(Document data,
+        GlobalContext globalContext, CallMeta callMeta);
+
+    protected void init() {
+    };
+
+    protected void finish() {
+    };
+
+    @Override
+    public Service.Output process(Document data, GlobalContext globalContext,
+        CallMeta callMeta) {
+        log.debug("process");
+
+        init();
+        try {
+            JFreeChart chart = createChart(data, globalContext, callMeta);
+
+            if (chart == null) {
+                return empty();
+            }
+
+            Dimension extent = getExtent(data);
+            String format = getFormat(data);
+
+            return encode(chart, extent, format);
+        }
+        finally {
+            finish();
+        }
+    }
+
+    protected static Output encode(JFreeChart chart, Dimension extent,
+        String format) {
+        BufferedImage image = chart.createBufferedImage(extent.width,
+            extent.height, Transparency.BITMASK, null);
+
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+        try {
+            ImageIO.write(image, format, out);
+        }
+        catch (IOException ioe) {
+            log.warn("writing image failed", ioe);
+            return empty();
+        }
+
+        return new Output(out.toByteArray(), "image/" + format);
+    }
+
+    protected static Dimension getExtent(Document input) {
+
+        int width = DEFAULT_WIDTH;
+        int height = DEFAULT_HEIGHT;
+
+        NodeList extents = input.getElementsByTagName("extent");
+
+        if (extents.getLength() > 0) {
+            Element element = (Element) extents.item(0);
+            String w = element.getAttribute("width");
+            String h = element.getAttribute("height");
+
+            try {
+                width = Math.max(1, Integer.parseInt(w));
+            }
+            catch (NumberFormatException nfe) {
+                log.warn("width '" + w + "' is not a valid.");
+            }
+
+            try {
+                height = Math.max(1, Integer.parseInt(h));
+            }
+            catch (NumberFormatException nfe) {
+                log.warn("height '" + h + "' is not a valid");
+            }
+        }
+
+        return new Dimension(width, height);
+    }
+
+    protected static String getFormat(Document input) {
+        String format = DEFAULT_FORMAT;
+
+        NodeList formats = input.getElementsByTagName("format");
+
+        if (formats.getLength() > 0) {
+            String type = ((Element) formats.item(0)).getAttribute("type");
+            if (type.length() > 0) {
+                format = type;
+            }
+        }
+
+        return format;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/BedKMChartService.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,302 @@
+package org.dive4elements.river.artifacts.services;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Transparency;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Date;
+import java.util.List;
+
+import javax.imageio.ImageIO;
+
+import org.apache.log4j.Logger;
+import org.jfree.chart.ChartFactory;
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.axis.DateAxis;
+import org.jfree.chart.plot.PlotOrientation;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
+import org.jfree.data.xy.XYSeries;
+import org.jfree.data.xy.XYSeriesCollection;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+import org.dive4elements.artifactdatabase.DefaultService;
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.artifacts.GlobalContext;
+import org.dive4elements.artifacts.Service;
+import org.dive4elements.river.artifacts.model.minfo.BedOverview;
+import org.dive4elements.river.artifacts.model.minfo.BedOverviewFactory;
+import org.dive4elements.river.artifacts.resources.Resources;
+import org.dive4elements.river.backend.SedDBSessionHolder;
+import org.dive4elements.river.utils.KMIndex;
+
+public class BedKMChartService extends DefaultService {
+
+    private static final long serialVersionUID = -4946194087923870485L;
+
+    private static final Logger log =
+        Logger.getLogger(BedKMChartService.class);
+
+    public static final int DEFAULT_WIDTH  = 240;
+    public static final int DEFAULT_HEIGHT = 180;
+
+    public static final String I18N_CHART_LABEL =
+        "bed.km.chart.label";
+
+    public static final String DEFAULT_CHART_LABEL =
+        "Measuring Points";
+
+    public static final String I18N_CHART_TITLE =
+        "bed.km.chart.title";
+
+    public static final String DEFAULT_CHART_TITLE =
+        "Measuring points";
+
+    public static final String I18N_KM_AXIS =
+        "bed.km.chart.km.axis";
+
+    public static final String DEFAULT_KM_AXIS =
+        "km";
+
+    public static final String I18N_DATE_AXIS =
+        "bed.km.chart.date.axis";
+
+    public static final String DEFAULT_DATE_AXIS =
+        "Date";
+
+    public static final String DEFAULT_FORMAT = "png";
+
+    // TODO: Load fancy image from resources.
+    public static final byte [] EMPTY = {
+        (byte)0x89, (byte)0x50, (byte)0x4e, (byte)0x47,
+        (byte)0x0d, (byte)0x0a, (byte)0x1a, (byte)0x0a,
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x0d,
+        (byte)0x49, (byte)0x48, (byte)0x44, (byte)0x52,
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01,
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01,
+        (byte)0x08, (byte)0x00, (byte)0x00, (byte)0x00,
+        (byte)0x00, (byte)0x3a, (byte)0x7e, (byte)0x9b,
+        (byte)0x55, (byte)0x00, (byte)0x00, (byte)0x00,
+        (byte)0x01, (byte)0x73, (byte)0x52, (byte)0x47,
+        (byte)0x42, (byte)0x00, (byte)0xae, (byte)0xce,
+        (byte)0x1c, (byte)0xe9, (byte)0x00, (byte)0x00,
+        (byte)0x00, (byte)0x09, (byte)0x70, (byte)0x48,
+        (byte)0x59, (byte)0x73, (byte)0x00, (byte)0x00,
+        (byte)0x0b, (byte)0x13, (byte)0x00, (byte)0x00,
+        (byte)0x0b, (byte)0x13, (byte)0x01, (byte)0x00,
+        (byte)0x9a, (byte)0x9c, (byte)0x18, (byte)0x00,
+        (byte)0x00, (byte)0x00, (byte)0x07, (byte)0x74,
+        (byte)0x49, (byte)0x4d, (byte)0x45, (byte)0x07,
+        (byte)0xdc, (byte)0x04, (byte)0x04, (byte)0x10,
+        (byte)0x30, (byte)0x15, (byte)0x7d, (byte)0x77,
+        (byte)0x36, (byte)0x0b, (byte)0x00, (byte)0x00,
+        (byte)0x00, (byte)0x08, (byte)0x74, (byte)0x45,
+        (byte)0x58, (byte)0x74, (byte)0x43, (byte)0x6f,
+        (byte)0x6d, (byte)0x6d, (byte)0x65, (byte)0x6e,
+        (byte)0x74, (byte)0x00, (byte)0xf6, (byte)0xcc,
+        (byte)0x96, (byte)0xbf, (byte)0x00, (byte)0x00,
+        (byte)0x00, (byte)0x0a, (byte)0x49, (byte)0x44,
+        (byte)0x41, (byte)0x54, (byte)0x08, (byte)0xd7,
+        (byte)0x63, (byte)0xf8, (byte)0x0f, (byte)0x00,
+        (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x00,
+        (byte)0x1b, (byte)0xb6, (byte)0xee, (byte)0x56,
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+        (byte)0x49, (byte)0x45, (byte)0x4e, (byte)0x44,
+        (byte)0xae, (byte)0x42, (byte)0x60, (byte)0x82
+    };
+
+    private static final Output empty() {
+        return new Output(EMPTY, "image/png");
+    }
+
+    @Override
+    public Service.Output process(
+        Document      data,
+        GlobalContext globalContext,
+        CallMeta      callMeta
+    ) {
+        log.debug("SQKMChartService.process");
+
+        SedDBSessionHolder.acquire();
+        try {
+            return doProcess(data, globalContext, callMeta);
+        }
+        finally {
+            SedDBSessionHolder.HOLDER.get().close();
+            SedDBSessionHolder.release();
+        }
+    }
+
+    protected Service.Output doProcess(
+        Document      input,
+        GlobalContext globalContext,
+        CallMeta      callMeta
+    ) {
+        String    river  = getRiverName(input);
+        Dimension extent = getExtent(input);
+        String    format = getFormat(input);
+
+        if (river == null) {
+            log.warn("River invalid.");
+            return empty();
+        }
+
+        BedOverview overview = BedOverviewFactory.getOverview(river);
+
+        if (overview == null) {
+            log.warn("No overview found for river '" + river + "'");
+            return empty();
+        }
+
+        KMIndex<List<Date>> entries = overview.filter(BedOverview.ACCEPT);
+
+        JFreeChart chart = createChart(entries, river, callMeta);
+
+        return encode(chart, extent, format);
+    }
+
+    protected static Output encode(
+        JFreeChart chart,
+        Dimension  extent,
+        String     format
+    ) {
+        BufferedImage image = chart.createBufferedImage(
+            extent.width, extent.height,
+            Transparency.BITMASK,
+            null);
+
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+        try {
+            ImageIO.write(image, format, out);
+        }
+        catch (IOException ioe) {
+            log.warn("writing image failed", ioe);
+            return empty();
+        }
+
+        return new Output(out.toByteArray(), "image/" + format);
+    }
+
+    protected static JFreeChart createChart(
+        KMIndex<List<Date>> entries,
+        String      river,
+        CallMeta    callMeta
+    ) {
+
+        XYSeriesCollection dataset = new XYSeriesCollection();
+        String key = Resources.format(
+            callMeta, I18N_CHART_LABEL, DEFAULT_CHART_LABEL, river);
+
+        XYSeries series = new XYSeries(key);
+        for (KMIndex.Entry<List<Date>> e: entries) {
+            double km = e.getKm();
+            List<Date> ds = e.getValue();
+            for (Date d: ds) {
+                series.add(km, d.getTime());
+            }
+        }
+
+        dataset.addSeries(series);
+        String title = Resources.format(
+            callMeta, I18N_CHART_TITLE, DEFAULT_CHART_TITLE, river);
+
+        String kmAxis = Resources.getMsg(
+            callMeta, I18N_KM_AXIS, DEFAULT_KM_AXIS);
+
+        String dateAxis = Resources.getMsg(
+            callMeta, I18N_DATE_AXIS, DEFAULT_DATE_AXIS);
+
+        JFreeChart chart = ChartFactory.createXYLineChart(
+            title,
+            kmAxis,
+            dateAxis,
+            null,
+            PlotOrientation.VERTICAL,
+            true,
+            true,
+            false);
+
+        XYPlot plot = (XYPlot)chart.getPlot();
+
+        DateAxis dA = new DateAxis();
+        plot.setRangeAxis(dA);
+        plot.setDataset(0, dataset);
+
+        chart.setBackgroundPaint(Color.white);
+        plot.setBackgroundPaint(Color.white);
+        plot.setDomainGridlinePaint(Color.gray);
+        plot.setRangeGridlinePaint(Color.gray);
+        plot.setDomainGridlinesVisible(true);
+        plot.setRangeGridlinesVisible(true);
+        XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer) plot.getRenderer();
+
+        renderer.setSeriesPaint(0, Color.gray);
+        renderer.setSeriesLinesVisible(0, false);
+        renderer.setSeriesShapesVisible(0, true);
+        renderer.setDrawOutlines(true);
+        return chart;
+    }
+
+
+    protected static String getRiverName(Document input) {
+        NodeList rivers = input.getElementsByTagName("river");
+
+        if (rivers.getLength() == 0) {
+            return null;
+        }
+
+        String river = ((Element)rivers.item(0)).getAttribute("name");
+
+        return river.length() > 0 ? river : null;
+    }
+
+    protected static Dimension getExtent(Document input) {
+
+        int width  = DEFAULT_WIDTH;
+        int height = DEFAULT_HEIGHT;
+
+        NodeList extents = input.getElementsByTagName("extent");
+
+        if (extents.getLength() > 0) {
+            Element element = (Element)extents.item(0);
+            String w = element.getAttribute("width");
+            String h = element.getAttribute("height");
+
+            try {
+                width = Math.max(1, Integer.parseInt(w));
+            }
+            catch (NumberFormatException nfe) {
+                log.warn("width '" + w + "' is not a valid.");
+            }
+
+            try {
+                height = Math.max(1, Integer.parseInt(h));
+            }
+            catch (NumberFormatException nfe) {
+                log.warn("height '" + h + "' is not a valid");
+            }
+        }
+
+        return new Dimension(width, height);
+    }
+
+    protected static String getFormat(Document input) {
+        String format = DEFAULT_FORMAT;
+
+        NodeList formats = input.getElementsByTagName("format");
+
+        if (formats.getLength() > 0) {
+            String type = ((Element)formats.item(0)).getAttribute("type");
+            if (type.length() > 0) {
+                format = type;
+            }
+        }
+
+        return format;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/BedloadKMChartService.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,302 @@
+package org.dive4elements.river.artifacts.services;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Transparency;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Date;
+import java.util.List;
+
+import javax.imageio.ImageIO;
+
+import org.apache.log4j.Logger;
+import org.jfree.chart.ChartFactory;
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.axis.DateAxis;
+import org.jfree.chart.plot.PlotOrientation;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
+import org.jfree.data.xy.XYSeries;
+import org.jfree.data.xy.XYSeriesCollection;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+import org.dive4elements.artifactdatabase.DefaultService;
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.artifacts.GlobalContext;
+import org.dive4elements.artifacts.Service;
+import org.dive4elements.river.artifacts.model.minfo.BedloadOverview;
+import org.dive4elements.river.artifacts.model.minfo.BedloadOverviewFactory;
+import org.dive4elements.river.artifacts.resources.Resources;
+import org.dive4elements.river.backend.SedDBSessionHolder;
+import org.dive4elements.river.utils.KMIndex;
+
+public class BedloadKMChartService extends DefaultService {
+
+    private static final long serialVersionUID = 4156704841305086495L;
+
+    private static final Logger log =
+        Logger.getLogger(BedloadKMChartService.class);
+
+    public static final int DEFAULT_WIDTH  = 240;
+    public static final int DEFAULT_HEIGHT = 180;
+
+    public static final String I18N_CHART_LABEL =
+        "bedload.km.chart.label";
+
+    public static final String DEFAULT_CHART_LABEL =
+        "Measuring Points";
+
+    public static final String I18N_CHART_TITLE =
+        "bedload.km.chart.title";
+
+    public static final String DEFAULT_CHART_TITLE =
+        "Measuring points";
+
+    public static final String I18N_KM_AXIS =
+        "bedload.km.chart.km.axis";
+
+    public static final String DEFAULT_KM_AXIS =
+        "km";
+
+    public static final String I18N_DATE_AXIS =
+        "bedload.km.chart.date.axis";
+
+    public static final String DEFAULT_DATE_AXIS =
+        "Date";
+
+    public static final String DEFAULT_FORMAT = "png";
+
+    // TODO: Load fancy image from resources.
+    public static final byte [] EMPTY = {
+        (byte)0x89, (byte)0x50, (byte)0x4e, (byte)0x47,
+        (byte)0x0d, (byte)0x0a, (byte)0x1a, (byte)0x0a,
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x0d,
+        (byte)0x49, (byte)0x48, (byte)0x44, (byte)0x52,
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01,
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01,
+        (byte)0x08, (byte)0x00, (byte)0x00, (byte)0x00,
+        (byte)0x00, (byte)0x3a, (byte)0x7e, (byte)0x9b,
+        (byte)0x55, (byte)0x00, (byte)0x00, (byte)0x00,
+        (byte)0x01, (byte)0x73, (byte)0x52, (byte)0x47,
+        (byte)0x42, (byte)0x00, (byte)0xae, (byte)0xce,
+        (byte)0x1c, (byte)0xe9, (byte)0x00, (byte)0x00,
+        (byte)0x00, (byte)0x09, (byte)0x70, (byte)0x48,
+        (byte)0x59, (byte)0x73, (byte)0x00, (byte)0x00,
+        (byte)0x0b, (byte)0x13, (byte)0x00, (byte)0x00,
+        (byte)0x0b, (byte)0x13, (byte)0x01, (byte)0x00,
+        (byte)0x9a, (byte)0x9c, (byte)0x18, (byte)0x00,
+        (byte)0x00, (byte)0x00, (byte)0x07, (byte)0x74,
+        (byte)0x49, (byte)0x4d, (byte)0x45, (byte)0x07,
+        (byte)0xdc, (byte)0x04, (byte)0x04, (byte)0x10,
+        (byte)0x30, (byte)0x15, (byte)0x7d, (byte)0x77,
+        (byte)0x36, (byte)0x0b, (byte)0x00, (byte)0x00,
+        (byte)0x00, (byte)0x08, (byte)0x74, (byte)0x45,
+        (byte)0x58, (byte)0x74, (byte)0x43, (byte)0x6f,
+        (byte)0x6d, (byte)0x6d, (byte)0x65, (byte)0x6e,
+        (byte)0x74, (byte)0x00, (byte)0xf6, (byte)0xcc,
+        (byte)0x96, (byte)0xbf, (byte)0x00, (byte)0x00,
+        (byte)0x00, (byte)0x0a, (byte)0x49, (byte)0x44,
+        (byte)0x41, (byte)0x54, (byte)0x08, (byte)0xd7,
+        (byte)0x63, (byte)0xf8, (byte)0x0f, (byte)0x00,
+        (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x00,
+        (byte)0x1b, (byte)0xb6, (byte)0xee, (byte)0x56,
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+        (byte)0x49, (byte)0x45, (byte)0x4e, (byte)0x44,
+        (byte)0xae, (byte)0x42, (byte)0x60, (byte)0x82
+    };
+
+    private static final Output empty() {
+        return new Output(EMPTY, "image/png");
+    }
+
+    @Override
+    public Service.Output process(
+        Document      data,
+        GlobalContext globalContext,
+        CallMeta      callMeta
+    ) {
+        log.debug("SQKMChartService.process");
+
+        SedDBSessionHolder.acquire();
+        try {
+            return doProcess(data, globalContext, callMeta);
+        }
+        finally {
+            SedDBSessionHolder.HOLDER.get().close();
+            SedDBSessionHolder.release();
+        }
+    }
+
+    protected Service.Output doProcess(
+        Document      input,
+        GlobalContext globalContext,
+        CallMeta      callMeta
+    ) {
+        String    river  = getRiverName(input);
+        Dimension extent = getExtent(input);
+        String    format = getFormat(input);
+
+        if (river == null) {
+            log.warn("River invalid.");
+            return empty();
+        }
+
+        BedloadOverview overview = BedloadOverviewFactory.getOverview(river);
+
+        if (overview == null) {
+            log.warn("No overview found for river '" + river + "'");
+            return empty();
+        }
+
+        KMIndex<List<Date>> entries = overview.filter(BedloadOverview.ACCEPT);
+
+        JFreeChart chart = createChart(entries, river, callMeta);
+
+        return encode(chart, extent, format);
+    }
+
+    protected static Output encode(
+        JFreeChart chart,
+        Dimension  extent,
+        String     format
+    ) {
+        BufferedImage image = chart.createBufferedImage(
+            extent.width, extent.height,
+            Transparency.BITMASK,
+            null);
+
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+        try {
+            ImageIO.write(image, format, out);
+        }
+        catch (IOException ioe) {
+            log.warn("writing image failed", ioe);
+            return empty();
+        }
+
+        return new Output(out.toByteArray(), "image/" + format);
+    }
+
+    protected static JFreeChart createChart(
+        KMIndex<List<Date>> entries,
+        String      river,
+        CallMeta    callMeta
+    ) {
+
+        XYSeriesCollection dataset = new XYSeriesCollection();
+        String key = Resources.format(
+            callMeta, I18N_CHART_LABEL, DEFAULT_CHART_LABEL, river);
+
+        XYSeries series = new XYSeries(key);
+        for (KMIndex.Entry<List<Date>> e: entries) {
+            double km = e.getKm();
+            List<Date> ds = e.getValue();
+            for (Date d: ds) {
+                series.add(km, d.getTime());
+            }
+        }
+
+        dataset.addSeries(series);
+        String title = Resources.format(
+            callMeta, I18N_CHART_TITLE, DEFAULT_CHART_TITLE, river);
+
+        String kmAxis = Resources.getMsg(
+            callMeta, I18N_KM_AXIS, DEFAULT_KM_AXIS);
+
+        String dateAxis = Resources.getMsg(
+            callMeta, I18N_DATE_AXIS, DEFAULT_DATE_AXIS);
+
+        JFreeChart chart = ChartFactory.createXYLineChart(
+            title,
+            kmAxis,
+            dateAxis,
+            null,
+            PlotOrientation.VERTICAL,
+            true,
+            true,
+            false);
+
+        XYPlot plot = (XYPlot)chart.getPlot();
+
+        DateAxis dA = new DateAxis();
+        plot.setRangeAxis(dA);
+        plot.setDataset(0, dataset);
+
+        chart.setBackgroundPaint(Color.white);
+        plot.setBackgroundPaint(Color.white);
+        plot.setDomainGridlinePaint(Color.gray);
+        plot.setRangeGridlinePaint(Color.gray);
+        plot.setDomainGridlinesVisible(true);
+        plot.setRangeGridlinesVisible(true);
+        XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer) plot.getRenderer();
+
+        renderer.setSeriesPaint(0, Color.gray);
+        renderer.setSeriesLinesVisible(0, false);
+        renderer.setSeriesShapesVisible(0, true);
+        renderer.setDrawOutlines(true);
+        return chart;
+    }
+
+
+    protected static String getRiverName(Document input) {
+        NodeList rivers = input.getElementsByTagName("river");
+
+        if (rivers.getLength() == 0) {
+            return null;
+        }
+
+        String river = ((Element)rivers.item(0)).getAttribute("name");
+
+        return river.length() > 0 ? river : null;
+    }
+
+    protected static Dimension getExtent(Document input) {
+
+        int width  = DEFAULT_WIDTH;
+        int height = DEFAULT_HEIGHT;
+
+        NodeList extents = input.getElementsByTagName("extent");
+
+        if (extents.getLength() > 0) {
+            Element element = (Element)extents.item(0);
+            String w = element.getAttribute("width");
+            String h = element.getAttribute("height");
+
+            try {
+                width = Math.max(1, Integer.parseInt(w));
+            }
+            catch (NumberFormatException nfe) {
+                log.warn("width '" + w + "' is not a valid.");
+            }
+
+            try {
+                height = Math.max(1, Integer.parseInt(h));
+            }
+            catch (NumberFormatException nfe) {
+                log.warn("height '" + h + "' is not a valid");
+            }
+        }
+
+        return new Dimension(width, height);
+    }
+
+    protected static String getFormat(Document input) {
+        String format = DEFAULT_FORMAT;
+
+        NodeList formats = input.getElementsByTagName("format");
+
+        if (formats.getLength() > 0) {
+            String type = ((Element)formats.item(0)).getAttribute("type");
+            if (type.length() > 0) {
+                format = type;
+            }
+        }
+
+        return format;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/CacheInvalidationService.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,54 @@
+package org.dive4elements.river.artifacts.services;
+
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.artifacts.GlobalContext;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+
+import org.dive4elements.artifactdatabase.XMLService;
+
+import org.dive4elements.river.artifacts.cache.CacheFactory;
+
+import net.sf.ehcache.Cache;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+public class CacheInvalidationService
+extends      XMLService
+{
+    @Override
+    public Document processXML(
+        Document      data,
+        GlobalContext globalContext,
+        CallMeta      callMeta
+    ) {
+        Document result = XMLUtils.newDocument();
+
+        Element all = result.createElement("caches");
+
+        NodeList caches = data.getElementsByTagName("cache");
+
+        for (int i = 0, C = caches.getLength(); i < C; ++i) {
+            Element c = (Element)caches.item(i);
+            String name = c.getAttribute("name");
+            Element e = result.createElement("cache");
+            e.setAttribute("name", name);
+            Cache cache = CacheFactory.getCache(name);
+            if (cache != null) {
+                cache.removeAll();
+                e.setTextContent("All elements removed.");
+            }
+            else {
+                e.setTextContent("Error: Cache not found.");
+            }
+            all.appendChild(e);
+        }
+
+        result.appendChild(all);
+
+        return result;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/CrossSectionKMService.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,242 @@
+package org.dive4elements.river.artifacts.services;
+
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.artifacts.GlobalContext;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+
+import org.dive4elements.river.artifacts.cache.CacheFactory;
+
+import org.dive4elements.river.backend.SessionHolder;
+
+import org.dive4elements.river.model.CrossSection;
+import org.dive4elements.river.model.CrossSectionLine;
+
+import java.util.AbstractMap;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.List;
+import java.util.Map;
+import java.util.NavigableMap;
+
+import java.util.concurrent.ConcurrentSkipListMap;
+
+import net.sf.ehcache.Cache;
+
+import org.apache.log4j.Logger;
+
+import org.hibernate.Query;
+import org.hibernate.Session;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+
+/**
+ * Service to find the next/previous km (measurement) of cross sections.
+ * Looking at the query for a single cross-section id at a single km, the
+ * service does the following:
+ *
+ * It returns the km itself if a measurement at that km was found and
+ * the N nearest other measurement points in both directions.
+ *
+ * That means, you can pass N=0 to find out whether a measurement at given km
+ * exists.
+ *
+ * If less than N neighbours exist in one direction, less are delivered
+ * (e.g. given measurements at [0,2,3,4,5,7,8,9] a query for km=8, N=3 will
+ * result in [4,5,7,8,9]).
+ */
+public class CrossSectionKMService
+extends      FLYSService
+{
+    private static Logger logger =
+        Logger.getLogger(CrossSectionKMService.class);
+
+    public static final String CACHE_NAME = "cross-section-kms";
+
+
+    /** Trivial constructor. */
+    public CrossSectionKMService() {
+    }
+
+
+    /**
+     * @param data
+     */
+    @Override
+    public Document doProcess(
+        Document      data,
+        GlobalContext globalContext,
+        CallMeta      callMeta
+    ) {
+        logger.debug("CrossSectionKMService.doProcess");
+
+        NodeList crossSectionNodes =
+            data.getElementsByTagName("art:cross-section");
+
+        Document document = XMLUtils.newDocument();
+
+        Element all = document.createElement("cross-sections");
+
+        for (int i = 0, CS = crossSectionNodes.getLength(); i < CS; ++i) {
+            Element crossSectionElement = (Element)crossSectionNodes.item(i);
+
+            String idString = crossSectionElement.getAttribute("id");
+            String kmString = crossSectionElement.getAttribute("km");
+            String neighborsString = crossSectionElement.getAttribute("n");
+
+            if (idString.length() == 0 || kmString.length() == 0) {
+                logger.debug("missing attributes in cross-section element");
+                continue;
+            }
+
+            double  km;
+            Integer crossSectionId;
+            int     N = 2;
+
+            try {
+                km             = Double.parseDouble(kmString);
+                crossSectionId = Integer.valueOf(idString);
+
+                if (neighborsString.length() > 0) {
+                    N = Integer.parseInt(neighborsString);
+                }
+            }
+            catch (NumberFormatException nfe) {
+                logger.debug("converting number failed", nfe);
+                continue;
+            }
+
+            NavigableMap<Double, Integer> map = getKms(crossSectionId);
+
+            if (map == null) {
+                logger.debug("cannot find cross section " + crossSectionId);
+                continue;
+            }
+
+            Deque<Map.Entry<Double, Integer>> result =
+                nearestNeighbors(map, km, N);
+
+            if (!result.isEmpty()) {
+                Element csE = document.createElement("cross-section");
+                csE.setAttribute("id", idString);
+                for (Map.Entry<Double, Integer> entry: result) {
+                    Element lineE = document.createElement("line");
+                    lineE.setAttribute(
+                        "line-id", String.valueOf(entry.getValue()));
+                    lineE.setAttribute(
+                        "km", String.valueOf(entry.getKey()));
+                    csE.appendChild(lineE);
+                }
+                all.appendChild(csE);
+            }
+        }
+
+        document.appendChild(all);
+
+        return document;
+    }
+
+    public static NavigableMap<Double, Integer> getKms(int crossSectionId) {
+
+        Cache cache = CacheFactory.getCache(CACHE_NAME);
+
+        if (cache == null) {
+            return getUncached(crossSectionId);
+        }
+
+        NavigableMap<Double, Integer> map;
+
+        net.sf.ehcache.Element element = cache.get(crossSectionId);
+        if (element == null) {
+            map = getUncached(crossSectionId);
+            if (map != null) {
+                element = new net.sf.ehcache.Element(
+                    crossSectionId, map);
+                cache.put(element);
+            }
+        }
+        else {
+            map = (NavigableMap<Double, Integer>)element.getValue();
+        }
+
+        return map;
+    }
+
+
+    /**
+     * @param km  the kilometer from which to start searching for other
+     *            measurements
+     * @param N   number of neighboring measurements to find.
+     */
+    public static Deque<Map.Entry<Double, Integer>> nearestNeighbors(
+        NavigableMap<Double, Integer> map,
+        double                        km,
+        int                           N
+    ) {
+        Deque<Map.Entry<Double, Integer>> result =
+            new ArrayDeque<Map.Entry<Double, Integer>>(2*N);
+
+        Integer v = map.get(km);
+
+        if (v != null) {
+            result.add(new AbstractMap.SimpleEntry<Double, Integer>(km, v));
+        }
+
+        int i = 0;
+        for (Map.Entry<Double, Integer> entry:
+             map.headMap(km, false).descendingMap().entrySet()) {
+            if (i++ >= N) {
+                break;
+            }
+            result.addFirst(entry);
+        }
+
+        i = 0;
+        for (Map.Entry<Double, Integer> entry:
+             map.tailMap(km, false).entrySet()) {
+            if (i++ >= N) {
+                break;
+            }
+            result.addLast(entry);
+        }
+
+        return result;
+    }
+
+
+    /**
+     * @param crossSectionId id of queried cross-section (in db).
+     * @return Mapping from kilometer to db-id.
+     */
+    public static NavigableMap<Double, Integer> getUncached(
+        Integer crossSectionId
+    ) {
+        NavigableMap<Double, Integer> result =
+            new ConcurrentSkipListMap<Double, Integer>();
+
+        Session session = SessionHolder.HOLDER.get();
+        Query query = session.createQuery(
+            "from CrossSection where id=:id");
+        query.setParameter("id", crossSectionId);
+
+        List<CrossSection> crossSections = query.list();
+        if (crossSections.isEmpty()) {
+            return null;
+        }
+
+        CrossSection crossSection = crossSections.get(0);
+        List<CrossSectionLine> lines = crossSection.getLines();
+
+        for (CrossSectionLine line: lines) {
+            Double  km = line.getKm().doubleValue();
+            Integer id = line.getId();
+            result.put(km, id);
+        }
+
+        return result;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/DischargeInfoService.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,122 @@
+package org.dive4elements.river.artifacts.services;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Date;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.artifacts.GlobalContext;
+
+import org.dive4elements.artifacts.common.ArtifactNamespaceContext;
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+
+import org.dive4elements.river.artifacts.model.DischargeTables;
+import org.dive4elements.river.model.Gauge;
+import org.dive4elements.river.model.DischargeTable;
+import org.dive4elements.river.model.TimeInterval;
+
+/**
+ * This service provides information about discharges at a defined gauge.
+ *
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class DischargeInfoService extends FLYSService {
+
+    /** The logger used in this service. */
+    private static Logger logger = Logger.getLogger(DischargeInfoService.class);
+
+    public static final String GAUGE_XPATH = "/art:gauge/text()";
+
+    public DischargeInfoService() {
+    }
+
+
+    @Override
+    public Document doProcess(
+        Document      data,
+        GlobalContext globalContext,
+        CallMeta      callMeta
+    ) {
+        logger.debug("DischargeInfoService.process");
+        logger.debug(XMLUtils.toString(data));
+
+        String gaugeNumber = XMLUtils.xpathString(
+            data, GAUGE_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);
+
+        long gn;
+        try {
+            gn = Long.parseLong(gaugeNumber);
+        }
+        catch (NumberFormatException nfe) {
+            logger.warn("Invalid gauge number. Cannot return discharg info.");
+            return XMLUtils.newDocument();
+        }
+
+        Gauge gauge = Gauge.getGaugeByOfficialNumber(gn);
+
+        logger.debug("Found gauge: " + gauge.getName());
+
+        return buildDocument(gauge);
+    }
+
+
+    protected Document buildDocument(Gauge gauge) {
+        Document result = XMLUtils.newDocument();
+
+        List<DischargeTable> tables =gauge.getDischargeTables();
+        Collections.sort(tables);
+
+        Element all = result.createElement("discharges");
+        for (DischargeTable dt: tables) {
+            Element discharge = result.createElement("discharge");
+            discharge.setAttribute("description", dt.getDescription());
+
+            // Get time interval.
+            TimeInterval ti = dt.getTimeInterval();
+
+            if (ti == null) {
+                logger.warn("DischargeTable has no TimeInterval set!");
+                continue;
+            }
+
+            Date startTime = ti.getStartTime();
+            Date stopTime = ti.getStopTime();
+
+            if (startTime != null) {
+                discharge.setAttribute("start", String.valueOf(startTime.getTime()));
+            }
+            else {
+                continue;
+            }
+
+            if (stopTime != null && dt.getKind() != DischargeTables.MASTER) {
+                discharge.setAttribute("end", String.valueOf(stopTime.getTime()));
+            }
+            else if (dt.getKind() == DischargeTables.MASTER) {
+                long now = System.currentTimeMillis();
+                discharge.setAttribute("end", String.valueOf(now));
+            }
+            else {
+                continue;
+            }
+
+            all.appendChild(discharge);
+        }
+        result.appendChild(all);
+        return result;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/DischargeTablesOverview.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,300 @@
+package org.dive4elements.river.artifacts.services;
+
+import java.awt.Color;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+
+import org.apache.log4j.Logger;
+import org.hibernate.Session;
+import org.jfree.chart.ChartFactory;
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.plot.Marker;
+import org.jfree.chart.plot.PlotOrientation;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.data.xy.XYSeries;
+import org.jfree.data.xy.XYSeriesCollection;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.artifacts.GlobalContext;
+import org.dive4elements.river.artifacts.model.DischargeTables;
+import org.dive4elements.river.artifacts.model.GaugesFactory;
+import org.dive4elements.river.artifacts.resources.Resources;
+import org.dive4elements.river.backend.SessionHolder;
+import org.dive4elements.river.model.DischargeTable;
+import org.dive4elements.river.model.Gauge;
+import org.dive4elements.river.model.MainValue;
+import org.dive4elements.river.model.TimeInterval;
+
+
+/** Generate Discharge Table chart. */
+public class DischargeTablesOverview extends AbstractChartService {
+
+    private static final Logger log = Logger
+        .getLogger(DischargeTablesOverview.class);
+
+    private static final long serialVersionUID = 1L;
+
+    public static final String I18N_CHART_TITLE = "gauge.discharge.service.chart.title";
+    public static final String DEFAULT_CHART_TITLE = "Pegel: XXX";
+
+    public static final String I18N_CHART_X_AXIS_TITLE = "gauge.discharge.service.chart.x.title";
+    public static final String DEFAULT_X_AXIS_TITLE = "Q [m^3/s]";
+
+    public static final String I18N_CHART_Y_AXIS_TITLE = "gauge.discharge.service.chart.y.title";
+    public static final String DEFAULT_Y_AXIS_TITLE = "W [cm]";
+
+    public static final String I18N_CHART_SERIES_TITLE = "gauge.discharge.service.chart.series.title";
+    public static final String DEFAULT_CHART_SERIES_TITLE = "Abflusskurve";
+
+    public static final String I18N_CHART_SERIES_TITLE_MASTER = "gauge.discharge.service.chart.series.title.master";
+    public static final String DEFAULT_CHART_SERIES_TITLE_MASTER = "Aktuelle Abflusskurve";
+
+    public static final DateFormat DATE_FORMAT = DateFormat.getDateInstance(
+        DateFormat.SHORT, Locale.GERMANY);
+
+    private Session session;
+
+    @Override
+    protected void init() {
+        session = SessionHolder.acquire();
+    }
+
+    @Override
+    protected void finish() {
+        if (session != null) {
+            session.close();
+            SessionHolder.release();
+        }
+    }
+
+    protected JFreeChart createChart(Document data,
+        GlobalContext globalContext, CallMeta callMeta) {
+
+        Gauge gauge = extractGauge(data);
+
+        if (gauge == null) {
+            log.warn("Could not determine Gauge from request!");
+            return null;
+        }
+
+        log.info("create discharge chart for gauge '" + gauge.getName() + "'");
+        TimeInterval timerange = extractTimeInterval(data);
+
+        List<DischargeTable> dts = getDischargeTables(gauge, timerange);
+        XYSeriesCollection dataset = new XYSeriesCollection();
+
+        for (DischargeTable dt : dts) {
+            try {
+                XYSeries series = createSeries(callMeta, dt);
+                if (series != null) {
+                    dataset.addSeries(series);
+                }
+            }
+            catch (IllegalArgumentException iae) {
+                log.warn("unable to create discharge curve: "
+                    + iae.getMessage());
+            }
+        }
+
+        String title = Resources.format(callMeta, I18N_CHART_TITLE,
+            DEFAULT_CHART_TITLE, gauge.getName());
+
+        String xAxis = Resources.getMsg(callMeta, I18N_CHART_X_AXIS_TITLE,
+            DEFAULT_X_AXIS_TITLE);
+
+        String yAxis = Resources.format(callMeta, I18N_CHART_Y_AXIS_TITLE,
+            DEFAULT_Y_AXIS_TITLE);
+
+        JFreeChart chart = ChartFactory.createXYLineChart(title, xAxis, yAxis,
+            null, PlotOrientation.VERTICAL, true, true, false);
+
+        chart.setBackgroundPaint(Color.white);
+
+        XYPlot plot = (XYPlot) chart.getPlot();
+        plot.setDataset(0, dataset);
+        plot.setBackgroundPaint(Color.white);
+        plot.setDomainGridlinePaint(Color.gray);
+        plot.setRangeGridlinePaint(Color.gray);
+        plot.setDomainGridlinesVisible(true);
+        plot.setRangeGridlinesVisible(true);
+
+        applyMainValueMarkers(
+            plot,
+            gauge,
+            callMeta);
+
+        return chart;
+    }
+
+    protected XYSeries createSeries(CallMeta callMeta, DischargeTable dt)
+        throws IllegalArgumentException {
+
+        double[][] xy = null;
+
+        if (dt.getKind() == DischargeTables.MASTER) {
+            xy = DischargeTables.loadDischargeTableValues(dt,
+                DischargeTables.MASTER_SCALE);
+        }
+        else {
+            xy = DischargeTables.loadDischargeTableValues(dt,
+                DischargeTables.HISTORICAL_SCALE);
+        }
+
+        XYSeries series = new XYSeries(createSeriesTitle(callMeta, dt), false);
+        for (int i = 0, n = xy[0].length; i < n; i++) {
+            series.add(xy[0][i], xy[1][i]);
+        }
+
+        return series;
+    }
+
+
+    /** Add domain markers to plot that indicate mainvalues. */
+    protected static void applyMainValueMarkers(
+        XYPlot   plot,
+        Gauge    gauge,
+        CallMeta meta
+    ) {
+        String river = gauge.getRiver().getName();
+        double km    = gauge.getStation().doubleValue();
+
+        // Get Gauge s mainvalues.
+        List<MainValue> mainValues = gauge.getMainValues();
+        for (MainValue mainValue : mainValues) {
+            if (mainValue.getMainValue().getType().getName().equals("Q")) {
+                // Its a Q main value.
+                Marker m = FixingsKMChartService.createQSectorMarker(
+                    mainValue.getValue().doubleValue(),
+                    mainValue.getMainValue().getName());
+                plot.addDomainMarker(m);
+            }
+            else if (mainValue.getMainValue().getType().getName().equals("W")) {
+                // Its a W main value.
+                Marker m = FixingsKMChartService.createQSectorMarker(
+                    mainValue.getValue().doubleValue(),
+                    mainValue.getMainValue().getName());
+                plot.addRangeMarker(m);
+            }
+        }
+    }
+
+    protected String createSeriesTitle(CallMeta callMeta, DischargeTable dt)
+        throws IllegalArgumentException {
+        TimeInterval timeInterval = dt.getTimeInterval();
+
+        if (timeInterval == null) {
+            return Resources.format(callMeta, DEFAULT_CHART_SERIES_TITLE);
+        }
+
+        Date start = timeInterval.getStartTime();
+        Date end = timeInterval.getStopTime();
+
+        if (start != null && end != null) {
+            return Resources.format(callMeta, I18N_CHART_SERIES_TITLE,
+                DEFAULT_CHART_SERIES_TITLE, start, end);
+        }
+        else if (start != null) {
+            return Resources.format(callMeta, I18N_CHART_SERIES_TITLE_MASTER,
+                DEFAULT_CHART_SERIES_TITLE, start);
+        }
+        else {
+            throw new IllegalArgumentException(
+                "Missing start date of DischargeTable " + dt.getId());
+        }
+    }
+
+    protected Gauge extractGauge(Document data) {
+        NodeList gauges = data.getElementsByTagName("gauge");
+
+        if (gauges.getLength() > 0) {
+            String name = ((Element) gauges.item(0)).getAttribute("name");
+
+            try {
+                long officialNumber = Long.valueOf(name);
+                return Gauge.getGaugeByOfficialNumber(officialNumber);
+            }
+            catch (NumberFormatException nfe) {
+                // it seems, that the client uses the name of the gauge instead
+                // of its official number
+            }
+
+            if (name != null && name.length() > 0) {
+                return GaugesFactory.getGauge(name);
+            }
+        }
+
+        return null;
+    }
+
+    protected TimeInterval extractTimeInterval(Document data) {
+        NodeList timeranges = data.getElementsByTagName("timerange");
+
+        if (timeranges != null && timeranges.getLength() > 0) {
+            Element timerange = (Element) timeranges.item(0);
+
+            String lower = timerange.getAttribute("lower");
+            String upper = timerange.getAttribute("upper");
+
+            if (lower != null && upper != null) {
+                try {
+                    Date d1 = DATE_FORMAT.parse(lower);
+                    Date d2 = DATE_FORMAT.parse(upper);
+
+                    return new TimeInterval(d1, d2);
+                }
+                catch (ParseException pe) {
+                    log.warn("Wrong time format: " + pe.getMessage());
+                }
+            }
+        }
+
+        return null;
+    }
+
+    protected List<DischargeTable> getDischargeTables(Gauge gauge,
+        TimeInterval timerange) {
+        List<DischargeTable> all = gauge.getDischargeTables();
+        Collections.sort(all);
+
+        if (timerange == null) {
+            return all;
+        }
+
+        List<DischargeTable> dts = new ArrayList<DischargeTable>(all.size());
+        long startDate = timerange.getStartTime().getTime();
+        long stopDate = timerange.getStopTime().getTime();
+
+        for (DischargeTable dt : all) {
+            TimeInterval tmp = dt.getTimeInterval();
+            if (tmp == null) {
+                // this should never happen because all discharge tables should
+                // have a time interval set!
+                continue;
+            }
+
+            Date start = tmp.getStartTime();
+            Date stop = tmp.getStartTime();
+
+            if (start.getTime() > startDate && start.getTime() < stopDate) {
+                dts.add(dt);
+                continue;
+            }
+            else if (stop != null && stop.getTime() < stopDate
+                && stop.getTime() > startDate) {
+                dts.add(dt);
+                continue;
+            }
+        }
+
+        return dts;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/DistanceInfoService.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,141 @@
+package org.dive4elements.river.artifacts.services;
+
+import java.util.Iterator;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.artifacts.GlobalContext;
+
+import org.dive4elements.artifacts.common.ArtifactNamespaceContext;
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+
+import org.dive4elements.river.model.FastAnnotations;
+
+import org.dive4elements.river.artifacts.model.LocationProvider;
+
+
+/**
+ * This service provides information about distances of a specified river.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DistanceInfoService extends FLYSService {
+
+    /** The logger used in this service. */
+    private static Logger logger = Logger.getLogger(DistanceInfoService.class);
+
+    public static final String RIVER_XPATH = "/art:river/text()";
+
+    public static final String FILTER_XPATH = "/art:river/art:filter/text()";
+
+
+    /**
+     * The default constructor.
+     */
+    public DistanceInfoService() {
+    }
+
+
+    @Override
+    public Document doProcess(
+        Document      data,
+        GlobalContext globalContext,
+        CallMeta      callMeta
+    ) {
+        logger.debug("DistanceInfoService.process");
+
+        String river = XMLUtils.xpathString(
+            data, RIVER_XPATH, ArtifactNamespaceContext.INSTANCE);
+
+        String filterName  = XMLUtils.xpathString(
+            data, FILTER_XPATH, ArtifactNamespaceContext.INSTANCE);
+
+        if (river == null || (river = river.trim()).length() == 0) {
+            logger.warn("No river specified. Cannot return distance info!");
+            return XMLUtils.newDocument();
+        }
+
+        logger.debug("Search distances for river: " + river);
+
+        FastAnnotations fas = LocationProvider.getAnnotations(river);
+
+        FastAnnotations.Filter filter = selectFilter(filterName);
+
+        return buildDocument(fas.filter(filter));
+    }
+
+    protected Document buildDocument(
+        Iterator<FastAnnotations.Annotation> iter
+    ) {
+        Document result = XMLUtils.newDocument();
+
+        Element all = result.createElement("distances");
+
+        while (iter.hasNext()) {
+            all.appendChild(buildNode(result, iter.next()));
+        }
+
+        result.appendChild(all);
+
+        return result;
+    }
+
+    protected static FastAnnotations.Filter selectFilter(String name) {
+
+        if (name != null) {
+            if ("locations".equals(name)) return FastAnnotations.IS_POINT;
+            if ("distances".equals(name)) return FastAnnotations.IS_RANGE;
+            if ("measuringpoint".equals(name))
+                return new FastAnnotations.NameFilter("Messstelle");
+        }
+
+        return FastAnnotations.ALL;
+    }
+
+    /**
+     * Builds an Element for a distance info.
+     *
+     * @param an The Annotation that provides information about the distance.
+     *
+     * @return an Element that contains information about a distance.
+     */
+    protected static Element buildNode(
+        Document                   document,
+        FastAnnotations.Annotation an
+    ) {
+        Element distance = document.createElement("distance");
+
+        distance.setAttribute("description", an.getPosition());
+
+        String riverSide = an.getAttribute();
+
+        if (riverSide != null && riverSide.length() > 0) {
+            distance.setAttribute("riverside", riverSide);
+        }
+
+        distance.setAttribute("from", String.valueOf(an.getA()));
+
+        double b      = an.getB();
+        double bottom = an.getBottom();
+        double top    = an.getTop();
+
+        if (!Double.isNaN(b)) {
+            distance.setAttribute("to", String.valueOf(b));
+        }
+
+        if (!Double.isNaN(bottom)) {
+            distance.setAttribute("bottom", String.valueOf(bottom));
+        }
+
+        if (!Double.isNaN(top)) {
+            distance.setAttribute("top", String.valueOf(top));
+        }
+
+        return distance;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/FLYSService.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,59 @@
+package org.dive4elements.river.artifacts.services;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+import org.hibernate.Session;
+
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.artifacts.GlobalContext;
+
+import org.dive4elements.artifactdatabase.XMLService;
+
+import org.dive4elements.river.backend.SessionHolder;
+
+
+public abstract class FLYSService extends XMLService {
+
+    private static final Logger logger = Logger.getLogger(FLYSService.class);
+
+
+    @Override
+    public Document processXML(
+        Document      data,
+        GlobalContext globalContext,
+        CallMeta      callMeta
+    ) {
+        init();
+
+        try {
+            return doProcess(data, globalContext, callMeta);
+        }
+        finally {
+            shutdown();
+        }
+    }
+
+
+    protected abstract Document doProcess(
+        Document      data,
+        GlobalContext globalContext,
+        CallMeta      callMeta);
+
+
+    protected void init() {
+        logger.debug("init");
+        SessionHolder.acquire();
+    }
+
+
+    protected void shutdown() {
+        logger.debug("shutdown");
+        Session session = SessionHolder.HOLDER.get();
+        session.close();
+
+        SessionHolder.release();
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/FileUploadService.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,115 @@
+package org.dive4elements.river.artifacts.services;
+
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.artifacts.GlobalContext;
+import org.dive4elements.artifacts.common.ArtifactNamespaceContext;
+import org.dive4elements.artifacts.common.utils.FileTools;
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator;
+import org.dive4elements.river.utils.FLYSUtils;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.log4j.Logger;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+/**
+ * Service that receives XML-packed Shapefile bundles from the client.
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class FileUploadService extends FLYSService {
+
+    /** The logger used in this service.*/
+    private static Logger logger = Logger.getLogger(FileUploadService.class);
+
+    /** XPath that points to the artifact uuid.*/
+    public static final String XPATH_ARTIFACT_UUID =
+        "/upload/artifact-uuid/text()";
+
+    /** XPath that points to the base64 encoded data.*/
+    public static final String XPATH_DATA = "/upload/data/text()";
+
+    public FileUploadService() {
+    }
+
+    @Override
+    protected Document doProcess(
+        Document data,
+        GlobalContext context,
+        CallMeta callMeta
+    ) {
+        logger.debug("FileUploadService.process");
+
+        Document doc = XMLUtils.newDocument();
+        ElementCreator ec = new ElementCreator(doc, null, null);
+        Element resp   = ec.create("response");
+        Element status = ec.create("status");
+        resp.appendChild(status);
+        doc.appendChild(resp);
+
+        String uuid = extractUuid(data);
+
+        byte[] fileData = extractData(data);
+        if (fileData != null) {
+            try {
+                String shapePath = FLYSUtils.getXPathString(
+                    FLYSUtils.XPATH_FLOODMAP_SHAPEFILE_DIR);
+
+                File artifactDir = FileTools.getDirectory(shapePath, uuid);
+                FileOutputStream fos =
+                    new FileOutputStream(
+                    new File(artifactDir, "user-rgd.zip"));
+                try {
+                    fos.write(fileData);
+
+                    // Write operation successful
+                    status.setTextContent("Upload erfolgreich!"); // TODO: i18n
+                }
+                finally {
+                    fos.close();
+                }
+            }
+            catch (IOException ioe) {
+                logger.warn(ioe, ioe);
+                status.setTextContent("Upload fehlgeschlagen!");
+            }
+        }
+        else {
+            logger.debug("No data in uploaded xml.");
+            status.setTextContent("Upload fehlgeschlagen!");
+        }
+
+        return doc;
+    }
+
+    /**
+     * Extracts the UUID from the XML document.
+     * @param data
+     * @return
+     */
+    protected String extractUuid(Document data) {
+        return XMLUtils.xpathString(
+            data, XPATH_ARTIFACT_UUID, ArtifactNamespaceContext.INSTANCE);
+    }
+
+    /**
+     * Extracts the base64 encoded ZIP file from the XML document.
+     * @param data
+     * @return
+     */
+    protected byte[] extractData(Document data) {
+        String b64Data = XMLUtils.xpathString(
+            data, XPATH_DATA, ArtifactNamespaceContext.INSTANCE);
+
+        if (b64Data != null && b64Data.length() > 0) {
+           byte[] fileData = Base64.decodeBase64(b64Data);
+           return fileData;
+        }
+        return 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/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/FixingsKMChartService.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,489 @@
+package org.dive4elements.river.artifacts.services;
+
+import org.dive4elements.artifactdatabase.DefaultService;
+
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.artifacts.GlobalContext;
+import org.dive4elements.artifacts.Service;
+
+import org.dive4elements.river.artifacts.model.FixingsColumn;
+import org.dive4elements.river.artifacts.model.FixingsColumnFactory;
+import org.dive4elements.river.artifacts.model.FixingsFilterBuilder;
+
+import org.dive4elements.river.artifacts.model.FixingsOverview.Fixing;
+
+import org.dive4elements.river.artifacts.model.FixingsOverview;
+import org.dive4elements.river.artifacts.model.FixingsOverviewFactory;
+import org.dive4elements.river.artifacts.model.GaugeFinder;
+import org.dive4elements.river.artifacts.model.GaugeFinderFactory;
+import org.dive4elements.river.artifacts.model.GaugeRange;
+
+import org.dive4elements.river.artifacts.model.fixings.QWI;
+
+import org.dive4elements.river.artifacts.resources.Resources;
+
+import org.dive4elements.river.backend.SessionHolder;
+
+import org.dive4elements.river.jfree.ShapeRenderer;
+
+import org.dive4elements.river.utils.Formatter;
+import org.dive4elements.river.utils.Pair;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Transparency;
+
+import java.awt.geom.Rectangle2D;
+
+import java.awt.image.BufferedImage;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.imageio.ImageIO;
+
+import org.apache.log4j.Logger;
+
+import org.jfree.chart.ChartFactory;
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.LegendItemCollection;
+
+import org.jfree.chart.axis.NumberAxis;
+
+import org.jfree.chart.plot.Marker;
+import org.jfree.chart.plot.PlotOrientation;
+import org.jfree.chart.plot.ValueMarker;
+import org.jfree.chart.plot.XYPlot;
+
+import org.jfree.data.Range;
+
+import org.jfree.ui.RectangleAnchor;
+import org.jfree.ui.TextAnchor;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+
+/** Serve chart of Fixings at certain km. */
+public class FixingsKMChartService
+extends      DefaultService
+{
+    private static final Logger log =
+        Logger.getLogger(FixingsKMChartService.class);
+
+    public static final int DEFAULT_WIDTH  = 240;
+    public static final int DEFAULT_HEIGHT = 180;
+
+    public static final String [] I18N_Q_SECTOR_BOARDERS = {
+        "fix.km.chart.q.sector.border0",
+        "fix.km.chart.q.sector.border1",
+        "fix.km.chart.q.sector.border2"
+    };
+
+    public static final String [] DEFAULT_Q_SECTOR_BORDERS = {
+        "(MNQ + MQ)/2",
+        "(MQ + MHQ)/2",
+        "HQ5"
+    };
+
+    public static final String I18N_CHART_LABEL_DATE =
+        "fix.km.chart.label.date";
+
+    public static final String DEFAULT_CHART_LABEL_DATE =
+        "yyyy/MM/dd";
+
+    public static final String I18N_CHART_TITLE =
+        "fix.km.chart.title";
+
+    public static final String DEFAULT_CHART_TITLE =
+        "Fixings {0} km {1,number,#.###}";
+
+    public static final String I18N_Q_AXIS =
+        "fix.km.chart.q.axis";
+
+    public static final String DEFAULT_Q_AXIS =
+        "Q [m\u00b3/s]";
+
+    public static final String I18N_W_AXIS =
+        "fix.km.chart.w.axis";
+
+    public static final String DEFAULT_W_AXIS =
+        "W [NN + m]";
+
+    public static final String I18N_MEASURED =
+        "fix.km.chart.measured";
+
+    public static final String DEFAULT_MEASURED =
+        "measured";
+
+    public static final String I18N_INTERPOLATED =
+        "fix.km.chart.interpolated";
+
+    public static final String DEFAULT_INTERPOLATED =
+        "interpolated";
+
+    public static final String DEFAULT_FORMAT = "png";
+
+    // TODO: Load fancy image from resources.
+    public static final byte [] EMPTY = {
+        (byte)0x89, (byte)0x50, (byte)0x4e, (byte)0x47,
+        (byte)0x0d, (byte)0x0a, (byte)0x1a, (byte)0x0a,
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x0d,
+        (byte)0x49, (byte)0x48, (byte)0x44, (byte)0x52,
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01,
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01,
+        (byte)0x08, (byte)0x00, (byte)0x00, (byte)0x00,
+        (byte)0x00, (byte)0x3a, (byte)0x7e, (byte)0x9b,
+        (byte)0x55, (byte)0x00, (byte)0x00, (byte)0x00,
+        (byte)0x01, (byte)0x73, (byte)0x52, (byte)0x47,
+        (byte)0x42, (byte)0x00, (byte)0xae, (byte)0xce,
+        (byte)0x1c, (byte)0xe9, (byte)0x00, (byte)0x00,
+        (byte)0x00, (byte)0x09, (byte)0x70, (byte)0x48,
+        (byte)0x59, (byte)0x73, (byte)0x00, (byte)0x00,
+        (byte)0x0b, (byte)0x13, (byte)0x00, (byte)0x00,
+        (byte)0x0b, (byte)0x13, (byte)0x01, (byte)0x00,
+        (byte)0x9a, (byte)0x9c, (byte)0x18, (byte)0x00,
+        (byte)0x00, (byte)0x00, (byte)0x07, (byte)0x74,
+        (byte)0x49, (byte)0x4d, (byte)0x45, (byte)0x07,
+        (byte)0xdc, (byte)0x04, (byte)0x04, (byte)0x10,
+        (byte)0x30, (byte)0x15, (byte)0x7d, (byte)0x77,
+        (byte)0x36, (byte)0x0b, (byte)0x00, (byte)0x00,
+        (byte)0x00, (byte)0x08, (byte)0x74, (byte)0x45,
+        (byte)0x58, (byte)0x74, (byte)0x43, (byte)0x6f,
+        (byte)0x6d, (byte)0x6d, (byte)0x65, (byte)0x6e,
+        (byte)0x74, (byte)0x00, (byte)0xf6, (byte)0xcc,
+        (byte)0x96, (byte)0xbf, (byte)0x00, (byte)0x00,
+        (byte)0x00, (byte)0x0a, (byte)0x49, (byte)0x44,
+        (byte)0x41, (byte)0x54, (byte)0x08, (byte)0xd7,
+        (byte)0x63, (byte)0xf8, (byte)0x0f, (byte)0x00,
+        (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x00,
+        (byte)0x1b, (byte)0xb6, (byte)0xee, (byte)0x56,
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+        (byte)0x49, (byte)0x45, (byte)0x4e, (byte)0x44,
+        (byte)0xae, (byte)0x42, (byte)0x60, (byte)0x82
+    };
+
+    private static final Output empty() {
+        return new Output(EMPTY, "image/png");
+    }
+
+    @Override
+    public Service.Output process(
+        Document      data,
+        GlobalContext globalContext,
+        CallMeta      callMeta
+    ) {
+        log.debug("FixingsKMChartService.process");
+
+        SessionHolder.acquire();
+        try {
+            return doProcess(data, globalContext, callMeta);
+        }
+        finally {
+            SessionHolder.HOLDER.get().close();
+            SessionHolder.release();
+        }
+    }
+
+    protected Service.Output doProcess(
+        Document      input,
+        GlobalContext globalContext,
+        CallMeta      callMeta
+    ) {
+        String    river  = getRiverName(input);
+        Double    km     = getKM(input);
+        Dimension extent = getExtent(input);
+        String    format = getFormat(input);
+
+        if (river == null || km == null) {
+            log.warn("River and/or km invalid.");
+            return empty();
+        }
+
+        FixingsOverview overview = FixingsOverviewFactory.getOverview(river);
+
+        if (overview == null) {
+            log.warn("No overview found for river '" + river + "'");
+            return empty();
+        }
+
+        FixingsFilterBuilder ffb = new FixingsFilterBuilder(input);
+
+        List<Fixing.Column> columns = overview.filter(
+            ffb.getRange(),
+            ffb.getFilter());
+
+        List<Pair<Fixing.Column, FixingsColumn>> cols =
+            new ArrayList<Pair<Fixing.Column, FixingsColumn>>();
+
+        for (Fixing.Column col: columns) {
+            FixingsColumn data =
+                FixingsColumnFactory.INSTANCE.getColumnData(col);
+            if (data != null) {
+                cols.add(new Pair<Fixing.Column, FixingsColumn>(col, data));
+            }
+        }
+
+        JFreeChart chart = createChart(cols, river, km, callMeta);
+
+        return encode(chart, extent, format);
+    }
+
+    protected static Output encode(
+        JFreeChart chart,
+        Dimension  extent,
+        String     format
+    ) {
+        BufferedImage image = chart.createBufferedImage(
+            extent.width, extent.height,
+            Transparency.BITMASK,
+            null);
+
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+        try {
+            ImageIO.write(image, format, out);
+        }
+        catch (IOException ioe) {
+            log.warn("writing image failed", ioe);
+            return empty();
+        }
+
+        return new Output(out.toByteArray(), "image/" + format);
+    }
+
+    protected static JFreeChart createChart(
+        List<Pair<Fixing.Column, FixingsColumn>> cols,
+        String      river,
+        double      km,
+        CallMeta    callMeta
+    ) {
+        String labelFormat = Resources.getMsg(
+            callMeta, I18N_CHART_LABEL_DATE, DEFAULT_CHART_LABEL_DATE);
+
+        QWSeriesCollection.LabelGenerator lg =
+            new QWSeriesCollection.DateFormatLabelGenerator(labelFormat);
+
+        QWSeriesCollection dataset = new QWSeriesCollection(lg);
+
+        double [] w = new double[1];
+        for (Pair<Fixing.Column, FixingsColumn> col: cols) {
+            boolean interpolated = !col.getB().getW(km, w);
+            double q = col.getB().getQ(km);
+            if (!Double.isNaN(w[0]) && !Double.isNaN(q)) {
+                QWI qw = new QWI(
+                    q, w[0],
+                    col.getA().getDescription(),
+                    col.getA().getStartTime(),
+                    interpolated, 0);
+                dataset.add(qw);
+            }
+        }
+
+        String title = Resources.format(
+            callMeta, I18N_CHART_TITLE, DEFAULT_CHART_TITLE, river, km);
+
+        String qAxis = Resources.getMsg(
+            callMeta, I18N_Q_AXIS, DEFAULT_Q_AXIS);
+
+        String wAxis = Resources.getMsg(
+            callMeta, I18N_W_AXIS, DEFAULT_W_AXIS);
+
+        JFreeChart chart = ChartFactory.createXYLineChart(
+            title,
+            qAxis,
+            wAxis,
+            null,
+            PlotOrientation.VERTICAL,
+            true,
+            true,
+            false);
+
+        XYPlot plot = (XYPlot)chart.getPlot();
+
+        NumberAxis qA = (NumberAxis)plot.getDomainAxis();
+        qA.setNumberFormatOverride(Formatter.getWaterlevelQ(callMeta));
+
+        NumberAxis wA = (NumberAxis)plot.getRangeAxis();
+        wA.setNumberFormatOverride(Formatter.getWaterlevelW(callMeta));
+
+        plot.setRenderer(0, dataset.createRenderer());
+        plot.setDataset(0, dataset);
+
+        Rectangle2D area = dataset.getArea();
+
+        if (area != null) {
+            double height = area.getHeight();
+            double wInset = Math.max(height, 0.01) * 0.25d;
+
+            wA.setAutoRangeIncludesZero(false);
+            wA.setRange(new Range(
+                area.getMinY() - wInset,
+                area.getMaxY() + wInset));
+        }
+
+        final String measuredS = Resources.getMsg(
+            callMeta, I18N_MEASURED, DEFAULT_MEASURED);
+
+        final String interpolatedS = Resources.getMsg(
+            callMeta, I18N_INTERPOLATED, DEFAULT_INTERPOLATED);
+
+        LegendItemCollection lic = plot.getLegendItems();
+        dataset.addLegendItems(lic, new ShapeRenderer.LabelGenerator() {
+            @Override
+            public String createLabel(ShapeRenderer.Entry entry) {
+                return entry.getFilled() ? measuredS : interpolatedS;
+            }
+        });
+        plot.setFixedLegendItems(lic);
+
+        applyQSectorMarkers(plot, river, km, callMeta);
+
+        chart.setBackgroundPaint(Color.white);
+        plot.setBackgroundPaint(Color.white);
+        plot.setDomainGridlinePaint(Color.gray);
+        plot.setRangeGridlinePaint(Color.gray);
+        plot.setDomainGridlinesVisible(true);
+        plot.setRangeGridlinesVisible(true);
+
+        return chart;
+    }
+
+    /** Add domain markers to plot that indicate Q-sectors. */
+    protected static void applyQSectorMarkers(
+        XYPlot   plot,
+        String   river,
+        double   km,
+        CallMeta meta
+    ) {
+        GaugeFinderFactory ggf = GaugeFinderFactory.getInstance();
+        GaugeFinder        gf  = ggf.getGaugeFinder(river);
+
+        if (gf == null) {
+            log.warn("No gauge finder found for river '" + river + "'");
+            return;
+        }
+
+        GaugeRange gr = gf.find(km);
+        if (gr == null) {
+            log.debug("No gauge range found for km "
+                + km + " on river " + river + ".");
+            return;
+        }
+
+        if (log.isDebugEnabled()) {
+            log.debug(gr);
+        }
+
+        for (int i = 0; i < I18N_Q_SECTOR_BOARDERS.length; ++i) {
+            String key   = I18N_Q_SECTOR_BOARDERS[i];
+            String def   = DEFAULT_Q_SECTOR_BORDERS[i];
+            String label = Resources.getMsg(meta, key, def);
+
+            Marker m = createQSectorMarker(
+                gr.getSectorBorder(i),
+                label);
+
+            if (m != null) {
+                plot.addDomainMarker(m);
+            }
+        }
+    }
+
+    /** Create Marker at value with label. */
+    protected static Marker createQSectorMarker(
+         double value, String label
+    ) {
+        if (Double.isNaN(value)) {
+            return null;
+        }
+        Marker m = new ValueMarker(value);
+        m.setPaint(Color.black);
+        m.setStroke(new BasicStroke());
+        m.setLabel(label);
+        m.setLabelAnchor(RectangleAnchor.TOP_LEFT);
+        m.setLabelTextAnchor(TextAnchor.TOP_LEFT);
+        return m;
+    }
+
+    protected static String getRiverName(Document input) {
+        NodeList rivers = input.getElementsByTagName("river");
+
+        if (rivers.getLength() == 0) {
+            return null;
+        }
+
+        String river = ((Element)rivers.item(0)).getAttribute("name");
+
+        return river.length() > 0 ? river : null;
+    }
+
+    protected static Double getKM(Document input) {
+        NodeList kms = input.getElementsByTagName("km");
+
+        if (kms.getLength() == 0) {
+            return null;
+        }
+
+        String km = ((Element)kms.item(0)).getAttribute("value");
+
+        try {
+            return Double.valueOf(km);
+        }
+        catch (NumberFormatException nfe) {
+            log.warn("Km '" + km + " is not a valid number.");
+            return null;
+        }
+    }
+
+    protected static Dimension getExtent(Document input) {
+
+        int width  = DEFAULT_WIDTH;
+        int height = DEFAULT_HEIGHT;
+
+        NodeList extents = input.getElementsByTagName("extent");
+
+        if (extents.getLength() > 0) {
+            Element element = (Element)extents.item(0);
+            String w = element.getAttribute("width");
+            String h = element.getAttribute("height");
+
+            try {
+                width = Math.max(1, Integer.parseInt(w));
+            }
+            catch (NumberFormatException nfe) {
+                log.warn("width '" + w + "' is not a valid.");
+            }
+
+            try {
+                height = Math.max(1, Integer.parseInt(h));
+            }
+            catch (NumberFormatException nfe) {
+                log.warn("height '" + h + "' is not a valid");
+            }
+        }
+
+        return new Dimension(width, height);
+    }
+
+    protected static String getFormat(Document input) {
+        String format = DEFAULT_FORMAT;
+
+        NodeList formats = input.getElementsByTagName("format");
+
+        if (formats.getLength() > 0) {
+            String type = ((Element)formats.item(0)).getAttribute("type");
+            if (type.length() > 0) {
+                format = type;
+            }
+        }
+
+        return format;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/FixingsOverviewService.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,63 @@
+package org.dive4elements.river.artifacts.services;
+
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.artifacts.GlobalContext;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+
+import org.dive4elements.river.artifacts.model.FixingsFilterBuilder;
+
+import org.dive4elements.river.artifacts.model.FixingsOverview.Fixing.Filter;
+
+import org.dive4elements.river.artifacts.model.Range;
+
+import org.dive4elements.river.artifacts.model.FixingsOverview;
+import org.dive4elements.river.artifacts.model.FixingsOverviewFactory;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+public class FixingsOverviewService
+extends      FLYSService
+{
+    private static Logger log =
+        Logger.getLogger(FixingsOverviewService.class);
+
+    public FixingsOverviewService() {
+    }
+
+    @Override
+    public Document doProcess(
+        Document      data,
+        GlobalContext globalContext,
+        CallMeta      callMeta
+    ) {
+        log.debug("FixingsOverviewService.doProcess");
+
+        Document document = XMLUtils.newDocument();
+
+        NodeList nodes = data.getElementsByTagName("river");
+
+        String river = nodes.getLength() > 0
+            ? ((Element)nodes.item(0)).getAttribute("name")
+            : "";
+
+        FixingsOverview overview = FixingsOverviewFactory.getOverview(river);
+
+        if (overview != null) {
+            FixingsFilterBuilder ffb = new FixingsFilterBuilder(data);
+            Range  range  = ffb.getRange();
+            Filter filter = ffb.getFilter();
+            overview.generateOverview(document, range, filter);
+        }
+        else {
+            log.warn("No overview for river '" + river + "' available.");
+        }
+
+        return document;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/GaugeInfoService.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,160 @@
+package org.dive4elements.river.artifacts.services;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.common.ArtifactNamespaceContext;
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator;
+
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.artifacts.GlobalContext;
+
+import org.dive4elements.river.model.Gauge;
+import org.dive4elements.river.model.Range;
+import org.dive4elements.river.model.River;
+
+import org.dive4elements.river.artifacts.model.RiverFactory;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class GaugeInfoService extends FLYSService {
+
+    interface Filter {
+        boolean apply(Gauge gauge);
+    }
+
+
+    private static final class ReferenceNumberFilter implements Filter {
+        private long refNr;
+
+        public ReferenceNumberFilter(long refNr) {
+            this.refNr = refNr;
+        }
+
+        @Override
+        public boolean apply(Gauge  gauge) {
+            if (logger.isDebugEnabled()) {
+                logger.debug("Test gauge '" + gauge.getName() + "'");
+            }
+
+            return gauge != null && gauge.getOfficialNumber() == refNr;
+        }
+    } // end of ReferenceNumberFilter class
+
+
+    /** The logger that is used by this service.*/
+    private static Logger logger = Logger.getLogger(GaugeInfoService.class);
+
+
+    public static final String XPATH_RIVERNAME = "/art:river/@name";
+
+    public static final String XPATH_REFERENCE_NR
+        = "/art:river/art:filter/art:gauge/text()";
+
+
+    public GaugeInfoService() {
+    }
+
+
+    @Override
+    public Document doProcess(
+        Document      data,
+        GlobalContext context,
+        CallMeta      callMeta
+    ) {
+        logger.debug("GaugeInfoService.process");
+
+        if (logger.isDebugEnabled()) {
+            logger.debug(XMLUtils.toString(data));
+        }
+
+        River river = getRiverFromRequest(data);
+
+        List<Filter> filters  = getFilters(data);
+        List<Gauge> allGauges = river.getGauges();
+        List<Gauge> filtered  = new ArrayList<Gauge>();
+
+        for (Gauge g: allGauges) {
+            for (Filter f: filters) {
+                if (f.apply(g)) {
+                    filtered.add(g);
+                    break;
+                }
+            }
+        }
+
+        return buildInfoDocument(filtered);
+    }
+
+
+    protected River getRiverFromRequest(Document data) {
+        String rivername = XMLUtils.xpathString(
+            data,
+            XPATH_RIVERNAME,
+            ArtifactNamespaceContext.INSTANCE);
+
+        logger.debug("Return Gauge info for River '" + rivername + "'");
+
+        return rivername != null ? RiverFactory.getRiver(rivername) : null;
+    }
+
+
+    protected List<Filter> getFilters(Document data) {
+        List<Filter> filters = new ArrayList<Filter>();
+
+        String refNr = XMLUtils.xpathString(
+            data,
+            XPATH_REFERENCE_NR,
+            ArtifactNamespaceContext.INSTANCE);
+
+        if (refNr != null && refNr.length() > 0) {
+            try {
+                filters.add(
+                    new ReferenceNumberFilter(Long.parseLong(refNr)));
+            }
+            catch (NumberFormatException nfe) {
+                logger.warn(nfe, nfe);
+            }
+        }
+
+        return filters;
+    }
+
+
+    protected Document buildInfoDocument(List<Gauge> gauges) {
+        Document doc = XMLUtils.newDocument();
+
+        ElementCreator cr = new ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element service = cr.create("service");
+
+        logger.debug("Append " + gauges.size() + " gauges to info doc.");
+
+        for (Gauge g: gauges) {
+            Range r = g.getRange();
+
+            Element el = cr.create("gauge");
+            cr.addAttr(el, "name", g.getName());
+            cr.addAttr(el, "lower", String.valueOf(r.getA().doubleValue()));
+            cr.addAttr(el, "upper", String.valueOf(r.getB().doubleValue()));
+
+            service.appendChild(el);
+        }
+
+        doc.appendChild(service);
+
+        return doc;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/GaugeOverviewInfoService.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,113 @@
+package org.dive4elements.river.artifacts.services;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.artifacts.GlobalContext;
+
+import org.dive4elements.river.model.Gauge;
+import org.dive4elements.river.model.MinMaxWQ;
+import org.dive4elements.river.model.Range;
+
+
+/**
+ * @author <a href="mailto:bjoern.ricks@intevation.de">Björn Ricks</a>
+ */
+public class GaugeOverviewInfoService extends RiverInfoService {
+
+    private static final Logger logger = Logger.getLogger(
+            GaugeOverviewInfoService.class);
+
+    @Override
+    public Document doProcess(
+        Document      data,
+        GlobalContext globalContext,
+        CallMeta      callMeta
+    ) {
+        Document result = super.doProcess(data, globalContext, callMeta);
+
+        logger.debug("GaugeOverviewInfoService.process");
+
+        Element egs = ec.create("gauges");
+
+        List<Gauge> gauges = river.getGauges();
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("Loaded gauges: " + gauges);
+        }
+
+        for (Gauge gauge: river.getGauges()) {
+            Element eg = ec.create("gauge");
+
+            String name = gauge.getName();
+            if (name != null) {
+                ec.addAttr(eg, "name", gauge.getName(), true);
+            }
+
+            String aeo = getStringValue(gauge.getAeo());
+            if (aeo != null) {
+                ec.addAttr(eg, "aeo", aeo, true);
+            }
+
+            String datum = getStringValue(gauge.getDatum());
+            if (datum != null) {
+                ec.addAttr(eg, "datum", datum, true);
+            }
+
+            Range range = gauge.getRange();
+            if (range != null) {
+                BigDecimal a = range.getA();
+                if (a != null) {
+                    double min = a.doubleValue();
+                    ec.addAttr(eg, "start", Double.toString(min), true);
+                }
+
+                BigDecimal b = range.getB();
+                if (b != null) {
+                    double max = range.getB().doubleValue();
+                    ec.addAttr(eg, "end", Double.toString(max), true);
+                }
+            }
+            MinMaxWQ minmaxwq = gauge.fetchMaxMinWQ();
+            String minw = getStringValue(minmaxwq.getMinW());
+            String maxw = getStringValue(minmaxwq.getMaxW());
+            String minq = getStringValue(minmaxwq.getMinQ());
+            String maxq = getStringValue(minmaxwq.getMaxQ());
+
+            if (minw != null) {
+                ec.addAttr(eg, "minw", minw, true);
+            }
+            if (maxw != null) {
+                ec.addAttr(eg, "maxw", maxw, true);
+            }
+            if (minq != null) {
+                ec.addAttr(eg, "minq", minq, true);
+            }
+            if (maxq != null) {
+                ec.addAttr(eg, "maxq", maxq, true);
+            }
+
+            String station = getStringValue(gauge.getStation());
+            if (station != null) {
+                ec.addAttr(eg, "station", station, true);
+            }
+
+            Long official = gauge.getOfficialNumber();
+            if (official != null) {
+                ec.addAttr(eg, "official", official.toString(), true);
+            }
+
+            egs.appendChild(eg);
+        }
+
+        riverele.appendChild(egs);
+
+        return result;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/MainValuesService.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,304 @@
+package org.dive4elements.river.artifacts.services;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.artifacts.GlobalContext;
+
+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.model.Gauge;
+import org.dive4elements.river.model.MainValue;
+import org.dive4elements.river.model.MainValueType;
+import org.dive4elements.river.model.NamedMainValue;
+import org.dive4elements.river.model.Range;
+import org.dive4elements.river.model.River;
+
+import org.dive4elements.river.artifacts.model.RiverFactory;
+
+
+/**
+ * This service returns the main values of a river's gauge based on the start
+ * and end point of the river.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class MainValuesService extends FLYSService {
+
+    /** The logger that is used by this service.*/
+    private static Logger logger = Logger.getLogger(MainValuesService.class);
+
+
+    /** The XPath that points to the river definition of the incoming request.*/
+    public static final String XPATH_RIVER = "/art:mainvalues/art:river/text()";
+
+    /** The XPath that points to the start definition of the incoming request.*/
+    public static final String XPATH_START = "/art:mainvalues/art:start/text()";
+
+    /** The XPath that points to the end definition of the incoming request.*/
+    public static final String XPATH_END = "/art:mainvalues/art:end/text()";
+
+    /**
+     * The default constructor.
+     */
+    public MainValuesService() {
+    }
+
+    private static final Document error(String msg) {
+        logger.debug(msg);
+        return XMLUtils.newDocument();
+    }
+
+
+    @Override
+    public Document doProcess(
+        Document      data,
+        GlobalContext context,
+        CallMeta      callMeta
+    ) {
+        logger.debug("MainValuesService.process");
+
+        River river = getRequestedRiver(data);
+        if (river == null) {
+            return error("no river found.");
+        }
+
+        double[] minmax = getRequestedStartEnd(data, river);
+        Gauge gauge = river.determineGauge(minmax[0], minmax[1]);
+
+        if (gauge == null) {
+            return error("no gauge found.");
+        }
+
+        List<MainValue> mainValues = getMainValues(river, gauge);
+
+        return buildDocument(river, gauge, mainValues, context);
+    }
+
+
+    /**
+     * This method extracts the river from the incoming request. If no river
+     * string was found or no river is found in the database based on this
+     * string a NullPointerException is thrown.
+     *
+     * @param data The incoming request data.
+     *
+     * @return the River object.
+     */
+    protected River getRequestedRiver(Document data)
+    throws    NullPointerException
+    {
+        logger.debug("MainValuesService.getRiver");
+
+        String riverStr = XMLUtils.xpathString(
+            data, XPATH_RIVER, ArtifactNamespaceContext.INSTANCE);
+
+         return riverStr != null && (riverStr = riverStr.trim()).length() > 0
+            ? RiverFactory.getRiver(riverStr)
+            : null;
+    }
+
+
+    /**
+     * This method extracts the start and end point from incoming request
+     * document and returns both values in an array. If no start and end strings
+     * are found in the document, the min/max values of the <i>river</i> are
+     * returned.
+     *
+     * @param data The incoming request data.
+     * @param river The river of the request.
+     *
+     * @return the start and end point.
+     */
+    protected double[] getRequestedStartEnd(Document data, River river) {
+        logger.debug("MainValuesService.getStartEnd");
+
+        String startStr = XMLUtils.xpathString(
+            data, XPATH_START, ArtifactNamespaceContext.INSTANCE);
+
+        String endStr = XMLUtils.xpathString(
+            data, XPATH_END, ArtifactNamespaceContext.INSTANCE);
+
+        if (startStr == null || endStr == null) {
+            return river.determineMinMaxDistance();
+        }
+
+        try {
+            double start = Double.parseDouble(startStr);
+            double end   = Double.parseDouble(endStr);
+
+            if (logger.isDebugEnabled()) {
+                logger.debug("Found start: " + start);
+                logger.debug("Found end: " + end);
+            }
+
+            return new double[] { start, end };
+        }
+        catch (NumberFormatException nfe) {
+            logger.warn(nfe, nfe);
+            return river.determineMinMaxDistance();
+        }
+    }
+
+
+    /**
+     * This method creates the result document that includes the main values of
+     * the specified <i>gauge</i>.
+     *
+     * @param river The river.
+     * @param gauge The gauge.
+     *
+     * @return a document that includes the main values of the specified river
+     * at the specified gauge.
+     */
+    protected List<MainValue> getMainValues(River river, Gauge gauge) {
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("MainValuesService.buildMainValues");
+            logger.debug("River: " + river.getName());
+            logger.debug("Gauge: " + gauge.getName());
+        }
+
+        List<MainValue> mainValues = gauge.getMainValues();
+
+        if (logger.isDebugEnabled()) {
+            logger.debug(mainValues.size() + " main values found.");
+        }
+
+        return mainValues;
+    }
+
+
+    protected Document buildDocument(
+        River           river,
+        Gauge           gauge,
+        List<MainValue> mainValues,
+        Object          context)
+    {
+        logger.debug("MainValuesService.buildDocument");
+
+        Document doc = XMLUtils.newDocument();
+
+        ElementCreator cr = new ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element rootEl = cr.create("service");
+        cr.addAttr(rootEl, "name", "mainvalues");
+
+        doc.appendChild(rootEl);
+
+        appendMetaInformation(doc, rootEl, river, gauge, context);
+        appendMainValues(doc, rootEl, mainValues, context);
+
+        return doc;
+    }
+
+
+    /**
+     * This method appends some meta information to the result document.
+     * Currently, the river's and gauge's names and the gauge's range are
+     * appended.
+     *
+     * @param root The root element of the result document.
+     * @param river The river.
+     * @param gauge The gauge.
+     * @param context The context object.
+     */
+    protected void appendMetaInformation(
+        Document doc,
+        Element  root,
+        River    river,
+        Gauge    gauge,
+        Object   context)
+    {
+        logger.debug("MainValuesService.appendMetaInformation");
+
+        ElementCreator cr = new ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Range range = gauge.getRange();
+
+        Element riverEl = cr.create("river");
+        cr.addAttr(riverEl, "name", river.getName());
+
+        Element gaugeEl = cr.create("gauge");
+        cr.addAttr(gaugeEl, "name", gauge.getName());
+        cr.addAttr(gaugeEl, "from", range.getA().toString());
+        cr.addAttr(gaugeEl, "to", range.getB().toString());
+
+        root.appendChild(riverEl);
+        root.appendChild(gaugeEl);
+    }
+
+
+    protected void appendMainValues(
+        Document        doc,
+        Element         root,
+        List<MainValue> mainValues,
+        Object          context)
+    {
+        logger.debug("MainValuesService.appendMainValues");
+
+        ElementCreator cr = new ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element list = cr.create("mainvalues");
+
+        for (MainValue mainValue: mainValues) {
+            Element newEl = buildMainValueElement(doc, mainValue, context);
+
+            if (newEl != null) {
+                list.appendChild(newEl);
+            }
+        }
+
+        root.appendChild(list);
+    }
+
+
+    /**
+     * This method builds a concrete mainvalue element. This element consists of
+     * three attributes: the value, its name and its type.
+     *
+     * @param doc The owner document.
+     * @param mainValue The mainvalue.
+     * @param context The context object.
+     *
+     * @return a mainvalue element.
+     */
+    protected Element buildMainValueElement(
+        Document  doc,
+        MainValue mainValue,
+        Object    context)
+    {
+        ElementCreator cr = new ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        NamedMainValue namedMainValue = mainValue.getMainValue();
+        MainValueType  mainValueType  = namedMainValue.getType();
+
+        Element el = cr.create("mainvalue");
+
+        cr.addAttr(el, "value", mainValue.getValue().toString());
+        cr.addAttr(el, "name", namedMainValue.getName());
+        cr.addAttr(el, "type", mainValueType.getName());
+
+        return el;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/MapInfoService.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,180 @@
+package org.dive4elements.river.artifacts.services;
+
+import com.vividsolutions.jts.geom.Envelope;
+
+import org.dive4elements.artifactdatabase.XMLService;
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.artifacts.GlobalContext;
+import org.dive4elements.artifacts.common.ArtifactNamespaceContext;
+import org.dive4elements.artifacts.common.utils.Config;
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator;
+import org.dive4elements.river.utils.GeometryUtils;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.xml.xpath.XPathConstants;
+
+import org.apache.log4j.Logger;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+/**
+ * This service provides information about the supported rivers by this
+ * application.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class MapInfoService extends XMLService {
+
+    /** XPath that points to the river.*/
+    public static final String XPATH_RIVER = "/mapinfo/river/text()";
+
+    public static final String XPATH_MAPTYPE = "/mapinfo/maptype/text()";
+
+    private static final String XPATH_RIVER_PROJECTION =
+        "/artifact-database/*[local-name()=$maptype]/river[@name=$river]/srid/@value";
+
+    private static final String XPATH_RIVER_BACKGROUND =
+        "/artifact-database/*[local-name()=$maptype]/river[@name=$river]/background-wms";
+
+    private static final String XPATH_RIVER_WMS =
+        "/artifact-database/*[local-name()=$maptype]/river[@name=$river]/river-wms";
+
+
+    /** The logger used in this service.*/
+    private static Logger logger = Logger.getLogger(MapInfoService.class);
+
+
+    /**
+     * The default constructor.
+     */
+    public MapInfoService() {
+    }
+
+    protected static String getStringXPath(
+        String              query,
+        Map<String, String> variables
+    ) {
+        return (String)XMLUtils.xpath(
+            Config.getConfig(), query, XPathConstants.STRING,
+            null, variables);
+    }
+
+    protected static Node getNodeXPath(
+        String              query,
+        Map<String, String> variables
+    ) {
+        return (Node)XMLUtils.xpath(
+            Config.getConfig(), query, XPathConstants.NODE,
+            null, variables);
+    }
+
+    @Override
+    public Document processXML(
+        Document      data,
+        GlobalContext globalContext,
+        CallMeta      callMeta
+    ) {
+        logger.debug("MapInfoService.process");
+
+        Document result   = XMLUtils.newDocument();
+        ElementCreator cr = new ElementCreator(result, null, null);
+
+        Element mapinfo = cr.create("mapinfo");
+        result.appendChild(mapinfo);
+
+        String river = extractRiver(data);
+        if (river == null || river.length() == 0) {
+            logger.warn("Cannot generate information: river is empty!");
+            return result;
+        }
+
+        String mapType = extractMaptype(data);
+        if (mapType == null
+        || !(mapType.equals("floodmap") || mapType.equals("rivermap"))) {
+            mapType = "floodmap";
+        }
+
+        Element root = cr.create("river");
+        cr.addAttr(root, "name", river);
+        mapinfo.appendChild(root);
+
+        Envelope env = GeometryUtils.getRiverBoundary(river);
+        if (env != null) {
+            String bounds = GeometryUtils.jtsBoundsToOLBounds(env);
+            if (logger.isDebugEnabled()) {
+                logger.debug("River '" + river + "' bounds: " + bounds);
+            }
+
+            Element bbox = cr.create("bbox");
+            cr.addAttr(bbox, "value", bounds);
+            root.appendChild(bbox);
+        }
+
+        Map<String, String> vars = new HashMap<String, String>();
+        vars.put("maptype", mapType);
+        vars.put("river", river);
+
+        String sridStr = getStringXPath(XPATH_RIVER_PROJECTION, vars);
+
+        if (sridStr != null && sridStr.length() > 0) {
+            Element srid = cr.create("srid");
+            cr.addAttr(srid, "value", sridStr);
+            root.appendChild(srid);
+        }
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("processXML: " + XMLUtils.toString(root));
+        }
+
+        root.appendChild(
+            createWMSElement("background-wms",
+                XPATH_RIVER_BACKGROUND, vars, cr));
+
+        root.appendChild(
+            createWMSElement("river-wms",
+                XPATH_RIVER_WMS, vars, cr));
+
+        return result;
+    }
+
+
+    protected Element createWMSElement(
+        String elementName,
+        String xpath,
+        Map<String, String> vars,
+        ElementCreator cr)
+    {
+        logger.debug("createWMSElement()");
+
+        Element el = cr.create(elementName);
+        Element wms = (Element)getNodeXPath(xpath, vars);
+
+        if (wms != null) {
+            cr.addAttr(el, "url", wms.getAttribute("url"));
+            cr.addAttr(el, "layers", wms.getAttribute("layers"));
+
+            logger.debug("createWMSElement: " + XMLUtils.toString(el));
+        }
+        else {
+            logger.debug("createWMSElement: wms == null");
+        }
+
+        return el;
+    }
+
+
+    private static String extractRiver(Document data) {
+        return XMLUtils.xpathString(
+            data, XPATH_RIVER, ArtifactNamespaceContext.INSTANCE);
+    }
+
+    private static String extractMaptype(Document data) {
+        return XMLUtils.xpathString(
+            data, XPATH_MAPTYPE, ArtifactNamespaceContext.INSTANCE);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/MeasurementStationInfoService.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,124 @@
+package org.dive4elements.river.artifacts.services;
+
+import java.math.BigDecimal;
+import java.text.DateFormat;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.artifacts.GlobalContext;
+
+import org.dive4elements.river.model.MeasurementStation;
+import org.dive4elements.river.model.Range;
+import org.dive4elements.river.model.TimeInterval;
+
+/**
+ * @author <a href="mailto:bjoern.ricks@intevation.de">Björn Ricks</a>
+ */
+public class MeasurementStationInfoService extends RiverInfoService {
+
+    private static final Logger logger = Logger.getLogger(
+            MeasurementStationInfoService.class);
+
+    public static final DateFormat DATE_FORMAT = DateFormat.getDateInstance(
+        DateFormat.SHORT, Locale.GERMANY);
+
+    @Override
+    public Document doProcess(
+        Document      data,
+        GlobalContext globalContext,
+        CallMeta      callMeta
+    ) {
+        Document result = super.doProcess(data, globalContext, callMeta);
+
+        Element egs = ec.create("measurement-stations");
+
+        List<MeasurementStation> mstations = river.getMeasurementStations();
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("Loaded stations: " + mstations);
+        }
+
+        for (MeasurementStation mstation: mstations) {
+            Element eg = ec.create("measurement-station");
+
+            String name = mstation.getName();
+            if (name != null) {
+                ec.addAttr(eg, "name", name, true);
+            }
+
+            Integer id = mstation.getId();
+            if (id != null) {
+                ec.addAttr(eg, "id", Integer.toString(id), true);
+            }
+
+            String type = mstation.getMeasurementType();
+            if (type != null) {
+                ec.addAttr(eg, "type", type, true);
+            }
+
+            String riverside = mstation.getRiverside();
+            if (riverside != null) {
+                ec.addAttr(eg, "riverside", riverside, true);
+            }
+
+            Double station = mstation.getStation();
+            if (station != null) {
+                ec.addAttr(eg, "station", Double.toString(station), true);
+            }
+
+            Range range = mstation.getRange();
+            if (range != null) {
+                BigDecimal a = range.getA();
+                if (a != null) {
+                    ec.addAttr(eg, "start", getStringValue(a), true);
+                }
+
+                BigDecimal b = range.getB();
+                if (b != null) {
+                    ec.addAttr(eg, "end", getStringValue(b), true);
+                }
+            }
+
+            String moperator = mstation.getOperator();
+            if (moperator != null) {
+                ec.addAttr(eg, "operator", moperator, true);
+            }
+
+            TimeInterval tinterval = mstation.getObservationTimerange();
+            if (tinterval != null) {
+                Date tstart = tinterval.getStartTime();
+                if (tstart != null) {
+                    ec.addAttr(eg, "starttime", DATE_FORMAT.format(tstart),
+                            true);
+                }
+                Date tstop = tinterval.getStopTime();
+                if (tstop != null) {
+                    ec.addAttr(eg, "stoptime", DATE_FORMAT.format(tstop),
+                            true);
+                }
+            }
+
+            String gaugename= mstation.getGaugeName();
+
+            if (gaugename != null) {
+                Element egauge = ec.create("gauge");
+                ec.addAttr(egauge, "name", gaugename, true);
+                eg.appendChild(egauge);
+            }
+
+            egs.appendChild(eg);
+        }
+
+        this.riverele.appendChild(egs);
+
+        return result;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/MetaDataService.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,191 @@
+package org.dive4elements.river.artifacts.services;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+import java.util.Map;
+import java.util.HashMap;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.artifacts.GlobalContext;
+import org.dive4elements.artifacts.ArtifactDatabase;
+import org.dive4elements.artifacts.ArtifactDatabaseException;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+import org.dive4elements.artifacts.common.utils.StringUtils;
+
+import org.dive4elements.artifacts.common.ArtifactNamespaceContext;
+
+import org.dive4elements.river.artifacts.datacage.Recommendations;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+
+/**
+ * Following XPaths are evaluated on the incoming document.
+ *
+ *  "/art:meta/art:artifact-id/@value" The UUID of the artifact. Optional.
+ *                                     Used to fill the template enviroment.
+ *  "/art:meta/art:user-id/@value"     The UUID of the user. Optional.
+ *                                     If given the user specific template is filled.
+ *  "/art:meta/art:outs/@value"        The list of outs used to recommend for the
+ *                                     various outputs.
+ *  "/art:meta/art:parameters/@value"  A list of key/value pairs to inject more
+ *                                     filters to the templating, as "key:value;key2:value2"
+ */
+public class MetaDataService
+extends      FLYSService
+{
+    private static Logger log = Logger.getLogger(MetaDataService.class);
+
+    public static final String XPATH_ARTIFACT_ID = "/art:meta/art:artifact-id/@value";
+    public static final String XPATH_USER_ID     = "/art:meta/art:user-id/@value";
+    public static final String XPATH_OUTS        = "/art:meta/art:outs/@value";
+    public static final String XPATH_PARAMETERS  = "/art:meta/art:parameters/@value";
+
+    /** The global context key of the artifact database. */
+    public static final String ARTIFACT_DATA_BASE_KEY =
+        "global.artifact.database";
+
+    public MetaDataService() {
+    }
+
+    @Override
+    protected Document doProcess(
+        Document      data,
+        GlobalContext globalContext,
+        CallMeta      callMeta
+    ) {
+        log.debug("MetaDataService.process");
+
+        String artifactId = XMLUtils.xpathString(
+            data, XPATH_ARTIFACT_ID, ArtifactNamespaceContext.INSTANCE);
+
+        if (artifactId != null
+        && (artifactId = artifactId.trim()).length() == 0) {
+            artifactId = null;
+        }
+
+        String userId = XMLUtils.xpathString(
+            data, XPATH_USER_ID, ArtifactNamespaceContext.INSTANCE);
+
+        if (userId != null
+        && (userId = userId.trim()).length() == 0) {
+            userId = null;
+        }
+
+        String outs = XMLUtils.xpathString(
+            data, XPATH_OUTS, ArtifactNamespaceContext.INSTANCE);
+
+        String parameters = XMLUtils.xpathString(
+            data, XPATH_PARAMETERS, ArtifactNamespaceContext.INSTANCE);
+
+        return doService(
+            artifactId, userId, outs, parameters, globalContext);
+    }
+
+
+    /**
+     * Split parameterstring in the form of key1:value1;key2:value2
+     * into hash (key1->value1, key2->value2).
+     * @param parameters "key1:value1;key2:value2"
+     * @param data Map into wich to put parameter hash and return.
+     * @return parameter data
+     */
+    protected static Map<String, Object> splitParameters(
+        String              parameters,
+        Map<String, Object> data
+    ) {
+        if (parameters != null) {
+            String [] parts = parameters.split("\\s*;\\s*");
+            for (String part: parts) {
+                String [] kv = part.split("\\s*:\\s*");
+                if (kv.length < 2 || (kv[0] = kv[0].trim()).length() == 0) {
+                    continue;
+                }
+                String [] values = kv[1].split("\\s*,\\s*");
+                data.put(kv[0], values.length == 1 ? values[0] : values);
+            }
+        }
+        return data;
+    }
+
+    /** Return the document containing matched stuff from meta-data.xml. */
+    protected Document doService(
+        String        artifactId,
+        String        userId,
+        String        outsString,
+        String        parameters,
+        GlobalContext globalContext
+    ) {
+        Document result = XMLUtils.newDocument();
+
+        FLYSArtifact flysArtifact;
+
+        if (log.isDebugEnabled()) {
+            log.debug("artifact  : " + artifactId);
+            log.debug("user      : " + userId);
+            log.debug("outs      : " + outsString);
+            log.debug("parameters: " + parameters);
+        }
+
+        if (userId != null && !StringUtils.checkUUID(userId)) {
+            log.warn("'" + userId + "' is not a UUID");
+            return result;
+        }
+
+        if (artifactId != null) {
+            if (!StringUtils.checkUUID(artifactId)) {
+                log.warn("'" + artifactId + "' is not a UUID");
+                return result;
+            }
+
+            Object dbObject =
+                (ArtifactDatabase)globalContext.get(ARTIFACT_DATA_BASE_KEY);
+
+            if (!(dbObject instanceof ArtifactDatabase)) {
+                log.error("Cannot find artifact database");
+                return result;
+            }
+
+            ArtifactDatabase db = (ArtifactDatabase)dbObject;
+
+            Artifact artifact;
+
+            try {
+                artifact = db.getRawArtifact(artifactId);
+            }
+            catch (ArtifactDatabaseException adbe) {
+                log.warn("fetching artifact failed", adbe);
+                return result;
+            }
+
+            if (!(artifact instanceof FLYSArtifact)) {
+                log.warn("artifact is not a FLYS artifact.");
+                return result;
+            }
+
+            flysArtifact = (FLYSArtifact)artifact;
+        }
+        else {
+            flysArtifact = null;
+        }
+
+
+        Map<String, Object> data = splitParameters(
+            parameters, new HashMap<String, Object>());
+
+        String [] outs = outsString == null
+            ? new String [0]
+            : outsString.split("\\s*,\\s*");
+
+        Recommendations rec = Recommendations.getInstance();
+        rec.recommend(
+            flysArtifact, userId, outs, data, result);
+
+        return result;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/ModuleService.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,62 @@
+package org.dive4elements.river.artifacts.services;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.artifacts.GlobalContext;
+import org.dive4elements.artifacts.ArtifactNamespaceContext;
+import org.dive4elements.river.artifacts.model.Module;
+import org.dive4elements.river.artifacts.context.FLYSContext;
+import org.dive4elements.river.artifacts.resources.Resources;
+
+public class ModuleService extends FLYSService {
+
+    private static final String MODULE = "module";
+
+    private static Logger logger = Logger.getLogger(ModuleService.class);
+
+    protected Document doProcess(
+        Document      data,
+        GlobalContext globalContext,
+        CallMeta      callMeta
+    ) {
+        logger.debug("ModuleService.process");
+
+        Document result = XMLUtils.newDocument();
+
+        XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(
+            result,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element em = ec.create("modules");
+        List<Module> modules = (List<Module>)globalContext.get(FLYSContext.MODULES);
+
+        for (Module module : modules) {
+            Element m = ec.create("module");
+            ec.addAttr(m, "name", module.getName(), true);
+            String localname = Resources.getMsg(callMeta,
+                    MODULE + "." + module.getName(), module.getName());
+            ec.addAttr(m, "localname", localname, true);
+
+            if (module.isSelected()) {
+                ec.addAttr(m, "selected", "true", true);
+            }
+
+            em.appendChild(m);
+        }
+
+        result.appendChild(em);
+
+        return result;
+    }
+}
+
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 tw=80:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/QWSeriesCollection.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,227 @@
+package org.dive4elements.river.artifacts.services;
+
+import org.dive4elements.river.artifacts.model.fixings.QWI;
+
+import org.dive4elements.river.java2d.ShapeUtils;
+
+import org.dive4elements.river.jfree.ShapeRenderer;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Paint;
+import java.awt.Shape;
+
+import java.awt.geom.Rectangle2D;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.jfree.chart.LegendItem;
+import org.jfree.chart.LegendItemCollection;
+
+import org.jfree.chart.labels.XYItemLabelGenerator;
+
+import org.jfree.chart.renderer.xy.StandardXYItemRenderer;
+
+import org.jfree.data.xy.XYDataset;
+import org.jfree.data.xy.XYSeries;
+import org.jfree.data.xy.XYSeriesCollection;
+
+public class QWSeriesCollection
+extends      XYSeriesCollection
+implements   XYItemLabelGenerator
+{
+    public interface LabelGenerator {
+        String createLabel(QWI qw);
+    } // interface LabelGenerator
+
+    public static class DateFormatLabelGenerator
+    implements          LabelGenerator
+    {
+        protected DateFormat format;
+
+        public DateFormatLabelGenerator() {
+            this("dd.MM.yyyy");
+        }
+
+        public DateFormatLabelGenerator(String format) {
+            this(new SimpleDateFormat(format));
+        }
+
+        public DateFormatLabelGenerator(DateFormat format) {
+            this.format = format;
+        }
+
+        @Override
+        public String createLabel(QWI qw) {
+            Date date = qw.getDate();
+            return date != null ? format.format(date) : "";
+        }
+    } // class DateFormatLabelGenerator
+
+    public static final LabelGenerator SIMPLE_GENERATOR =
+        new DateFormatLabelGenerator();
+
+    protected Date minDate;
+    protected Date maxDate;
+
+    protected List<List<QWI>> labels;
+
+    protected Rectangle2D area;
+
+    protected LabelGenerator labelGenerator;
+
+    protected Map<ShapeRenderer.Entry, Integer> knownShapes =
+        new HashMap<ShapeRenderer.Entry, Integer>();
+
+    public QWSeriesCollection() {
+        labels = new ArrayList<List<QWI>>();
+        labelGenerator = SIMPLE_GENERATOR;
+    }
+
+    public QWSeriesCollection(LabelGenerator labelGenerator) {
+        this();
+        this.labelGenerator = labelGenerator;
+    }
+
+    protected static ShapeRenderer.Entry classify(QWI qw) {
+        boolean interpolated = qw.getInterpolated();
+
+        Shape shape = interpolated
+            ? ShapeUtils.INTERPOLATED_SHAPE
+            : ShapeUtils.MEASURED_SHAPE;
+
+        boolean filled = !interpolated;
+        Color color = Color.blue;
+
+        return new ShapeRenderer.Entry(shape, color, filled);
+    }
+
+    public void add(QWI qw) {
+
+        ShapeRenderer.Entry key = classify(qw);
+
+        Integer seriesNo = knownShapes.get(key);
+
+        XYSeries series;
+
+        if (seriesNo == null) {
+            seriesNo = Integer.valueOf(getSeriesCount());
+            knownShapes.put(key, seriesNo);
+            series = new XYSeries(seriesNo, false);
+            addSeries(series);
+            labels.add(new ArrayList<QWI>());
+        }
+        else {
+            series = getSeries(seriesNo);
+        }
+
+        series.add(qw.getQ(), qw.getW());
+
+        labels.get(seriesNo).add(qw);
+
+        extendDateRange(qw);
+        extendArea(qw);
+    }
+
+    protected void extendDateRange(QWI qw) {
+        Date date = qw.getDate();
+        if (date != null) {
+            if (minDate == null) {
+                minDate = maxDate = date;
+            }
+            else {
+                if (date.compareTo(minDate) < 0) {
+                    minDate = date;
+                }
+                if (date.compareTo(maxDate) > 0) {
+                    maxDate = date;
+                }
+            }
+        }
+    }
+
+    protected void extendArea(QWI qw) {
+        if (area == null) {
+            area = new Rectangle2D.Double(
+                qw.getQ(), qw.getW(), 0d, 0d);
+        }
+        else {
+            area.add(qw.getQ(), qw.getW());
+        }
+    }
+
+    public Rectangle2D getArea() {
+        return area;
+    }
+
+    public Date getMinDate() {
+        return minDate;
+    }
+
+    public Date getMaxDate() {
+        return maxDate;
+    }
+
+    public LabelGenerator getLabelGenerator() {
+        return labelGenerator;
+    }
+
+    @Override
+    public String generateLabel(XYDataset dataset, int series, int item) {
+        return labelGenerator.createLabel(labels.get(series).get(item));
+    }
+
+    public StandardXYItemRenderer createRenderer() {
+        StandardXYItemRenderer renderer = new ShapeRenderer(knownShapes);
+        renderer.setBaseItemLabelGenerator(this);
+        renderer.setBaseSeriesVisibleInLegend(false);
+        renderer.setBaseItemLabelsVisible(true);
+        return renderer;
+    }
+
+    public static final LegendItem legendItem(
+        String  label,
+        Paint   paint,
+        Shape   shape,
+        boolean filled
+    ) {
+        BasicStroke stroke = new BasicStroke();
+        return new LegendItem(
+            label,  // label
+            null,   // description
+            null,   // tooltip
+            null,   // url
+            true,   // shape visible
+            shape,  // shape
+            filled, // shape filled
+            filled ? paint : Color.white, // fill paint
+            true,   // shape outline
+            paint,  // outline paint
+            stroke, // outline stroke
+            false,  // line visible
+            shape,  // line
+            stroke, // line stroke
+            Color.white);
+    }
+
+    public void addLegendItems(
+        LegendItemCollection         lic,
+        ShapeRenderer.LabelGenerator lg
+    ) {
+        for (ShapeRenderer.Entry entry: knownShapes.keySet()) {
+            lic.add(legendItem(
+                lg.createLabel(entry),
+                entry.getPaint(),
+                entry.getShape(),
+                entry.getFilled()));
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/RiverInfoService.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,85 @@
+package org.dive4elements.river.artifacts.services;
+
+import java.math.BigDecimal;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.artifacts.GlobalContext;
+import org.dive4elements.artifacts.common.ArtifactNamespaceContext;
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+
+import org.dive4elements.river.artifacts.model.RiverFactory;
+import org.dive4elements.river.model.River;
+
+/**
+ * @author <a href="mailto:bjoern.ricks@intevation.de">Björn Ricks</a>
+ */
+public class RiverInfoService extends FLYSService {
+
+    private static final Logger logger = Logger.getLogger(
+            RiverInfoService.class);
+
+    protected static final String RIVER_XPATH = "/art:river/text()";
+
+    protected XMLUtils.ElementCreator ec;
+    protected River river;
+    protected Element riverele;
+
+    protected Document doProcess(
+        Document      data,
+        GlobalContext globalContext,
+        CallMeta      callMeta
+    ) {
+        String rivername = XMLUtils.xpathString(
+            data, RIVER_XPATH, ArtifactNamespaceContext.INSTANCE);
+
+        river = RiverFactory.getRiver(rivername);
+
+        Document result = XMLUtils.newDocument();
+
+        if (river == null) {
+            logger.warn("No river with name " + rivername + " found.");
+            return null;
+        }
+
+        ec = new XMLUtils.ElementCreator(
+            result,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        riverele = ec.create("river-info");
+
+        double[] minmax  = river.determineMinMaxDistance();
+        double[] minmaxq = river.determineMinMaxQ();
+
+        Element r = ec.create("river");
+        ec.addAttr(r, "name", river.getName(), true);
+        ec.addAttr(r, "start", Double.toString(minmax[0]), true);
+        ec.addAttr(r, "end", Double.toString(minmax[1]), true);
+        ec.addAttr(r, "wstunit", river.getWstUnit().getName(), true);
+        ec.addAttr(r, "kmup", Boolean.toString(river.getKmUp()), true);
+        ec.addAttr(r, "minq", Double.toString(minmaxq[0]), true);
+        ec.addAttr(r, "maxq", Double.toString(minmaxq[1]), true);
+        ec.addAttr(r, "official", Long.toString(river.getOfficialNumber()),
+                    true);
+
+        riverele.appendChild(r);
+        result.appendChild(riverele);
+
+        return result;
+    }
+
+    /**
+     * Returns a Double as String from a BigDecimal value.
+     *
+     * If value is null an empty String is returned.
+     */
+    protected static String getStringValue(BigDecimal value) {
+        return value != null
+            ? Double.toString(value.doubleValue()) : "";
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/RiverService.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,63 @@
+package org.dive4elements.river.artifacts.services;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.artifacts.GlobalContext;
+
+import org.dive4elements.artifacts.common.ArtifactNamespaceContext;
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+
+import org.dive4elements.river.model.River;
+
+import org.dive4elements.river.artifacts.model.RiverFactory;
+
+
+/**
+ * This service provides information about the supported rivers by this
+ * application.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class RiverService extends FLYSService {
+
+    /** The logger used in this service.*/
+    private static Logger logger = Logger.getLogger(RiverService.class);
+
+
+    protected Document doProcess(
+        Document      data,
+        GlobalContext globalContext,
+        CallMeta      callMeta
+    ) {
+        logger.debug("RiverService.process");
+
+        Document result = XMLUtils.newDocument();
+
+        XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(
+            result,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        List<River> allRivers = RiverFactory.getRivers();
+
+        Element rivers = ec.create("rivers");
+
+        for (River river: allRivers) {
+            Element r = ec.create("river");
+            ec.addAttr(r, "name", river.getName(), true);
+
+            rivers.appendChild(r);
+        }
+
+        result.appendChild(rivers);
+
+        return result;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/SQKMChartService.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,316 @@
+package org.dive4elements.river.artifacts.services;
+
+import org.dive4elements.artifactdatabase.DefaultService;
+
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.artifacts.GlobalContext;
+import org.dive4elements.artifacts.Service;
+
+import org.dive4elements.river.artifacts.model.SQOverview;
+import org.dive4elements.river.artifacts.model.SQOverviewFactory;
+
+import org.dive4elements.river.artifacts.resources.Resources;
+
+import org.dive4elements.river.backend.SedDBSessionHolder;
+
+import org.dive4elements.river.utils.KMIndex;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Transparency;
+
+import java.awt.image.BufferedImage;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import java.util.Date;
+import java.util.List;
+
+import javax.imageio.ImageIO;
+
+import org.apache.log4j.Logger;
+
+import org.jfree.chart.ChartFactory;
+import org.jfree.chart.JFreeChart;
+
+import org.jfree.chart.axis.DateAxis;
+
+import org.jfree.chart.plot.PlotOrientation;
+import org.jfree.chart.plot.XYPlot;
+
+import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
+
+import org.jfree.data.xy.XYSeries;
+import org.jfree.data.xy.XYSeriesCollection;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+public class SQKMChartService
+extends DefaultService {
+
+    private static final Logger log =
+        Logger.getLogger(SQKMChartService.class);
+
+    public static final int DEFAULT_WIDTH  = 240;
+    public static final int DEFAULT_HEIGHT = 180;
+
+    public static final String I18N_CHART_LABEL =
+        "sq.km.chart.label";
+
+    public static final String DEFAULT_CHART_LABEL =
+        "Measuring Points";
+
+    public static final String I18N_CHART_TITLE =
+        "sq.km.chart.title";
+
+    public static final String DEFAULT_CHART_TITLE =
+        "Measuring points";
+
+    public static final String I18N_KM_AXIS =
+        "sq.km.chart.km.axis";
+
+    public static final String DEFAULT_KM_AXIS =
+        "km";
+
+    public static final String I18N_DATE_AXIS =
+        "sq.km.chart.date.axis";
+
+    public static final String DEFAULT_DATE_AXIS =
+        "Date";
+
+    public static final String DEFAULT_FORMAT = "png";
+
+    // TODO: Load fancy image from resources.
+    public static final byte [] EMPTY = {
+        (byte)0x89, (byte)0x50, (byte)0x4e, (byte)0x47,
+        (byte)0x0d, (byte)0x0a, (byte)0x1a, (byte)0x0a,
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x0d,
+        (byte)0x49, (byte)0x48, (byte)0x44, (byte)0x52,
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01,
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01,
+        (byte)0x08, (byte)0x00, (byte)0x00, (byte)0x00,
+        (byte)0x00, (byte)0x3a, (byte)0x7e, (byte)0x9b,
+        (byte)0x55, (byte)0x00, (byte)0x00, (byte)0x00,
+        (byte)0x01, (byte)0x73, (byte)0x52, (byte)0x47,
+        (byte)0x42, (byte)0x00, (byte)0xae, (byte)0xce,
+        (byte)0x1c, (byte)0xe9, (byte)0x00, (byte)0x00,
+        (byte)0x00, (byte)0x09, (byte)0x70, (byte)0x48,
+        (byte)0x59, (byte)0x73, (byte)0x00, (byte)0x00,
+        (byte)0x0b, (byte)0x13, (byte)0x00, (byte)0x00,
+        (byte)0x0b, (byte)0x13, (byte)0x01, (byte)0x00,
+        (byte)0x9a, (byte)0x9c, (byte)0x18, (byte)0x00,
+        (byte)0x00, (byte)0x00, (byte)0x07, (byte)0x74,
+        (byte)0x49, (byte)0x4d, (byte)0x45, (byte)0x07,
+        (byte)0xdc, (byte)0x04, (byte)0x04, (byte)0x10,
+        (byte)0x30, (byte)0x15, (byte)0x7d, (byte)0x77,
+        (byte)0x36, (byte)0x0b, (byte)0x00, (byte)0x00,
+        (byte)0x00, (byte)0x08, (byte)0x74, (byte)0x45,
+        (byte)0x58, (byte)0x74, (byte)0x43, (byte)0x6f,
+        (byte)0x6d, (byte)0x6d, (byte)0x65, (byte)0x6e,
+        (byte)0x74, (byte)0x00, (byte)0xf6, (byte)0xcc,
+        (byte)0x96, (byte)0xbf, (byte)0x00, (byte)0x00,
+        (byte)0x00, (byte)0x0a, (byte)0x49, (byte)0x44,
+        (byte)0x41, (byte)0x54, (byte)0x08, (byte)0xd7,
+        (byte)0x63, (byte)0xf8, (byte)0x0f, (byte)0x00,
+        (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x00,
+        (byte)0x1b, (byte)0xb6, (byte)0xee, (byte)0x56,
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+        (byte)0x49, (byte)0x45, (byte)0x4e, (byte)0x44,
+        (byte)0xae, (byte)0x42, (byte)0x60, (byte)0x82
+    };
+
+    private static final Output empty() {
+        return new Output(EMPTY, "image/png");
+    }
+
+    @Override
+    public Service.Output process(
+        Document      data,
+        GlobalContext globalContext,
+        CallMeta      callMeta
+    ) {
+        log.debug("SQKMChartService.process");
+
+        SedDBSessionHolder.acquire();
+        try {
+            return doProcess(data, globalContext, callMeta);
+        }
+        finally {
+            SedDBSessionHolder.HOLDER.get().close();
+            SedDBSessionHolder.release();
+        }
+    }
+
+    protected Service.Output doProcess(
+        Document      input,
+        GlobalContext globalContext,
+        CallMeta      callMeta
+    ) {
+        String    river  = getRiverName(input);
+        Dimension extent = getExtent(input);
+        String    format = getFormat(input);
+
+        if (river == null) {
+            log.warn("River invalid.");
+            return empty();
+        }
+
+        SQOverview overview = SQOverviewFactory.getOverview(river);
+
+        if (overview == null) {
+            log.warn("No overview found for river '" + river + "'");
+            return empty();
+        }
+
+        KMIndex<List<Date>> entries = overview.filter(SQOverview.ACCEPT);
+
+        JFreeChart chart = createChart(entries, river, callMeta);
+
+        return encode(chart, extent, format);
+    }
+
+    protected static Output encode(
+        JFreeChart chart,
+        Dimension  extent,
+        String     format
+    ) {
+        BufferedImage image = chart.createBufferedImage(
+            extent.width, extent.height,
+            Transparency.BITMASK,
+            null);
+
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+        try {
+            ImageIO.write(image, format, out);
+        }
+        catch (IOException ioe) {
+            log.warn("writing image failed", ioe);
+            return empty();
+        }
+
+        return new Output(out.toByteArray(), "image/" + format);
+    }
+
+    protected static JFreeChart createChart(
+        KMIndex<List<Date>> entries,
+        String      river,
+        CallMeta    callMeta
+    ) {
+
+        XYSeriesCollection dataset = new XYSeriesCollection();
+        String key = Resources.format(
+            callMeta, I18N_CHART_LABEL, DEFAULT_CHART_LABEL, river);
+
+        XYSeries series = new XYSeries(key);
+        for (KMIndex.Entry<List<Date>> e: entries) {
+            double km = e.getKm();
+            List<Date> ds = e.getValue();
+            for (Date d: ds) {
+                series.add(km, d.getTime());
+            }
+        }
+
+        dataset.addSeries(series);
+        String title = Resources.format(
+            callMeta, I18N_CHART_TITLE, DEFAULT_CHART_TITLE, river);
+
+        String kmAxis = Resources.getMsg(
+            callMeta, I18N_KM_AXIS, DEFAULT_KM_AXIS);
+
+        String dateAxis = Resources.getMsg(
+            callMeta, I18N_DATE_AXIS, DEFAULT_DATE_AXIS);
+
+        JFreeChart chart = ChartFactory.createXYLineChart(
+            title,
+            kmAxis,
+            dateAxis,
+            null,
+            PlotOrientation.VERTICAL,
+            true,
+            true,
+            false);
+
+        XYPlot plot = (XYPlot)chart.getPlot();
+
+        DateAxis dA = new DateAxis();
+        plot.setRangeAxis(dA);
+        plot.setDataset(0, dataset);
+
+        chart.setBackgroundPaint(Color.white);
+        plot.setBackgroundPaint(Color.white);
+        plot.setDomainGridlinePaint(Color.gray);
+        plot.setRangeGridlinePaint(Color.gray);
+        plot.setDomainGridlinesVisible(true);
+        plot.setRangeGridlinesVisible(true);
+        XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer) plot.getRenderer();
+
+        renderer.setSeriesPaint(0, Color.gray);
+        renderer.setSeriesLinesVisible(0, false);
+        renderer.setSeriesShapesVisible(0, true);
+        renderer.setDrawOutlines(true);
+        return chart;
+    }
+
+
+    protected static String getRiverName(Document input) {
+        NodeList rivers = input.getElementsByTagName("river");
+
+        if (rivers.getLength() == 0) {
+            return null;
+        }
+
+        String river = ((Element)rivers.item(0)).getAttribute("name");
+
+        return river.length() > 0 ? river : null;
+    }
+
+    protected static Dimension getExtent(Document input) {
+
+        int width  = DEFAULT_WIDTH;
+        int height = DEFAULT_HEIGHT;
+
+        NodeList extents = input.getElementsByTagName("extent");
+
+        if (extents.getLength() > 0) {
+            Element element = (Element)extents.item(0);
+            String w = element.getAttribute("width");
+            String h = element.getAttribute("height");
+
+            try {
+                width = Math.max(1, Integer.parseInt(w));
+            }
+            catch (NumberFormatException nfe) {
+                log.warn("width '" + w + "' is not a valid.");
+            }
+
+            try {
+                height = Math.max(1, Integer.parseInt(h));
+            }
+            catch (NumberFormatException nfe) {
+                log.warn("height '" + h + "' is not a valid");
+            }
+        }
+
+        return new Dimension(width, height);
+    }
+
+    protected static String getFormat(Document input) {
+        String format = DEFAULT_FORMAT;
+
+        NodeList formats = input.getElementsByTagName("format");
+
+        if (formats.getLength() > 0) {
+            String type = ((Element)formats.item(0)).getAttribute("type");
+            if (type.length() > 0) {
+                format = type;
+            }
+        }
+
+        return format;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/SedimentLoadInfoService.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,92 @@
+package org.dive4elements.river.artifacts.services;
+
+import java.util.Calendar;
+
+import org.apache.log4j.Logger;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import org.dive4elements.artifacts.ArtifactNamespaceContext;
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.artifacts.GlobalContext;
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+import org.dive4elements.river.artifacts.model.minfo.SedimentLoad;
+import org.dive4elements.river.artifacts.model.minfo.SedimentLoadFactory;
+
+
+public class SedimentLoadInfoService
+extends FLYSService
+{
+    /** The logger used in this service. */
+    private static Logger logger = Logger.getLogger(SedimentLoadInfoService.class);
+
+    public static final String RIVER_XPATH = "/art:river/text()";
+    public static final String TYPE_XPATH = "/art:river/art:type/text()";
+    public static final String FROM_XPATH = "/art:river/art:location/art:from/text()";
+    public static final String TO_XPATH = "/art:river/art:location/art:to/text()";
+
+    @Override
+    protected Document doProcess(
+        Document data,
+        GlobalContext globalContext,
+        CallMeta callMeta) {
+        String river = XMLUtils.xpathString(
+            data,
+            RIVER_XPATH,
+            ArtifactNamespaceContext.INSTANCE);
+        String type = XMLUtils.xpathString(
+            data,
+            TYPE_XPATH,
+            ArtifactNamespaceContext.INSTANCE);
+        String from = XMLUtils.xpathString(
+            data,
+            FROM_XPATH,
+            ArtifactNamespaceContext.INSTANCE);
+        String to = XMLUtils.xpathString(
+            data,
+            TO_XPATH,
+            ArtifactNamespaceContext.INSTANCE);
+        double f, t;
+        try {
+            f = Double.parseDouble(from);
+            t = Double.parseDouble(to);
+        }
+        catch (NumberFormatException nfe) {
+            logger.warn("Invalid locations. Cannot return sediment loads.");
+            return XMLUtils.newDocument();
+        }
+
+        SedimentLoad[] loads = SedimentLoadFactory.getLoads(river, type, f, t);
+        return buildDocument(loads);
+    }
+
+    protected Document buildDocument(SedimentLoad[] loads) {
+        Document result = XMLUtils.newDocument();
+        Element all = result.createElement("sedimentloads");
+        for (SedimentLoad sl : loads) {
+            Element load = result.createElement("sedimentload");
+            load.setAttribute("description", sl.getDescription());
+            if (sl.isEpoch()) {
+                Calendar calendarS = Calendar.getInstance();
+                calendarS.setTime(sl.getStart());
+                Calendar calendarE = Calendar.getInstance();
+                calendarE.setTime(sl.getEnd());
+                load.setAttribute(
+                    "date",
+                    calendarS.get(Calendar.YEAR) +
+                        " - " +
+                        calendarE.get(Calendar.YEAR));
+            }
+            else {
+                Calendar calendar = Calendar.getInstance();
+                calendar.setTime(sl.getStart());
+                load.setAttribute(
+                    "date",
+                    String.valueOf(calendar.get(Calendar.YEAR)));
+            }
+            all.appendChild(load);
+        }
+        result.appendChild(all);
+        return result;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/services/ThemeListingService.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,74 @@
+package org.dive4elements.river.artifacts.services;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.artifacts.GlobalContext;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+
+import org.dive4elements.river.themes.Theme;
+import org.dive4elements.river.themes.ThemeGroup;
+
+import org.dive4elements.river.themes.ThemeFactory;
+import org.dive4elements.river.artifacts.context.FLYSContext;
+
+/**
+ * This service provides a list of themes filtered by the theme name.
+ *
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class ThemeListingService extends FLYSService {
+
+    /** The logger used in this service.*/
+    private static Logger logger = Logger.getLogger(ThemeListingService.class);
+
+    private static final String XPATH_THEME_NAME = "/theme/@name";
+
+    protected Document doProcess(
+        Document      data,
+        GlobalContext context,
+        CallMeta      callMeta
+    ) {
+        logger.debug("ThemeListingService.process");
+        String name = XMLUtils.xpathString(data, XPATH_THEME_NAME, null);
+
+        if (name == null) {
+            logger.warn("No theme name provided.");
+        }
+        Document result = XMLUtils.newDocument();
+
+        XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(
+            result,
+            null,
+            null);
+
+        List<ThemeGroup> tgs =
+            ThemeFactory.getThemeGroups((FLYSContext) context);
+
+        Element te = ec.create("themes");
+
+        for (ThemeGroup tg: tgs) {
+            Element elem = ec.create("themegroup");
+            if (tg.getName().equals("virtual")) {
+                continue;
+            }
+            ec.addAttr(elem, "name", tg.getName());
+            Theme theme = tg.getThemeByName(name);
+            Document d = theme.toXML();
+            Node imported = result.importNode(d.getDocumentElement(), true);
+            elem.appendChild(imported);
+            te.appendChild(elem);
+        }
+
+        result.appendChild(te);
+        return result;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/AnnotationRiverState.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,71 @@
+package org.dive4elements.river.artifacts.states;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+
+import org.dive4elements.river.artifacts.AnnotationArtifact;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+import org.dive4elements.river.artifacts.model.AnnotationFacet;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+
+import org.dive4elements.river.artifacts.resources.Resources;
+
+
+/**
+ * The only state for an AnnotationArtifact (River is known).
+ */
+public class AnnotationRiverState
+extends      DefaultState
+implements   FacetTypes
+{
+    /** Developer-centric description of facet. */
+    public static final String I18N_DESCRIPTION = "facet.longitudinal_section.annotations";
+
+    /** The logger that is used in this state. */
+    private static final Logger logger = Logger.getLogger(AnnotationRiverState.class);
+
+
+    /**
+     * Add an AnnotationFacet to list of Facets.
+     *
+     * @param artifact Ignored.
+     * @param hash Ignored.
+     * @param context Ignored.
+     * @param meta CallMeta to be used for internationalization.
+     * @param facets List to add AnnotationFacet to.
+     *
+     * @return null.
+     */
+    @Override
+    public Object computeInit(
+        FLYSArtifact artifact,
+        String       hash,
+        Object       context,
+        CallMeta     meta,
+        List<Facet>  facets
+    ) {
+        logger.debug("AnnotationRiverState.computeInit()");
+
+        AnnotationArtifact annotationArtifact = (AnnotationArtifact) artifact;
+
+        String facetName = annotationArtifact.getFilterName();
+        if (facetName == null) {
+            facetName = Resources.getMsg(meta, I18N_DESCRIPTION, I18N_DESCRIPTION);
+        }
+
+        AnnotationFacet facet = new AnnotationFacet(
+            0,
+            LONGITUDINAL_ANNOTATION,
+            facetName);
+        facets.add(facet);
+
+        return null;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/AreaCreationState.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,86 @@
+package org.dive4elements.river.artifacts.states;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.model.AreaFacet;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.AreaArtifact;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+
+
+/** Trivial state to create areafacets, no caching. */
+public class AreaCreationState
+extends      DefaultState
+implements   FacetTypes
+{
+    /** The logger that is used in this state. */
+    private static Logger logger = Logger.getLogger(AreaCreationState.class);
+
+
+    /**
+     * From this state can only be continued trivially.
+     */
+    @Override
+    protected String getUIProvider() {
+        return "continue";
+    }
+
+
+    /** Just reproduce the Facet. */
+    protected Object compute(
+        FLYSArtifact  areaArtifact,
+        CallContext   cc,
+        String        hash,
+        List<Facet>   facets,
+        Object        old
+    ) {
+        logger.debug("AreaCreationState.compute");
+
+        if (facets != null) {
+            AreaArtifact aArt = (AreaArtifact) areaArtifact;
+
+            facets.add(new AreaFacet(0, aArt.getFacetName(), aArt.getAreaName()));
+        }
+
+        // TODO use compute to exploit caching strategies.
+
+        return null;
+    }
+
+
+    /**
+     */
+    @Override
+    public Object computeFeed(
+        FLYSArtifact artifact,
+        String       hash,
+        CallContext  context,
+        List<Facet>  facets,
+        Object       old
+    ) {
+        return compute((FLYSArtifact) artifact, context, hash, facets, old);
+    }
+
+
+    /**
+     *
+     */
+    @Override
+    public Object computeAdvance(
+        FLYSArtifact artifact,
+        String       hash,
+        CallContext  context,
+        List<Facet>  facets,
+        Object       old
+    ) {
+        return compute((FLYSArtifact) artifact, context, hash, facets, old);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/CalculationSelect.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,142 @@
+package org.dive4elements.river.artifacts.states;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Element;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+
+import org.dive4elements.artifactdatabase.data.StateData;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.resources.Resources;
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class CalculationSelect extends DefaultState {
+
+    /** The logger that is used in this class. */
+    private static Logger logger = Logger.getLogger(CalculationSelect.class);
+
+    /** Name of data item. */
+    public static final String FIELD_MODE = "calculation_mode";
+
+    /** Constant value for the reference line calculation. */
+    public static final String CALCULATION_SURFACE_CURVE =
+        "calc.surface.curve";
+
+    /** Constant value for the differences calculation. */
+    public static final String CALCULATION_DURATION_CURVE =
+        "calc.duration.curve";
+
+    /** Constant value for the flood map calculation. */
+    public static final String CALCULATION_FLOOD_MAP =
+        "calc.flood.map";
+
+    /** Constant value for the profile calculation. */
+    public static final String CALCULATION_DISCHARGE_LONGITUDINAL_CURVE =
+        "calc.discharge.longitudinal.section";
+
+    /** Constant value for the state discharge curve calculation. */
+    public static final String CALCULATION_DISCHARGE_CURVE =
+        "calc.discharge.curve";
+
+    /** Constant value for the state w differences calculation. */
+    public static final String CALCULATION_W_DIFFERENCES =
+        "calc.w.differences";
+
+    /** Constant value for the state reference curve calculation. */
+    public static final String CALCULATION_REFERENCE_CURVE =
+        "calc.reference.curve";
+
+    /** Constant value for the historical discharge curve calculation. */
+    public static final String CALCULATION_HISTORICAL_DISCHARGE_CURVE =
+        "calc.historical.discharge.curve";
+
+    /** Constant value for the extreme W curve calculation. */
+    public static final String CALCULATION_EXTREME =
+        "calc.extreme.curve";
+
+    /** An array that holds all available calculation modes. */
+    public static final String[] CALCULATIONS = {
+        CALCULATION_SURFACE_CURVE,
+        CALCULATION_FLOOD_MAP,
+        CALCULATION_DISCHARGE_CURVE,
+        CALCULATION_HISTORICAL_DISCHARGE_CURVE,
+        CALCULATION_DURATION_CURVE,
+        CALCULATION_DISCHARGE_LONGITUDINAL_CURVE,
+        CALCULATION_W_DIFFERENCES,
+        CALCULATION_REFERENCE_CURVE,
+        CALCULATION_EXTREME};
+
+
+    /** Error message that is thrown if no mode has been chosen. */
+    public static final String ERROR_NO_CALCULATION_MODE =
+        "error_feed_no_calculation_mode";
+
+    /** Error message that is thrown if an invalid calculation mode has been
+     * chosen. */
+    public static  final String ERROR_INVALID_CALCULATION_MODE =
+        "error_feed_invalid_calculation_mode";
+
+
+    public CalculationSelect() {
+    }
+
+
+    /** Create choices (i18ned). */
+    @Override
+    protected Element[] createItems(
+        XMLUtils.ElementCreator cr,
+        Artifact    artifact,
+        String      name,
+        CallContext context)
+    {
+        CallMeta meta   = context.getMeta();
+        Element[] calcs = new Element[CALCULATIONS.length];
+
+        for (int i = 0; i < CALCULATIONS.length; ++i) {
+            String calc = CALCULATIONS[i];
+            calcs[i] = createItem(
+                cr, new String[] {
+                    Resources.getMsg(meta, calc, calc),
+                    calc
+                });
+        }
+
+        return calcs;
+    }
+
+
+    /** Validate the chosen calculation. */
+    @Override
+    public boolean validate(Artifact artifact)
+    throws IllegalArgumentException
+    {
+        logger.debug("CalculationSelect.validate");
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        StateData data = getData(flys, FIELD_MODE);
+        String    calc = (data != null) ? (String) data.getValue() : null;
+
+        if (calc == null) {
+            throw new IllegalArgumentException(ERROR_NO_CALCULATION_MODE);
+        }
+
+        calc = calc.trim().toLowerCase();
+
+        for (String mode: CALCULATIONS) {
+            if (mode.equals(calc)) {
+                return true;
+            }
+        }
+
+        throw new IllegalArgumentException(ERROR_INVALID_CALCULATION_MODE);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/CalculationSelectMinfo.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,109 @@
+package org.dive4elements.river.artifacts.states;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Element;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+
+import org.dive4elements.artifactdatabase.data.StateData;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.resources.Resources;
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class CalculationSelectMinfo extends DefaultState {
+
+    /** The logger that is used in this class. */
+    private static Logger logger = Logger.getLogger(CalculationSelectMinfo.class);
+
+
+    public static final String FIELD_MODE = "calculation_mode";
+
+    public static final String CALC_BED_MIDDLE    = "calc.bed.middle";
+    public static final String CALC_BED_DIFF      = "calc.bed.diff";
+    public static final String CALC_BED_QUALITY   = "calc.bed.quality";
+    public static final String CALC_SEDIMENT_LOAD = "calc.sediment.load";
+    public static final String CALC_FLOW_VELOCITY = "calc.flow.velocity";
+    public static final String CALC_SQ_RELATION   = "calc.sq.relation";
+
+    /** An array that holds all available calculation modes. */
+    public static final String[] CALCULATIONS = {
+        CALC_BED_MIDDLE,
+        CALC_BED_DIFF,
+        CALC_BED_QUALITY,
+        CALC_SEDIMENT_LOAD,
+        CALC_FLOW_VELOCITY,
+        CALC_SQ_RELATION
+    };
+
+
+    /** Error message that is thrown if no mode has been chosen. */
+    public static final String ERROR_NO_CALCULATION_MODE =
+        "error_feed_no_calculation_mode";
+
+    /** Error message that is thrown if an invalid calculation mode has been
+     * chosen. */
+    public static  final String ERROR_INVALID_CALCULATION_MODE =
+        "error_feed_invalid_calculation_mode";
+
+
+    public CalculationSelectMinfo() {
+    }
+
+
+    @Override
+    protected Element[] createItems(
+        XMLUtils.ElementCreator cr,
+        Artifact    artifact,
+        String      name,
+        CallContext context)
+    {
+        CallMeta meta   = context.getMeta();
+        Element[] calcs = new Element[CALCULATIONS.length];
+
+        for (int i = 0; i < CALCULATIONS.length; ++i) {
+            String calc = CALCULATIONS[i];
+            calcs[i] = createItem(
+                cr, new String[] {
+                    Resources.getMsg(meta, calc, calc),
+                    calc
+                });
+        }
+
+        return calcs;
+    }
+
+
+    @Override
+    public boolean validate(Artifact artifact)
+    throws IllegalArgumentException
+    {
+        logger.debug("CalculationSelect.validate");
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        StateData data = getData(flys, FIELD_MODE);
+        String    calc = (data != null) ? (String) data.getValue() : null;
+
+        if (calc == null) {
+            throw new IllegalArgumentException(ERROR_NO_CALCULATION_MODE);
+        }
+
+        calc = calc.trim().toLowerCase();
+
+        for (String mode: CALCULATIONS) {
+            if (mode.equals(calc)) {
+                return true;
+            }
+        }
+
+        throw new IllegalArgumentException(ERROR_INVALID_CALCULATION_MODE);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/ComputationRangeState.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,145 @@
+package org.dive4elements.river.artifacts.states;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Element;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+
+import org.dive4elements.artifactdatabase.ProtocolUtils;
+import org.dive4elements.artifactdatabase.data.StateData;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.resources.Resources;
+
+import org.dive4elements.river.utils.FLYSUtils;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ComputationRangeState
+extends      RangeState
+implements   FacetTypes
+{
+    private static Logger logger =
+        Logger.getLogger(ComputationRangeState.class);
+
+    /** The default step width. */
+    public static final int DEFAULT_STEP = 100;
+
+
+    public ComputationRangeState() {
+    }
+
+
+    @Override
+    protected Element createData(
+        XMLUtils.ElementCreator cr,
+        Artifact    artifact,
+        StateData   data,
+        CallContext context)
+    {
+        Element select = ProtocolUtils.createArtNode(
+            cr, "select", null, null);
+
+        cr.addAttr(select, "name", data.getName(), true);
+
+        Element label = ProtocolUtils.createArtNode(
+            cr, "label", null, null);
+
+        // XXX: DEAD CODE
+        /*
+        Element choices = ProtocolUtils.createArtNode(
+            cr, "choices", null, null);
+        */
+
+        label.setTextContent(Resources.getMsg(
+            context.getMeta(),
+            data.getName(),
+            data.getName()));
+
+        select.appendChild(label);
+
+        return select;
+    }
+
+
+    @Override
+    protected Element[] createItems(
+        XMLUtils.ElementCreator cr,
+        Artifact    artifact,
+        String      name,
+        CallContext context)
+    {
+        double[] minmax = getMinMax(artifact);
+
+        double minVal = Double.MIN_VALUE;
+        double maxVal = Double.MAX_VALUE;
+
+        if (minmax != null) {
+            minVal = minmax[0];
+            maxVal = minmax[1];
+        }
+        else {
+            logger.warn("Could not read min/max distance values!");
+        }
+
+        if (name.equals("ld_from")) {
+            Element min = createItem(
+                cr,
+                new String[] {"min", new Double(minVal).toString()});
+
+            return new Element[] { min };
+        }
+        else if (name.equals("ld_to")) {
+            Element max = createItem(
+                cr,
+                new String[] {"max", new Double(maxVal).toString()});
+
+            return new Element[] { max };
+        }
+        else {
+            Element step = createItem(
+                cr,
+                new String[] {"step", String.valueOf(getDefaultStep())});
+            return new Element[] { step };
+        }
+
+    }
+
+
+    protected Element createItem(XMLUtils.ElementCreator cr, Object obj) {
+        Element item  = ProtocolUtils.createArtNode(cr, "item", null, null);
+        Element label = ProtocolUtils.createArtNode(cr, "label", null, null);
+        Element value = ProtocolUtils.createArtNode(cr, "value", null, null);
+
+        String[] arr = (String[]) obj;
+
+        label.setTextContent(arr[0]);
+        value.setTextContent(arr[1]);
+
+        item.appendChild(label);
+        item.appendChild(value);
+
+        return item;
+    }
+
+
+    @Override
+    protected double[] getMinMax(Artifact artifact) {
+        FLYSArtifact flysArtifact = (FLYSArtifact) artifact;
+        return FLYSUtils.getRiverMinMax(flysArtifact);
+    }
+
+
+    protected double getDefaultStep() {
+        return DEFAULT_STEP;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/ComputedDischargeCurveState.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,105 @@
+package org.dive4elements.river.artifacts.states;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.WINFOArtifact;
+import org.dive4elements.river.artifacts.ChartArtifact;
+
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.WaterlevelFacet;
+import org.dive4elements.river.artifacts.model.DataFacet;
+import org.dive4elements.river.artifacts.model.ReportFacet;
+import org.dive4elements.river.artifacts.model.EmptyFacet;
+import org.dive4elements.river.artifacts.model.WQKms;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+
+import org.dive4elements.river.artifacts.resources.Resources;
+
+import org.dive4elements.river.utils.FLYSUtils;
+
+/**
+ * The final state that will be reached after the discharge curve calculation
+ * mode has been chosen.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ComputedDischargeCurveState
+extends      DefaultState
+implements   FacetTypes
+{
+    /** The logger that is used in this state. */
+    private static Logger logger =
+        Logger.getLogger(ComputedDischargeCurveState.class);
+
+
+    public ComputedDischargeCurveState() {
+    }
+
+
+    /**
+     * Get computed discharge curve data from cache (if available) or
+     * compute anew. Create Waterlevel and DataFacets.
+     */
+    @Override
+    public Object computeAdvance(
+        FLYSArtifact artifact,
+        String       hash,
+        CallContext  context,
+        List<Facet>  facets,
+        Object       old
+    ) {
+        logger.debug("ComputedDischargeCurveState.computeAdvance");
+        if(artifact instanceof WINFOArtifact) {
+            WINFOArtifact winfo = (WINFOArtifact)artifact;
+
+            CalculationResult res = old instanceof CalculationResult
+                ? (CalculationResult)old
+                : winfo.getComputedDischargeCurveData();
+
+            WQKms [] wqkms = (WQKms [])res.getData();
+
+            if (facets != null && wqkms.length > 0) {
+                for (int i = 0; i < wqkms.length; ++i) {
+
+                    Object[] args = new Object[] {
+                        FLYSUtils.getRiver(winfo).getName(),
+                        // Parse Double to allow i18n.
+                        Double.parseDouble(wqkms[i].getName())
+                    };
+
+                    String name = Resources.getMsg(
+                        context.getMeta(),
+                        "chart.computed.discharge.curve.curve.label",
+                        "",
+                        args);
+
+                    facets.add(new WaterlevelFacet(i, COMPUTED_DISCHARGE_Q, name));
+                    facets.add(new WaterlevelFacet(i, AT, "AT data"));
+                }
+
+                facets.add(new DataFacet(CSV, "CSV data"));
+                facets.add(new DataFacet(PDF, "PDF data"));
+
+                if (res.getReport().hasProblems()) {
+                    facets.add(new ReportFacet());
+                }
+            }
+
+            return res;
+        }
+        else if(artifact instanceof ChartArtifact) {
+            ChartArtifact chart = (ChartArtifact)artifact;
+            facets.add(new EmptyFacet());
+            return null;
+        }
+        return null;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/DGMSelect.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,140 @@
+package org.dive4elements.river.artifacts.states;
+
+import java.io.File;
+
+import org.w3c.dom.Element;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator;
+
+import org.dive4elements.river.model.DGM;
+import org.dive4elements.river.model.River;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.utils.FLYSUtils;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DGMSelect extends DefaultState {
+
+    private static final Logger logger = Logger.getLogger(DGMSelect.class);
+
+    public static final String ERR_EMPTY         = "error_no_dgm_selected";
+    public static final String ERR_INVALID_DGM   = "error_invalid_dgm_selected";
+    public static final String ERR_BAD_DGM_RANGE = "error_bad_dgm_range";
+    public static final String ERR_BAD_DGM_RIVER = "error_bad_dgm_river";
+
+
+    @Override
+    protected String getUIProvider() {
+        return "dgm_datacage_panel";
+    }
+
+
+    @Override
+    protected Element createStaticData(
+        FLYSArtifact   flys,
+        ElementCreator creator,
+        CallContext    cc,
+        String         name,
+        String         value,
+        String         type
+    ) {
+        Element dataElement = creator.create("data");
+        creator.addAttr(dataElement, "name", name, true);
+        creator.addAttr(dataElement, "type", type, true);
+
+        Element itemElement = creator.create("item");
+        creator.addAttr(itemElement, "value", value, true);
+
+        creator.addAttr(itemElement, "label", getLabel(cc, value), true);
+        dataElement.appendChild(itemElement);
+
+        return dataElement;
+    }
+
+
+    public static String getLabel(CallContext cc, String value) {
+        logger.debug("Create label for value: " + value);
+
+        try {
+            DGM dgm = DGM.getDGM(Integer.parseInt(value));
+
+            File file = new File(dgm.getPath());
+            return file.getName();
+        }
+        catch (NumberFormatException nfe) {
+            logger.warn("Cannot parse int value: '" + value + "'");
+        }
+
+        return "";
+    }
+
+
+    @Override
+    public boolean validate(Artifact artifact)
+    throws IllegalArgumentException
+    {
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        DGM dgm = getDGM(flys);
+
+        if (dgm == null) {
+            throw new IllegalArgumentException(ERR_INVALID_DGM);
+        }
+
+        double l = dgm.getRange().getA().doubleValue();
+        double u = dgm.getRange().getB().doubleValue();
+
+        double[] range = FLYSUtils.getKmFromTo(flys);
+
+        if (range[0] < l || range[0] > u || range[1] < l || range[1] > u) {
+            throw new IllegalArgumentException(ERR_BAD_DGM_RANGE);
+        }
+
+        River selectedRiver = FLYSUtils.getRiver(flys);
+        River dgmRiver      = dgm.getRiver();
+
+        if (selectedRiver != dgmRiver) {
+            throw new IllegalArgumentException(ERR_BAD_DGM_RIVER);
+        }
+
+        return true;
+    }
+
+
+    /**
+     * Returns the DGM specified in the parameters of <i>flys</i>.
+     *
+     * @param flys The FLYSArtifact that knows the ID of a DGM.
+     *
+     * @throws IllegalArgumentException If the FLYSArtifact doesn't know the ID
+     * of a DGM.
+     *
+     * @return the DGM specified by FLYSArtifact's parameters.
+     */
+    public static DGM getDGM(FLYSArtifact flys)
+    throws IllegalArgumentException
+    {
+        try {
+            Integer dgmId = flys.getDataAsInteger("dgm");
+            if (dgmId == null) {
+                throw new IllegalArgumentException(ERR_EMPTY);
+            }
+
+            logger.debug("Found selected dgm: '" + dgmId + "'");
+
+            return DGM.getDGM(dgmId);
+        }
+        catch (NumberFormatException nfe) {
+            throw new IllegalArgumentException(ERR_INVALID_DGM);
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/DefaultState.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,476 @@
+package org.dive4elements.river.artifacts.states;
+
+import java.text.NumberFormat;
+import java.util.Locale;
+import java.util.Map;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.ArtifactNamespaceContext;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator;
+
+import org.dive4elements.artifactdatabase.ProtocolUtils;
+
+import org.dive4elements.artifactdatabase.data.StateData;
+
+import org.dive4elements.artifactdatabase.state.AbstractState;
+import org.dive4elements.artifactdatabase.state.Facet;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+import org.dive4elements.river.artifacts.resources.Resources;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public abstract class DefaultState extends AbstractState {
+
+    /** The logger that is used in this class. */
+    private static Logger logger = Logger.getLogger(DefaultState.class);
+
+
+    /** Determines, if the DESCRIBE document should contain default values or
+     * not. */
+    public static final boolean USE_DEFAULTS =
+        Boolean.getBoolean("flys.use.default.values");
+
+    /** The three possible compute types. */
+    public static enum ComputeType {
+        FEED, ADVANCE, INIT
+    }
+
+
+    protected StateData getData(FLYSArtifact artifact,  String name) {
+        return artifact.getData(name);
+    }
+
+
+    /**
+     * Append to a node and return xml description relevant for gui.
+     */
+    public Element describeStatic(
+        Artifact    artifact,
+        Document    document,
+        Node        root,
+        CallContext context,
+        String      uuid)
+    {
+        ElementCreator creator = new ElementCreator(
+            document,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        CallMeta meta = context.getMeta();
+
+        String helpText = getHelpText() != null ?
+            Resources.getMsg(meta, getHelpText(), getHelpText())
+            : null;
+
+        String label = Resources.getMsg(meta, getID(), getID());
+        Element ui   = ProtocolUtils.createArtNode(
+            creator, "state",
+            new String[] { "name", "uiprovider", "label", "helpText"},
+            new String[] { getID(), getUIProvider(), label, helpText });
+
+        Map<String, StateData> theData = getData();
+        if (theData == null) {
+            return ui;
+        }
+
+        FLYSArtifact flys = (FLYSArtifact)artifact;
+
+        for (String name: theData.keySet()) {
+            appendStaticData(flys, context, creator, ui, name);
+        }
+
+        return ui;
+    }
+
+
+    protected void appendStaticData(
+        FLYSArtifact   flys,
+        CallContext    context,
+        ElementCreator cr,
+        Element        ui,
+        String         name
+    ) {
+        StateData data  = getData(flys, name);
+        String    value = (data != null) ? (String) data.getValue() : null;
+
+        if (value == null) {
+            return;
+        }
+
+        String type = data.getType();
+
+        if (logger.isDebugEnabled()) {
+            logger.debug(
+                "Append element " + type + "'" +
+                name + "' (" + value + ")");
+        }
+
+        Element e = createStaticData(flys, cr, context, name, value, type);
+
+        ui.appendChild(e);
+
+    }
+
+
+    /**
+     * Creates a <i>data</i> element used in the static part of the DESCRIBE
+     * document.
+     *
+     * @param creator The ElementCreator that is used to build new Elements.
+     * @param cc The CallContext object used for nested i18n retrieval.
+     * @param name The name of the data item.
+     * @param value The value as string.
+     *
+     * @return an Element.
+     */
+    protected Element createStaticData(
+        FLYSArtifact   flys,
+        ElementCreator creator,
+        CallContext    cc,
+        String         name,
+        String         value,
+        String         type
+    ) {
+        Element dataElement = creator.create("data");
+        creator.addAttr(dataElement, "name", name, true);
+        creator.addAttr(dataElement, "type", type, true);
+
+        Element itemElement = creator.create("item");
+        creator.addAttr(itemElement, "value", value, true);
+
+        creator.addAttr(
+            itemElement,
+            "label",
+            getLabelFor(cc, name, value, type),
+            true);
+
+        dataElement.appendChild(itemElement);
+
+        return dataElement;
+    }
+
+
+    /**
+     * @param cc
+     * @param name
+     * @param value
+     * @param type
+     *
+     * @return
+     */
+    protected String getLabelFor(
+        CallContext cc,
+        String      name,
+        String      value,
+        String      type
+    ) {
+        CallMeta meta = cc.getMeta();
+
+        try {
+            // XXX A better way to format the output would be to use the
+            // 'type' value of the data objects.
+            double doubleVal = Double.parseDouble(value);
+            Locale         l = Resources.getLocale(meta);
+            NumberFormat  nf = NumberFormat.getInstance(l);
+
+            return nf.format(doubleVal);
+        }
+        catch (NumberFormatException nfe) {
+            return Resources.getMsg(meta, value, value);
+        }
+    }
+
+
+    /**
+     * This method returns the default value and description for <i>data</i>.
+     * Note, that this method returns the defaults only if <i>USE_DEFAULTS</i>
+     * is set; otherwise, null is returned.
+     * @param context The CallContext used for i18n.
+     * @param data The data objects that the defaults are for.
+     * @return a String[] with [default value, default label].
+     */
+    protected String[] getDefaultsFor(CallContext context, StateData data) {
+        if (USE_DEFAULTS) {
+            String defValue = (String) data.getValue();
+            String defDesc  = null;
+
+            if (defValue != null && defValue.length() > 0) {
+                defDesc = Resources.getMsg(
+                    context.getMeta(),
+                    defValue,
+                    defValue);
+            }
+
+            if (defValue != null && defDesc != null) {
+                return new String[] { defValue, defDesc };
+            }
+        }
+
+        return null;
+    }
+
+
+    public Element describe(
+        Artifact    artifact,
+        Document    document,
+        Node        root,
+        CallContext context,
+        String      uuid)
+    {
+        ElementCreator creator = new ElementCreator(
+            document,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        String helpText = Resources.getMsg(
+            context.getMeta(), getHelpText(), getHelpText());
+
+        Element ui        = null;
+        String uiprovider = getUIProvider();
+        if (uiprovider != null) {
+            ui = ProtocolUtils.createArtNode(
+                creator, "dynamic",
+                new String[] { "uiprovider", "helpText" },
+                new String[] { uiprovider, helpText });
+        }
+        else {
+            ui = ProtocolUtils.createArtNode(
+                creator, "dynamic",
+                new String[] { "helpText" },
+                new String[] { helpText });
+        }
+
+        Map<String, StateData> theData = getData();
+        if (theData == null) {
+            return ui;
+        }
+
+        FLYSArtifact flys = (FLYSArtifact)artifact;
+
+        for (String name: theData.keySet()) {
+            StateData data = getData(flys, name);
+
+            if (data == null) {
+                data = getData(name);
+            }
+
+            Element select = createData(creator, artifact, data, context);
+
+            String[] defaults = getDefaultsFor(context, data);
+            if (defaults != null && defaults.length > 1) {
+                creator.addAttr(select, "defaultValue", defaults[0], true);
+                creator.addAttr(select, "defaultLabel", defaults[1], true);
+            }
+
+            appendItems(artifact, creator, name, context, select);
+            ui.appendChild(select);
+        }
+
+        return ui;
+    }
+
+
+    /**
+     * @param artifact
+     * @param creator
+     * @param name
+     * @param context
+     * @param select
+     */
+    protected void appendItems(
+        Artifact       artifact,
+        ElementCreator creator,
+        String         name,
+        CallContext    context,
+        Element        select
+    ) {
+        Element choices = ProtocolUtils.createArtNode(
+            creator, "choices", null, null);
+
+        select.appendChild(choices);
+
+        Element[] items = createItems(creator, artifact, name, context);
+        if (items != null) {
+            for (Element item: items) {
+                choices.appendChild(item);
+            }
+        }
+    }
+
+
+    /**
+     * This method creates the root node that contains the list of selectable
+     * items.
+     *
+     * @param cr The ElementCreator.
+     *
+     * @return the root node of the item list.
+     */
+    protected Element createData(
+        ElementCreator cr,
+        Artifact    artifact,
+        StateData   data,
+        CallContext context)
+    {
+        Element select = ProtocolUtils.createArtNode(
+            cr, "select", null, null);
+        cr.addAttr(select, "name", data.getName(), true);
+
+        Element label = ProtocolUtils.createArtNode(
+            cr, "label", null, null);
+
+        select.appendChild(label);
+
+        label.setTextContent(Resources.getMsg(
+            context.getMeta(),
+            getID(),
+            getID()));
+
+        return select;
+    }
+
+
+    /**
+     * This method creates a list of items. These items represent the amount of
+     * input data that is possible for this state.
+     *
+     * @param cr The ElementCreator.
+     * @param name The name of the amount of data.
+     *
+     * @return a list of items.
+     */
+    protected Element[] createItems(
+        ElementCreator cr,
+        Artifact    artifact,
+        String      name,
+        CallContext context
+    ) {
+        return null;
+    }
+
+
+    /**
+     * This method is used to create an <i>item</i> Element that contains two
+     * further elements <i>label</i> and <i>value</i>. The label and value
+     * elements both have text nodes.
+     *
+     * @param cr The ElementCreator used to build new Elements.
+     * @param obj This implementation awaits a String array with [0] = label and
+     * [1] = value.
+     *
+     * @return an Element.
+     */
+    protected Element createItem(XMLUtils.ElementCreator cr, Object obj) {
+        Element item  = ProtocolUtils.createArtNode(cr, "item", null, null);
+        Element label = ProtocolUtils.createArtNode(cr, "label", null, null);
+        Element value = ProtocolUtils.createArtNode(cr, "value", null, null);
+
+        String[] arr = (String[]) obj;
+
+        label.setTextContent(arr[0]);
+        value.setTextContent(arr[1]);
+
+        item.appendChild(label);
+        item.appendChild(value);
+
+        return item;
+    }
+
+
+    /**
+     * This method transform a given value into a StateData object.
+     *
+     * @param flys The FLYSArtifact.
+     * @param name The name of the data object.
+     * @param val The value of the data object.
+     *
+     * @return a StateData object with <i>name</i> and <i>val</i>ue.
+     */
+    public StateData transform(
+        FLYSArtifact flys,
+        CallContext  cc,
+        StateData    stateData,
+        String       name,
+        String       val
+    ) {
+        if (logger.isDebugEnabled()) {
+            logger.debug("Transform data ('" + name + "','" + val + "')");
+        }
+
+        stateData.setValue(val);
+
+        return stateData;
+    }
+
+
+    /**
+     * This method validates the inserted data and returns true, if everything
+     * was correct, otherwise an exception is thrown.
+     *
+     * @param artifact A reference to the owner artifact.
+     *
+     * @return true, if everything was fine.
+     */
+    public boolean validate(Artifact artifact)
+    throws IllegalArgumentException
+    {
+        return true;
+    }
+
+
+    /**
+     * Returns which UIProvider shall be used to aid user input.
+     */
+    protected String getUIProvider() {
+        return null;
+    }
+
+
+    public Object computeAdvance(
+        FLYSArtifact artifact,
+        String       hash,
+        CallContext  context,
+        List<Facet>  facets,
+        Object       old
+    ) {
+        return null;
+    }
+
+
+    public Object computeFeed(
+        FLYSArtifact artifact,
+        String       hash,
+        CallContext  context,
+        List<Facet>  facets,
+        Object       old
+    ) {
+        return null;
+    }
+
+
+    public Object computeInit(
+        FLYSArtifact artifact,
+        String       hash,
+        Object       context,
+        CallMeta     meta,
+        List<Facet>  facets)
+    {
+        return null;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/DischargeLongitudinalSection.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,155 @@
+package org.dive4elements.river.artifacts.states;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifactdatabase.state.FacetActivity;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.ChartArtifact;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+import org.dive4elements.river.artifacts.access.Calculation4Access;
+
+import org.dive4elements.river.artifacts.model.Calculation4;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.ConstantWQKms;
+import org.dive4elements.river.artifacts.model.CrossSectionWaterLineFacet;
+import org.dive4elements.river.artifacts.model.DataFacet;
+import org.dive4elements.river.artifacts.model.EmptyFacet;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.ReportFacet;
+import org.dive4elements.river.artifacts.model.WQCKms;
+import org.dive4elements.river.artifacts.model.WQKms;
+import org.dive4elements.river.artifacts.model.WaterlevelFacet;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+public class DischargeLongitudinalSection
+extends      DefaultState
+implements   FacetTypes
+{
+    private static Logger log =
+        Logger.getLogger(DischargeLongitudinalSection.class);
+
+    static {
+        // Active/deactivate facets.
+        FacetActivity.Registry.getInstance().register(
+            "winfo",
+            new FacetActivity() {
+                @Override
+                public Boolean isInitialActive(
+                    Artifact artifact,
+                    Facet    facet,
+                    String   output
+                ) {
+                    String name = facet.getName();
+                    if (name.equals(DISCHARGE_LONGITUDINAL_Q_INFOLD_CUT)) {
+                        return Boolean.FALSE;
+                    }
+                    return Boolean.TRUE;
+                }
+            });
+    }
+
+    @Override
+    public Object computeAdvance(
+        FLYSArtifact artifact,
+        String       hash,
+        CallContext  context,
+        List<Facet>  facets,
+        Object       old
+    ) {
+        if (artifact instanceof ChartArtifact) {
+            ChartArtifact chart = (ChartArtifact)artifact;
+            facets.add(new EmptyFacet());
+            return null;
+        }
+
+        Calculation4Access access = new Calculation4Access(artifact);
+
+        CalculationResult res = old instanceof CalculationResult
+            ? (CalculationResult)old
+            : new Calculation4(access).calculate();
+
+        if (facets == null) {
+            return res;
+        }
+
+        WQKms [] wqkms = (WQKms [])res.getData();
+
+        for (int i = 0; i < wqkms.length; i++) {
+            String nameW = null;
+            String nameQ = null;
+
+            if (access.isQ()) {
+                nameQ = wqkms[i].getName();
+                nameW = "W(" + nameQ + ")";
+            }
+            else {
+                nameW = wqkms[i].getName();
+                nameQ = "Q(" + nameW + ")";
+            }
+
+            // Do not generate Waterlevel/Waterline facets
+            // for Q only curves.
+            if (!(wqkms[i] instanceof ConstantWQKms)) {
+
+                Facet w = new WaterlevelFacet(
+                    i, DISCHARGE_LONGITUDINAL_W, nameW);
+
+                Facet s = new CrossSectionWaterLineFacet(i, nameW);
+
+                Facet q = new WaterlevelFacet(
+                    i, DISCHARGE_LONGITUDINAL_Q, nameQ);
+                facets.add(s);
+                facets.add(w);
+                facets.add(q);
+            }
+            else {
+                Facet q;
+                if (nameQ.contains("geschnitten")) {
+                    q = new WaterlevelFacet(
+                        i, DISCHARGE_LONGITUDINAL_Q_INFOLD_CUT, nameQ);
+                }
+                else {
+                    q = new WaterlevelFacet(
+                        i, DISCHARGE_LONGITUDINAL_Q_INFOLD, nameQ);
+                }
+                facets.add(q);
+            }
+
+            if (wqkms[i] instanceof WQCKms) {
+                // TODO DO i18n
+
+                String nameC = nameW.replace(
+                    "benutzerdefiniert",
+                    "benutzerdefiniert [korrigiert]");
+
+                Facet c = new WaterlevelFacet(
+                    i, DISCHARGE_LONGITUDINAL_C, nameC);
+
+                // Here, avoid index clash with Facet "s" above and
+                // signal the WINFO later that we want to access Cs.
+                Facet r = new CrossSectionWaterLineFacet(i + 1, nameC);
+
+                facets.add(c);
+                facets.add(r);
+            }
+        }
+
+        if (wqkms.length > 0) {
+            facets.add(new DataFacet(CSV, "CSV data"));
+            facets.add(new DataFacet(WST, "WST data"));
+        }
+
+        if (res.getReport().hasProblems()) {
+            facets.add(new ReportFacet());
+        }
+
+        return res;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/DischargeState.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,161 @@
+package org.dive4elements.river.artifacts.states;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.artifacts.common.model.KVP;
+
+import org.dive4elements.river.model.DischargeZone;
+import org.dive4elements.river.model.River;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.utils.FLYSUtils;
+
+
+public class DischargeState extends MultiIntArrayState {
+
+    public static final String MAIN_CHANNEL  = "main_channel";
+    public static final String TOTAL_CHANNEL = "total_channel";
+
+
+    private static final Logger logger = Logger.getLogger(DischargeState.class);
+
+
+    /** Let client display a matrix. */
+    @Override
+    public String getUIProvider() {
+        return "parameter-matrix";
+    }
+
+
+    /**
+     * This method fetches all DischargeZones for a given river (extracted from
+     * <i>artifact</i>) and returns a KVP[] where the key is the ID of the
+     * DischargeZone and the value is a string that consists of lower discharge
+     * and upper discharge.
+     *
+     * @param artifact Needs to be a FLYSArtifact that provides river
+     * information.
+     * @param parameterName The name of a parameter.
+     *
+     * @return a KVP[].
+     */
+    @Override
+    protected KVP<Integer, String>[] getOptions(
+        Artifact artifact,
+        String   parameterName
+    )
+    throws IllegalArgumentException
+    {
+        if (!testParameterName(parameterName)) {
+            throw new IllegalArgumentException(
+                "Invalid parameter for state: '" + parameterName + "'");
+        }
+
+        List<DischargeZone> zones = getDischargeZones(artifact);
+
+        KVP[] kvp = new KVP[zones.size()];
+
+        for (int i = 0, Z = zones.size(); i < Z; i++) {
+            DischargeZone zone = zones.get(i);
+
+            String lower = zone.getLowerDischarge();
+            String upper = zone.getUpperDischarge();
+
+            if (lower.equals(upper)) {
+                kvp[i] = new KVP(zone.getId(), lower);
+            }
+            else {
+                kvp[i] = new KVP(zone.getId(), lower + " - " + upper);
+            }
+        }
+
+        return kvp;
+    }
+
+
+    @Override
+    protected String getLabelFor(
+        CallContext cc,
+        String      parameterName,
+        int         value
+    ) throws IllegalArgumentException
+    {
+        if (!testParameterName(parameterName)) {
+            throw new IllegalArgumentException(
+                "Invalid parameter for state: '" + parameterName + "'");
+        }
+
+        DischargeZone zone = DischargeZone.getDischargeZoneById(value);
+
+        if (zone == null) {
+            throw new IllegalArgumentException(
+                "Invalid id for DischargeZone: '" + value + "'");
+        }
+
+        String lo = zone.getLowerDischarge();
+        String hi = zone.getUpperDischarge();
+
+        return hi != null && !lo.equals(hi)
+            ? lo + " - " + hi
+            : lo;
+    }
+
+
+    /**
+     * This method might be used to test, if a parameter name is handled by this
+     * state.
+     *
+     * @param parameterName The name of a parameter.
+     *
+     * @return true, if parameterName is one of <i>MAIN_CHANNEL</i> or
+     * <i>TOTAL_CHANNEL</i>. Otherwise false.
+     */
+    protected boolean testParameterName(String parameterName) {
+        if (parameterName == null || parameterName.length() == 0) {
+            return false;
+        }
+        else if (parameterName.equals(MAIN_CHANNEL)) {
+            return true;
+        }
+        else if (parameterName.equals(TOTAL_CHANNEL)) {
+            return true;
+        }
+        else {
+            return false;
+        }
+    }
+
+
+    /**
+     * Returns all discharge zones for a given river. The river information is
+     * extracted from <i>artifact</i> using FLYSUtils.getRiver().
+     *
+     * @param artifact Needs to be a FLYSArtifact that stores a rivername.
+     *
+     * @return a list of DischargeZones.
+     *
+     * @throws IllegalArgumentException if no river information is provided by
+     * <i>artifact</i>.
+     */
+    protected List<DischargeZone> getDischargeZones(Artifact artifact)
+    throws IllegalArgumentException
+    {
+        River river = FLYSUtils.getRiver((FLYSArtifact) artifact);
+
+        if (river == null) {
+            throw new IllegalArgumentException("No river found");
+        }
+
+        List<DischargeZone> zones = DischargeZone.getDischargeZones(river);
+
+        logger.debug("Found " + zones.size() + " DischargeZones.");
+
+        return zones;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/DistanceOnlySelect.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,45 @@
+package org.dive4elements.river.artifacts.states;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.Artifact;
+
+import org.dive4elements.river.artifacts.access.RangeAccess;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+
+public class DistanceOnlySelect extends DistanceSelect {
+
+    private static Logger logger = Logger.getLogger(DistanceOnlySelect.class);
+
+    @Override
+    protected String getUIProvider() {
+        return "distance_only_panel";
+    }
+
+
+    @Override
+    public boolean validate(Artifact artifact)
+    throws IllegalArgumentException
+    {
+        // TODO think about better hierarchy wrt RangeState#validate.
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        try {
+            RangeAccess rangeAccess = new RangeAccess(flys, null);
+            double from = rangeAccess.getFrom();
+            double to   = rangeAccess.getTo();
+
+            double[] minmax = getMinMax(flys);
+
+            return validateBounds(minmax[0], minmax[1], from, to);
+        }
+        catch (NumberFormatException nfe) {
+            throw new IllegalArgumentException("error_invalid_double_value");
+        }
+        catch (NullPointerException npe) {
+            throw new IllegalArgumentException("error_empty_state");
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/DistanceSelect.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,24 @@
+package org.dive4elements.river.artifacts.states;
+
+import org.apache.log4j.Logger;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DistanceSelect extends ComputationRangeState {
+
+    /** The logger used in this class. */
+    private static Logger logger = Logger.getLogger(DistanceSelect.class);
+
+
+    public DistanceSelect() {
+    }
+
+
+    @Override
+    protected String getUIProvider() {
+        return "distance_panel";
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/DurationCurveState.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,113 @@
+package org.dive4elements.river.artifacts.states;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+
+import org.dive4elements.river.artifacts.access.RangeAccess;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.WINFOArtifact;
+import org.dive4elements.river.artifacts.ChartArtifact;
+
+import org.dive4elements.river.artifacts.model.DurationCurveFacet;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.WQDay;
+
+import org.dive4elements.river.artifacts.model.DataFacet;
+import org.dive4elements.river.artifacts.model.ReportFacet;
+import org.dive4elements.river.artifacts.model.EmptyFacet;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+
+import org.dive4elements.river.artifacts.resources.Resources;
+
+import org.dive4elements.river.utils.FLYSUtils;
+
+
+/**
+ * The final state that will be reached after the duration curve calculation
+ * mode has been chosen.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DurationCurveState
+extends      DefaultState
+implements   FacetTypes
+{
+    /** The logger that is used in this state. */
+    private static Logger logger = Logger.getLogger(DurationCurveState.class);
+
+    public DurationCurveState() {
+    }
+
+
+    @Override
+    public Object computeAdvance(
+        FLYSArtifact artifact,
+        String       hash,
+        CallContext  context,
+        List<Facet>  facets,
+        Object       old
+    ) {
+        if (artifact instanceof WINFOArtifact) {
+            WINFOArtifact winfo = (WINFOArtifact)artifact;
+
+            CalculationResult res;
+
+            if (old instanceof CalculationResult) {
+                res = (CalculationResult)old;
+            }
+            else {
+                res = winfo.getDurationCurveData();
+            }
+
+            WQDay wqday = (WQDay)res.getData();
+
+            if (wqday != null && facets != null) {
+                RangeAccess rangeAccess = new RangeAccess(winfo, context);
+                // Create an i18ed name for a (w or q) duration curve facet.
+                Object[] args = new Object[] {
+                    FLYSUtils.getRiver(winfo).getName(),
+                    rangeAccess.getLocations()[0]
+                };
+
+                String nameW = Resources.getMsg(
+                    context.getMeta(),
+                    "chart.duration.curve.curve.w",
+                    "",
+                    args);
+
+                String nameQ = Resources.getMsg(
+                    context.getMeta(),
+                    "chart.duration.curve.curve.q",
+                    "",
+                    args);
+
+                Facet w = new DurationCurveFacet(DURATION_W, nameW);
+                Facet q = new DurationCurveFacet(DURATION_Q, nameQ);
+
+                facets.add(w);
+                facets.add(q);
+
+                facets.add(new DataFacet(CSV, "CSV data"));
+                facets.add(new DataFacet(PDF, "PDF data"));
+
+                if (res.getReport().hasProblems()) {
+                    facets.add(new ReportFacet());
+                }
+            }
+
+            return res;
+        }
+        else if (artifact instanceof ChartArtifact) {
+            ChartArtifact chart = (ChartArtifact)artifact;
+            facets.add(new EmptyFacet());
+            return null;
+        }
+        return null;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/EnterLocationState.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,41 @@
+package org.dive4elements.river.artifacts.states;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+import org.dive4elements.river.utils.FLYSUtils;
+
+
+/**
+ * Get me a double (km).
+ */
+public class EnterLocationState extends InputDoubleState {
+
+    /** Provoke this kind of provider in the UI. */
+    @Override
+    protected String getUIProvider() {
+        return "location_panel";
+    }
+
+
+    /** Allow from min km of river. */
+    @Override
+    protected Object getLower(FLYSArtifact flys) {
+        double[] lowerUpper = FLYSUtils.getRiverMinMax(flys);
+
+        return lowerUpper != null
+            ? lowerUpper[0]
+            : 0;
+    }
+
+
+    /** Allow to max km of river. */
+    @Override
+    protected Object getUpper(FLYSArtifact flys) {
+        double[] lowerUpper = FLYSUtils.getRiverMinMax(flys);
+
+        return lowerUpper != null
+            ? lowerUpper[1]
+            : 0;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/EnterMultipleLocationsState.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,72 @@
+package org.dive4elements.river.artifacts.states;
+
+import org.apache.log4j.Logger;
+import org.w3c.dom.Element;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.common.utils.StringUtils;
+import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator;
+import org.dive4elements.river.artifacts.WINFOArtifact;
+
+
+/**
+ * Get me doubles (km).
+ */
+public class EnterMultipleLocationsState extends EnterLocationState {
+    /** The logger for this class. */
+    private static Logger logger = Logger.getLogger(EnterMultipleLocationsState.class);
+
+    @Override
+    protected String getUIProvider() {
+        logger.debug("multi location panel");
+        return "multi_location_panel";
+    }
+
+
+    /** Deal with multiple double values. */
+    @Override
+    protected String getLabelFor(
+        CallContext cc,
+        String      name,
+        String      value,
+        String      type
+    ) {
+        String[] vals = value.split(" ");
+        for (int i = 0; i < vals.length; i++) {
+            vals[i] = super.getLabelFor(cc, name, vals[i], type);
+        }
+
+        return StringUtils.join(" ", vals);
+    }
+
+    /**
+     * This method creates a list of items. These items represent the amount of
+     * input data that is possible for this state.
+     *
+     * @param cr The ElementCreator.
+     * @param name The name of the amount of data.
+     *
+     * @return a list of items.
+     */
+    @Override
+    protected Element[] createItems(
+        ElementCreator cr,
+        Artifact    artifact,
+        String      name,
+        CallContext context
+    ) {
+        if (name.equals("reference_endpoint")) {
+            Element[] elements = new Element[1];
+            WINFOArtifact winfo = (WINFOArtifact) artifact;
+            Double km = winfo.getReferenceStartKm();
+            elements[0] = createItem(
+                cr,
+                new String[] {"start_km", km.toString()});
+            return elements;
+        }
+        return null;
+    }
+
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/FloodMapState.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,1018 @@
+package org.dive4elements.river.artifacts.states;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.apache.velocity.Template;
+
+import org.geotools.feature.FeatureCollection;
+import org.geotools.feature.FeatureCollections;
+
+import org.geotools.feature.simple.SimpleFeatureBuilder;
+
+import org.hibernate.HibernateException;
+
+import org.opengis.feature.simple.SimpleFeature;
+import org.opengis.feature.simple.SimpleFeatureType;
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.LineString;
+import com.vividsolutions.jts.geom.Polygon;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.artifacts.GlobalContext;
+
+import org.dive4elements.artifacts.common.utils.FileTools;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+import org.dive4elements.river.artifacts.access.RangeAccess;
+
+import org.dive4elements.river.artifacts.context.FLYSContext;
+
+import org.dive4elements.river.artifacts.model.CalculationMessage;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.LayerInfo;
+import org.dive4elements.river.artifacts.model.WQKms;
+
+import org.dive4elements.river.artifacts.model.map.HWS;
+import org.dive4elements.river.artifacts.model.map.HWSContainer;
+import org.dive4elements.river.artifacts.model.map.HWSFactory;
+import org.dive4elements.river.artifacts.model.map.WMSLayerFacet;
+import org.dive4elements.river.artifacts.model.map.WSPLGENCalculation;
+import org.dive4elements.river.artifacts.model.map.WSPLGENJob;
+import org.dive4elements.river.artifacts.model.map.WSPLGENReportFacet;
+
+import org.dive4elements.river.artifacts.resources.Resources;
+
+import org.dive4elements.river.exports.WstWriter;
+
+import org.dive4elements.river.model.CrossSectionTrack;
+import org.dive4elements.river.model.DGM;
+import org.dive4elements.river.model.Floodplain;
+import org.dive4elements.river.model.RiverAxis;
+
+import org.dive4elements.river.utils.ArtifactMapfileGenerator;
+import org.dive4elements.river.utils.FLYSUtils;
+import org.dive4elements.river.utils.GeometryUtils;
+import org.dive4elements.river.utils.MapfileGenerator;
+
+import org.dive4elements.river.wsplgen.FacetCreator;
+import org.dive4elements.river.wsplgen.JobObserver;
+import org.dive4elements.river.wsplgen.Scheduler;
+
+public class FloodMapState
+extends      DefaultState
+implements   FacetTypes
+{
+    /** The logger that is used in this state. */
+    private static Logger logger = Logger.getLogger(FloodMapState.class);
+
+
+    public static final String KEEP_ARTIFACT_DIR =
+        System.getProperty("flys.uesk.keep.artifactsdir", "false");
+
+
+    public static final String OUTPUT_NAME = "floodmap";
+
+    public static final String WSP_ARTIFACT = "wsp";
+
+    public static final String WINFO_WSP_STATE_ID = "state.winfo.waterlevel";
+
+    public static final String WSPLGEN_PARAMETER_FILE = "wsplgen.par";
+    public static final String WSPLGEN_BARRIERS_LINES = "barrier_lines.shp";
+    public static final String WSPLGEN_BARRIERS_POLY  = "barrier_polygons.shp";
+    public static final String WSPLGEN_AXIS           = "axis.shp";
+    public static final String WSPLGEN_QPS            = "qps.shp";
+    public static final String WSPLGEN_FLOODPLAIN     = "talaue.shp";
+    public static final String WSPLGEN_WSP_FILE       = "waterlevel.wst";
+    public static final String WSPLGEN_OUTPUT_FILE    = "wsplgen.shp";
+    public static final String WSPLGEN_USER_SHAPE     = "user-rgd.shp";
+    public static final String WSPLGEN_USER_ZIP       = "user-rgd.zip";
+    public static final String WSPLGEN_USER_FILENAME  = "user-rgd";
+
+    public static final String WSPLGEN_QPS_NAME = "qps";
+
+    public static final int WSPLGEN_DEFAULT_OUTPUT = 0;
+
+    private static final String HWS_LINES_SHAPE = "hws-lines.shp";
+
+    private static final String I18N_HWS_POINTS_OFFICIAL = "floodmap.hws.points.official";
+    private static final String I18N_HWS_LINES_OFFICIAL = "floodmap.hws.lines.official";
+    private static final String HWS_LINES = "hws-lines";
+    private static final String HWS_POINT_SHAPE = "hws-points.shp";
+    private static final String HWS_POINTS = "hws-points";
+
+    /**
+     * @param orig
+     * @param owner
+     * @param context
+     * @param callMeta
+     */
+    @Override
+    public void initialize(
+        Artifact orig,
+        Artifact owner,
+        Object   context,
+        CallMeta callMeta
+    ) {
+        logger.info("Initialize State with Artifact: " + orig.identifier());
+
+        copyShapeDir(orig, owner);
+        modifyFacets(orig, owner, context, callMeta);
+
+        ArtifactMapfileGenerator amfg = new ArtifactMapfileGenerator();
+        try {
+            amfg.generate();
+        }
+        catch (IOException e) {
+            logger.error(e.getMessage(), e);
+        }
+    }
+
+
+    protected void copyShapeDir(Artifact orig, Artifact owner) {
+        File origDir = getDirectory((FLYSArtifact) orig);
+        File thisDir = getDirectory((FLYSArtifact) owner);
+
+        FileTools.copyDirectory(origDir, thisDir);
+    }
+
+
+    protected void modifyFacets(
+        Artifact orig,
+        Artifact owner,
+        Object   context,
+        CallMeta callMeta
+    ) {
+        FLYSArtifact flys  = (FLYSArtifact) owner;
+        List<Facet> facets = flys.getFacets();
+        if (facets == null || facets.isEmpty()) {
+            logger.warn("No facets for '" + OUTPUT_NAME + "' given!");
+            return;
+        }
+
+        for (Facet facet: facets) {
+            if (facet instanceof WMSLayerFacet) {
+                WMSLayerFacet wms = (WMSLayerFacet) facet;
+
+                List<String> layers = wms.getLayers();
+
+                for (String layer: layers) {
+                    if (layer.startsWith(MapfileGenerator.MS_WSPLGEN_PREFIX)) {
+                        wms.removeLayer(layer);
+
+                        String newLayer = MapfileGenerator.MS_WSPLGEN_PREFIX +
+                            owner.identifier();
+
+                        wms.addLayer(newLayer);
+
+                        logger.debug(
+                            "Replaced layer: " + layer + " with " + newLayer);
+                    }
+                }
+            }
+        }
+    }
+
+
+    @Override
+    public Object computeAdvance(
+        FLYSArtifact artifact,
+        String       hash,
+        CallContext  context,
+        List<Facet>  facets,
+        Object       old
+    ) {
+        logger.debug("FloodMapState.computeAdvance");
+
+        File artifactDir = getDirectory(artifact);
+
+        if (artifactDir == null) {
+            logger.error("Could not create directory for WSPLGEN results!");
+            return null;
+        }
+
+        WSPLGENCalculation calculation = new WSPLGENCalculation();
+
+        FacetCreator facetCreator = new FacetCreator(
+            artifact, context, hash, getID(), facets);
+
+        WSPLGENJob job = prepareWSPLGENJob(
+            artifact,
+            facetCreator,
+            artifactDir,
+            context,
+            calculation);
+
+        CalculationResult  res   = new CalculationResult(null, calculation);
+        WSPLGENReportFacet report= new WSPLGENReportFacet(
+            ComputeType.ADVANCE, hash, getID(), res);
+
+        facets.add(report);
+
+        if (job == null) {
+            if (KEEP_ARTIFACT_DIR.equals("false")) {
+                removeDirectory(artifact);
+            }
+
+            calculation.addError(-1, Resources.getMsg(
+                context.getMeta(),
+                "wsplgen.job.error",
+                "wsplgen.job.error"));
+
+            logger.error("No WSPLGEN processing has been started!");
+
+            return null;
+        }
+
+        context.afterCall(CallContext.BACKGROUND);
+        context.addBackgroundMessage(new CalculationMessage(
+            JobObserver.STEPS.length,
+            0,
+            Resources.getMsg(
+                context.getMeta(),
+                "wsplgen.job.queued",
+                "wsplgen.job.queued")
+        ));
+
+        GlobalContext gc    = (GlobalContext) context.globalContext();
+        Scheduler scheduler = (Scheduler) gc.get(FLYSContext.SCHEDULER);
+        scheduler.addJob(job);
+
+        return null;
+    }
+
+
+    /**
+     * Returns (and creates if not existing) the directory for storing WSPLEN
+     * data for the owner artifact.
+     *
+     * @param artifact The owner Artifact.
+     *
+     * @return the directory for WSPLEN data.
+     */
+    protected File getDirectory(FLYSArtifact artifact) {
+        String shapePath = FLYSUtils.getXPathString(
+            FLYSUtils.XPATH_FLOODMAP_SHAPEFILE_DIR);
+
+        File artifactDir = FileTools.getDirectory(
+            shapePath, artifact.identifier());
+
+        return artifactDir;
+    }
+
+
+    /**
+     * Removes the directory and all its content where the required data and the
+     * results of WSPLGEN are stored. Should be called in endOfLife().
+     */
+    protected void removeDirectory(FLYSArtifact artifact) {
+        String shapePath = FLYSUtils.getXPathString(
+            FLYSUtils.XPATH_FLOODMAP_SHAPEFILE_DIR);
+
+        File artifactDir = new File(shapePath, artifact.identifier());
+
+        if (artifactDir.exists()) {
+            logger.info("Delete directory: " + artifactDir.getAbsolutePath());
+            if (!FileTools.deleteRecursive(artifactDir)) {
+                logger.warn("Could not delete directory: "
+                        + artifactDir.getAbsolutePath());
+            }
+        }
+        else {
+            logger.debug("There is no directory to remove.");
+        }
+    }
+
+
+    @Override
+    public void endOfLife(Artifact artifact, Object callContext) {
+        logger.info("FloodMapState.endOfLife: " + artifact.identifier());
+
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        Scheduler scheduler = Scheduler.getInstance();
+        scheduler.cancelJob(flys.identifier());
+    }
+
+
+    protected WSPLGENJob prepareWSPLGENJob(
+        FLYSArtifact       artifact,
+        FacetCreator       facetCreator,
+        File               artifactDir,
+        CallContext        context,
+        WSPLGENCalculation calculation
+    ) {
+        logger.debug("FloodMapState.prepareWSPLGENJob");
+        String scenario = artifact.getDataAsString("scenario");
+
+        WSPLGENJob job = new WSPLGENJob(
+            artifact,
+            artifactDir,
+            facetCreator,
+            context,
+            calculation);
+
+        File paraFile = new File(artifactDir, WSPLGEN_PARAMETER_FILE);
+
+        setOut(artifact, job);
+        setRange(artifact, job);
+        setDelta(artifact, job);
+        setGel(artifact, job);
+        setDist(artifact, job);
+        setAxis(artifact, artifactDir, job);
+        setPro(artifact, artifactDir, job);
+        setDgm(artifact, job, context);
+        setArea(artifact, artifactDir, job);
+        setOutFile(artifact, job);
+        setWsp(artifact, context, artifactDir, job);    // WSP
+        if (scenario.equals("scenario.current")) {
+            setOfficialHWS(artifact, facetCreator, artifactDir, job);
+        }
+        else if (scenario.equals("scenario.scenario")) {
+            setAdditionalHWS(artifact, facetCreator, artifactDir, job);
+            setLine(artifact, facetCreator, artifactDir, job);
+            setUserShape(artifact, facetCreator, artifactDir, job);
+        }
+        // TODO
+        // setWspTag(artifact, job);
+
+        try {
+            job.toFile(paraFile);
+
+            return job;
+        }
+        catch (IOException ioe) {
+            logger.warn("Cannot write PAR file: " + ioe.getMessage());
+        }
+        catch (IllegalArgumentException iae) {
+            logger.warn("Cannot write PAR file: " + iae.getMessage());
+        }
+
+        return null;
+    }
+
+
+    private void setAdditionalHWS(
+        FLYSArtifact artifact,
+        FacetCreator facetCreator,
+        File dir,
+        WSPLGENJob job) {
+        File line = new File(dir, HWS_LINES_SHAPE);
+        boolean lines = line.exists();
+        logger.debug("shp file exists: " + lines);
+        if (lines) {
+            job.addLin(dir + "/" + HWS_LINES_SHAPE);
+            facetCreator.createShapeFacet(I18N_HWS_LINES_OFFICIAL,
+                MapfileGenerator.MS_LAYER_PREFIX + HWS_LINES,
+                FLOODMAP_LINES, 2);
+        }
+        File point = new File(dir, HWS_POINT_SHAPE);
+        boolean points = point.exists();
+        logger.debug("shp file exists: " + points);
+        if (points) {
+            facetCreator.createShapeFacet(I18N_HWS_POINTS_OFFICIAL,
+                MapfileGenerator.MS_LAYER_PREFIX + HWS_POINTS,
+                FLOODMAP_FIXPOINTS, 3);
+        }
+    }
+
+
+    private void setOfficialHWS(
+        FLYSArtifact artifact,
+        FacetCreator facetCreator,
+        File artifactDir,
+        WSPLGENJob job) {
+        String river = artifact.getDataAsString("river");
+
+        HWSContainer hwsLines = HWSFactory.getHWSLines(river);
+        List<HWS> selectedLines = hwsLines.getOfficialHWS();
+
+        FeatureCollection collectionLines = FeatureCollections.newCollection();
+        SimpleFeatureType lineType = null;
+        for (HWS h : selectedLines) {
+            lineType = h.getFeatureType();
+            collectionLines.add(h.getFeature());
+        }
+        boolean successLines = false;
+        if (lineType != null && collectionLines.size() > 0) {
+            File shapeLines = new File(artifactDir, HWS_LINES_SHAPE);
+            successLines = GeometryUtils.writeShapefile(
+                shapeLines, lineType, collectionLines);
+        }
+       if (successLines) {
+            createMapfile(
+                artifact,
+                artifactDir,
+                MapfileGenerator.MS_LAYER_PREFIX + "hws-lines",
+                HWS_LINES_SHAPE,
+                "LINE",
+                "31467",
+                "hws");
+            job.addLin(artifactDir + "/" + HWS_LINES_SHAPE);
+            facetCreator.createShapeFacet(I18N_HWS_LINES_OFFICIAL,
+                MapfileGenerator.MS_LAYER_PREFIX + HWS_LINES,
+                FLOODMAP_HWS_LINES,2);
+        }
+    }
+
+
+    private void createMapfile(
+        FLYSArtifact artifact,
+        File artifactDir,
+        String name,
+        String hwsShapefile,
+        String type,
+        String srid,
+        String group
+    ) {
+        LayerInfo info = new LayerInfo();
+        info.setName(name + artifact.identifier());
+        info.setType(type);
+        info.setDirectory(artifact.identifier());
+        info.setTitle(name);
+        info.setData(hwsShapefile);
+        info.setSrid(srid);
+        info.setGroupTitle(group);
+        MapfileGenerator generator = new ArtifactMapfileGenerator();
+        Template tpl = generator.getTemplateByName(MapfileGenerator.SHP_LAYER_TEMPLATE);
+        try {
+            File layer = new File(artifactDir.getCanonicalPath() + "/" + name);
+            generator.writeLayer(info, layer, tpl);
+            List<String> layers = new ArrayList<String>();
+            layers.add(layer.getAbsolutePath());
+            generator.generate();
+        }
+        catch(FileNotFoundException fnfe) {
+            logger.warn("Could not find mapfile for hws layer");
+        }
+        catch (Exception ioe) {
+            logger.warn("Could not create mapfile for hws layer");
+            logger.warn(Arrays.toString(ioe.getStackTrace()));
+        }
+    }
+
+
+    protected void setOut(FLYSArtifact artifact, WSPLGENJob job) {
+        job.setOut(WSPLGEN_DEFAULT_OUTPUT);
+    }
+
+
+    protected void setRange(FLYSArtifact artifact, WSPLGENJob job) {
+        RangeAccess rangeAccess = new RangeAccess(artifact, null);
+        double[] range = rangeAccess.getKmRange();
+
+        job.setStart(range[0]);
+        job.setEnd(range[1]);
+    }
+
+
+    protected void setDelta(FLYSArtifact artifact, WSPLGENJob job) {
+        String from = artifact.getDataAsString("diff_from");
+        String to   = artifact.getDataAsString("diff_to");
+        String diff = artifact.getDataAsString("diff_diff");
+
+        try {
+            job.setFrom(Double.parseDouble(from));
+        }
+        catch (NumberFormatException nfe) {
+        }
+
+        try {
+            job.setTo(Double.parseDouble(to));
+        }
+        catch (NumberFormatException nfe) {
+        }
+
+        try {
+            job.setDiff(Double.parseDouble(diff));
+        }
+        catch (NumberFormatException nfe) {
+        }
+    }
+
+
+    protected void setGel(FLYSArtifact artifact, WSPLGENJob job) {
+        String gel = artifact.getDataAsString("scenario");
+
+        logger.debug("Selected gel = '" + gel + "'");
+
+        if (gel == null || gel.length() == 0) {
+            job.setGel(WSPLGENJob.GEL_NOSPERRE);
+        }
+        else if (gel.equals("scenario.current")) {
+            job.setGel(WSPLGENJob.GEL_SPERRE);
+        }
+        else if (gel.equals("scenario.scenario")) {
+            job.setGel(WSPLGENJob.GEL_SPERRE);
+        }
+        else {
+            job.setGel(WSPLGENJob.GEL_NOSPERRE);
+        }
+    }
+
+
+    protected void setDist(FLYSArtifact artifact, WSPLGENJob job) {
+        String dist = artifact.getDataAsString("profile_distance");
+
+        try {
+            job.setDist(Double.parseDouble(dist));
+        }
+        catch (NumberFormatException nfe) {
+            // nothing to do here
+        }
+    }
+
+
+    protected void setLine(
+        FLYSArtifact artifact,
+        FacetCreator facetCreator,
+        File         dir,
+        WSPLGENJob   job
+    ) {
+        String river   = artifact.getDataAsString("river");
+        String geoJSON = artifact.getDataAsString("uesk.barriers");
+        String srid    = FLYSUtils.getRiverDGMSrid(river);
+        String srs     = "EPSG:" + srid;
+
+        if (geoJSON == null || geoJSON.length() == 0) {
+            logger.debug("No barrier features in parameterization existing.");
+            return;
+        }
+
+        SimpleFeatureType ft = getBarriersFeatureType(
+            "barriers", srs, Geometry.class);
+
+        List<SimpleFeature> features = GeometryUtils.parseGeoJSON(geoJSON, ft);
+        if (features == null || features.isEmpty()) {
+            logger.debug("No barrier features extracted.");
+            return;
+        }
+
+        FeatureCollection[] fcs = splitLinesAndPolygons(features);
+
+        File shapeLines = new File(dir, WSPLGEN_BARRIERS_LINES);
+        File shapePolys = new File(dir, WSPLGEN_BARRIERS_POLY);
+
+        Object[][] obj = new Object[][] {
+            new Object[] { "typ", String.class }
+        };
+
+        String scenario = job.getGel();
+
+        boolean l = GeometryUtils.writeShapefile(
+            shapeLines,
+            GeometryUtils.buildFeatureType("lines", srs, LineString.class, obj),
+            fcs[0]);
+
+        if (l) {
+            logger.debug(
+                "Successfully created barrier line shapefile. " +
+                "Write shapefile path into WSPLGEN job.");
+            createMapfile(
+                artifact,
+                dir,
+                MapfileGenerator.MS_LAYER_PREFIX + "barriers-lines",
+                WSPLGEN_BARRIERS_LINES,
+                "LINE",
+                srid,
+                "barriers");
+
+            if (scenario.equals(WSPLGENJob.GEL_NOSPERRE)) {
+                logger.debug("WSPLGEN will not use barrier features.");
+            }
+            else {
+                job.addLin(shapeLines.getAbsolutePath());
+            }
+        }
+
+        boolean p = GeometryUtils.writeShapefile(
+            shapePolys,
+            GeometryUtils.buildFeatureType("polygons", srs, Polygon.class, obj),
+            fcs[1]);
+
+
+        if (p) {
+            logger.debug(
+                "Successfully created barrier polygon shapefile. " +
+                "Write shapefile path into WSPLGEN job.");
+            createMapfile(
+                artifact,
+                dir,
+                MapfileGenerator.MS_LAYER_PREFIX + "barriers-poly",
+                shapePolys.getAbsolutePath(),
+                "POLYGON",
+                srid,
+                "barriers");
+
+            if (scenario.equals(WSPLGENJob.GEL_NOSPERRE)) {
+                logger.debug("WSPLGEN will not use barrier features.");
+            }
+            else {
+                job.addLin(shapePolys.getAbsolutePath());
+            }
+        }
+
+        if (p || l) {
+            facetCreator.createBarrierFacet();
+        }
+    }
+
+
+    protected void setUserShape(
+        FLYSArtifact artifact,
+        FacetCreator facetCreator,
+        File         dir,
+        WSPLGENJob   job
+    ) {
+        File archive = new File(dir, WSPLGEN_USER_SHAPE);
+        boolean exists = archive.exists();
+        logger.debug("shp file exists: " + exists);
+        if (exists) {
+            job.addLin(dir + "/" + WSPLGEN_USER_SHAPE);
+            facetCreator.createShapeFacet(FacetCreator.I18N_USERSHAPE,
+                MapfileGenerator.MS_LAYER_PREFIX + "user-rgd",
+                FLOODMAP_USERSHAPE,
+                4);
+        }
+    }
+
+    protected SimpleFeatureType getBarriersFeatureType(
+        String name,
+        String srs,
+        Class  type
+    ) {
+        Object[][] attrs = new Object[3][];
+        attrs[0] = new Object[] { "typ", String.class };
+        attrs[1] = new Object[] { "elevation", Double.class };
+        attrs[2] = new Object[] { "mark.selected", Integer.class };
+
+        return GeometryUtils.buildFeatureType(name, srs, type, attrs);
+    }
+
+
+    protected FeatureCollection[] splitLinesAndPolygons(List<SimpleFeature> f) {
+        FeatureCollection lines    = FeatureCollections.newCollection();
+        FeatureCollection polygons = FeatureCollections.newCollection();
+
+        for (SimpleFeature feature: f) {
+            Geometry geom = (Geometry) feature.getDefaultGeometry();
+
+
+            if (geom instanceof LineString) {
+                geom = applyElevationAttribute(feature, geom);
+                lines.add(feature);
+            }
+            else if (geom instanceof Polygon) {
+                geom = applyElevationAttribute(feature, geom);
+                polygons.add(feature);
+            }
+            else {
+                logger.warn("Feature not supported: " + geom.getClass());
+            }
+        }
+
+        logger.debug("Found " + lines.size() + " barrier lines.");
+        logger.debug("Found " + polygons.size() + " barrier polygons.");
+
+        return new FeatureCollection[] { lines, polygons };
+    }
+
+
+    protected static Geometry applyElevationAttribute(
+        SimpleFeature feature,
+        Geometry      geom
+    ) {
+        logger.debug("Apply elevations for: " + geom.getClass());
+
+        List<Double> elevations = extractElevations(feature);
+        int           numPoints = geom.getNumPoints();
+        int        numElevation = elevations.size();
+
+        String typ = (String) feature.getAttribute("typ");
+
+        if (numPoints > numElevation) {
+            logger.warn("More vertices in Geometry than elevations given.");
+        }
+
+        Coordinate[] c = geom.getCoordinates();
+        for (int i = 0; i < numPoints; i++) {
+            if (i < numElevation) {
+                c[i].z = elevations.get(i);
+            }
+            else if (typ != null && typ.equals("Graben")) {
+                c[i].z = -9999d;
+            }
+            else {
+                c[i].z = 9999d;
+            }
+        }
+
+        return geom;
+    }
+
+
+    protected static List<Double> extractElevations(SimpleFeature feature) {
+        String tmp = (String) feature.getAttribute("elevation");
+        String typ = (String) feature.getAttribute("typ");
+
+        String[] elevations = tmp == null ? null : tmp.split(" ");
+
+        int num = elevations != null ? elevations.length : 0;
+
+        List<Double> list = new ArrayList<Double>(num);
+
+        for (int i = 0; i < num; i++) {
+            try {
+                list.add(Double.parseDouble(elevations[i]));
+            }
+            catch (NumberFormatException nfe) {
+                logger.warn("Error while parsing elevation at pos: " + i);
+                if (typ != null && typ.equals("Graben")) {
+                    list.add(new Double(-9999.0));
+                }
+                else {
+                    list.add(new Double(9999.0));
+                }
+            }
+        }
+
+        return list;
+    }
+
+
+    protected void setAxis(FLYSArtifact artifact, File dir, WSPLGENJob job) {
+        String river = artifact.getDataAsString("river");
+        String srid    = FLYSUtils.getRiverDGMSrid(river);
+        String srs     = "EPSG:" + srid;
+
+        List<RiverAxis> axes = null;
+        try {
+            axes = RiverAxis.getRiverAxis(river);
+        }
+        catch (HibernateException iae) {
+            logger.warn("No valid river axis found for " + river);
+            return;
+        }
+        if (axes == null || axes.isEmpty()) {
+            logger.warn("Could not find river axis for: '" + river + "'");
+            return;
+        }
+
+        SimpleFeatureType ft = GeometryUtils.buildFeatureType(
+            "axis", srs, LineString.class);
+
+        SimpleFeatureBuilder builder = new SimpleFeatureBuilder(ft);
+        FeatureCollection collection = FeatureCollections.newCollection();
+
+        for (int i = 0, n = axes.size(); i < n; i++) {
+            RiverAxis axis = axes.get(i);
+
+            builder.add(axis.getGeom());
+            collection.add(builder.buildFeature(String.valueOf(i)));
+
+            builder.reset();
+        }
+
+        File axisShape = new File(dir, WSPLGEN_AXIS);
+
+        boolean a = GeometryUtils.writeShapefile(
+            axisShape,
+            GeometryUtils.buildFeatureType("axis", srs, LineString.class),
+            collection);
+
+        if (a) {
+            job.setAxis(axisShape.getAbsolutePath());
+        }
+    }
+
+
+    protected void setPro(FLYSArtifact artifact, File dir, WSPLGENJob job) {
+        String river = artifact.getDataAsString("river");
+        String srid    = FLYSUtils.getRiverDGMSrid(river);
+        String srs     = "EPSG:" + srid;
+
+        List<CrossSectionTrack> cst =
+            CrossSectionTrack.getCrossSectionTrack(river, WSPLGEN_QPS_NAME);
+
+        logger.debug("Found " + cst.size() + " CrossSectionTracks.");
+
+        Object[][] attrs = new Object[2][];
+        attrs[0] = new Object[] { "ELEVATION", Double.class };
+        attrs[1] = new Object[] { "KILOMETER", Double.class };
+
+        SimpleFeatureType ft = GeometryUtils.buildFeatureType(
+            "qps", srs, LineString.class, attrs);
+
+        SimpleFeatureBuilder builder = new SimpleFeatureBuilder(ft);
+        FeatureCollection collection = FeatureCollections.newCollection();
+
+        int i = 0;
+        for (CrossSectionTrack track: cst) {
+            builder.reset();
+            builder.add(track.getGeom());
+            builder.add(track.getZ().doubleValue());
+            builder.add(track.getKm().doubleValue());
+
+            collection.add(builder.buildFeature(String.valueOf(i++)));
+        }
+
+        File qpsShape = new File(dir, WSPLGEN_QPS);
+
+        boolean q = GeometryUtils.writeShapefile(
+            qpsShape,
+            GeometryUtils.buildFeatureType("qps", srs, LineString.class, attrs),
+            collection);
+
+        if (q) {
+            job.setPro(qpsShape.getAbsolutePath());
+        }
+    }
+
+
+    protected void setDgm(
+        FLYSArtifact artifact,
+        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);
+
+        if (dgm == null) {
+            logger.warn("Could not find specified DGM.");
+
+            return;
+        }
+
+        File dgmPath = new File (dgm.getPath());
+        if (dgmPath.isAbsolute()) {
+            job.setDgm(dgm.getPath());
+        }
+        else {
+            FLYSContext fc = (FLYSContext)context.globalContext();
+            String prefix = (String) fc.get("dgm-path");
+            job.setDgm(prefix.trim() + dgm.getPath().trim());
+        }
+    }
+
+
+    protected void setArea(FLYSArtifact artifact, File dir, WSPLGENJob job) {
+        String useFloodplain = artifact.getDataAsString("use_floodplain");
+        if (!Boolean.valueOf(useFloodplain)) {
+            logger.debug("WSPLGEN will not use floodplain.");
+            return;
+        }
+
+        String river = artifact.getDataAsString("river");
+        String srid  = FLYSUtils.getRiverDGMSrid(river);
+        String srs   = "EPSG:" + srid;
+
+        Floodplain plain = Floodplain.getFloodplain(river);
+
+        SimpleFeatureType ft = GeometryUtils.buildFeatureType(
+            "talaue", srs, Polygon.class);
+
+        SimpleFeatureBuilder builder = new SimpleFeatureBuilder(ft);
+        builder.add(plain.getGeom());
+
+        FeatureCollection collection = FeatureCollections.newCollection();
+        collection.add(builder.buildFeature("0"));
+
+        File talaueShape = new File(dir, WSPLGEN_FLOODPLAIN);
+
+        boolean t = GeometryUtils.writeShapefile(
+            talaueShape,
+            GeometryUtils.buildFeatureType("talaue", srs, Polygon.class),
+            collection);
+
+        if (t) {
+            job.setArea(talaueShape.getAbsolutePath());
+        }
+    }
+
+
+    protected void setOutFile(FLYSArtifact artifact, WSPLGENJob job) {
+        job.setOutFile(WSPLGEN_OUTPUT_FILE);
+    }
+
+
+    protected WQKms getWQKms(FLYSArtifact flys, CallContext cc) {
+        String   wspString = flys.getDataAsString(WSP_ARTIFACT);
+        if (wspString == null) {
+            logger.debug("getWQKms(): wspString == null");
+            return null;
+        }
+        String[] parts = wspString.split(";");
+        String otherArtifact = parts[0];
+
+        int idx = -1;
+        try {
+            idx = Integer.parseInt(parts[2]);
+        }
+        catch (NumberFormatException nfe) { /* do nothing */ }
+
+        FLYSArtifact src = otherArtifact != null
+            ? FLYSUtils.getArtifact(otherArtifact, cc)
+            : flys;
+
+        logger.debug("Use waterlevel provided by Artifact: " + src.identifier());
+
+        CalculationResult rawData = (CalculationResult) src.compute(
+            cc,
+            null,
+            WINFO_WSP_STATE_ID,
+            ComputeType.ADVANCE,
+            false);
+
+        WQKms[] wqkms = (WQKms[]) rawData.getData();
+
+        return wqkms == null || idx == -1 || idx >= wqkms.length
+            ? null
+            : wqkms[idx];
+    }
+
+
+    protected void setWsp(
+        FLYSArtifact artifact,
+        CallContext  context,
+        File         dir,
+        WSPLGENJob   job)
+    {
+        logger.debug("FloodMapState.setWsp");
+
+        WQKms data = getWQKms(artifact, context);
+
+        if (data == null) {
+            logger.warn("No WST data found!");
+            return;
+        }
+
+        WstWriter writer = new WstWriter(1);
+
+        // TODO REMOVE job.setWspTag(...) This is only used until the user is
+        // able to select the WSP column himself!
+        boolean writeWspTag = true;
+
+        double[] buf = new double[4];
+        logger.debug("Add WST column: " + data.getName());
+        writer.addColumn(data.getName());
+
+        if (writeWspTag) {
+            job.setWspTag(data.getName());
+            writeWspTag = false;
+        }
+
+        for (int i = 0, num = data.size(); i < num; i++) {
+            data.get(i, buf);
+            writer.add(buf);
+        }
+
+        FileOutputStream fout = null;
+
+        try {
+            File wspFile = new File(dir, WSPLGEN_WSP_FILE);
+            fout         = new FileOutputStream(wspFile);
+
+            writer.write(fout);
+
+            job.setWsp(wspFile.getAbsolutePath());
+        }
+        catch (FileNotFoundException fnfe) {
+            logger.warn("Error while writing wsp file: " + fnfe.getMessage());
+        }
+        finally {
+            if (fout != null) {
+                try {
+                    fout.close();
+                }
+                catch (IOException ioe) { /* do nothing */ }
+            }
+        }
+    }
+
+
+
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/FloodplainChoice.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,87 @@
+package org.dive4elements.river.artifacts.states;
+
+import org.w3c.dom.Element;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+
+import org.dive4elements.artifactdatabase.ProtocolUtils;
+
+import org.dive4elements.river.artifacts.resources.Resources;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class FloodplainChoice extends DefaultState {
+
+    public static final String OPTION   = "floodplain.option";
+    public static final String ACTIVE   = "floodplain.active";
+    public static final String INACTIVE = "floodplain.inactive";
+
+    private static final Logger logger =
+        Logger.getLogger(FloodplainChoice.class);
+
+
+    @Override
+    protected String getUIProvider() {
+        return "boolean_panel";
+    }
+
+
+    @Override
+    protected Element[] createItems(
+        XMLUtils.ElementCreator cr,
+        Artifact    artifact,
+        String      name,
+        CallContext context)
+    {
+        CallMeta meta = context.getMeta();
+
+        Element option = createItem(
+            cr,
+            new String[] { Resources.getMsg(meta, OPTION, OPTION), "true" });
+
+        return new Element[] { option };
+    }
+
+
+    @Override
+    protected String getLabelFor(
+        CallContext cc,
+        String      name,
+        String      value,
+        String      type
+    ) {
+        logger.debug("GET LABEL FOR '" + name + "' / '" + value + "'");
+        if (value != null && value.equals("true")) {
+            return Resources.getMsg(cc.getMeta(), ACTIVE, ACTIVE);
+        }
+        else {
+            return Resources.getMsg(cc.getMeta(), INACTIVE, INACTIVE);
+        }
+    }
+
+
+    protected Element createItem(XMLUtils.ElementCreator cr, Object obj) {
+        Element item  = ProtocolUtils.createArtNode(cr, "item", null, null);
+        Element label = ProtocolUtils.createArtNode(cr, "label", null, null);
+        Element value = ProtocolUtils.createArtNode(cr, "value", null, null);
+
+        String[] arr = (String[]) obj;
+
+        label.setTextContent(arr[0]);
+        value.setTextContent(arr[1]);
+
+        item.appendChild(label);
+        item.appendChild(value);
+
+        return item;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/FlowVelocityState.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,297 @@
+package org.dive4elements.river.artifacts.states;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifactdatabase.state.FacetActivity;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.access.FlowVelocityAccess;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.DataFacet;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.FlowVelocityCalculation;
+import org.dive4elements.river.artifacts.model.FlowVelocityData;
+import org.dive4elements.river.artifacts.model.FlowVelocityFacet;
+import org.dive4elements.river.artifacts.model.FlowVelocityFilterFacet;
+import org.dive4elements.river.artifacts.resources.Resources;
+
+
+/* State in which flow velocities can/will be calculated. */
+public class FlowVelocityState extends DefaultState implements FacetTypes {
+
+    private static Logger logger = Logger.getLogger(FlowVelocityState.class);
+
+    public static final String I18N_MAINCHANNEL_FACET =
+        "facet.flow_velocity.mainchannel";
+
+    public static final String I18N_TOTALCHANNEL_FACET =
+        "facet.flow_velocity.totalchannel";
+
+    public static final String I18N_TAU_FACET =
+        "facet.flow_velocity.tauchannel";
+
+    public static final String I18N_MAINCHANNEL_FACET_RAW =
+        "facet.flow_velocity.mainchannel.raw";
+
+    public static final String I18N_TOTALCHANNEL_FACET_RAW =
+        "facet.flow_velocity.totalchannel.raw";
+
+    public static final String I18N_TAU_FACET_RAW =
+        "facet.flow_velocity.tauchannel.raw";
+
+    public static final String I18N_DISCHARGE_FACET =
+        "facet.flow_velocity.discharge";
+
+
+    @Override
+    public Object computeAdvance(
+        FLYSArtifact artifact,
+        String       hash,
+        CallContext  context,
+        List<Facet>  facets,
+        Object       old
+    ) {
+        logger.debug("FlowVelocityState.computeAdvance");
+
+        List<Facet> newFacets = new ArrayList<Facet>();
+
+        FlowVelocityAccess access = new FlowVelocityAccess(artifact, context);
+
+        CalculationResult res = old instanceof CalculationResult
+            ? (CalculationResult) old
+            : new FlowVelocityCalculation().calculate(access);
+
+        if (facets == null || res == null) {
+            return res;
+        }
+
+        FlowVelocityData[] data = (FlowVelocityData[]) res.getData();
+
+        logger.debug("Calculated " + data.length + " FlowVelocityData objects");
+
+        String id  = getID();
+        int    idx = 0;
+
+        for (FlowVelocityData d: data) {
+            if (d.getType().equals("main")) {
+                newFacets.add(new FlowVelocityFacet(
+                    idx,
+                    FLOW_VELOCITY_MAINCHANNEL,
+                    buildFacetName(artifact, context, d, I18N_MAINCHANNEL_FACET_RAW),
+                    ComputeType.ADVANCE,
+                    id,
+                    hash
+                ));
+
+                newFacets.add(new FlowVelocityFacet(
+                    idx,
+                    FLOW_VELOCITY_TAU,
+                    buildFacetName(artifact, context, d, I18N_TAU_FACET_RAW),
+                    ComputeType.ADVANCE,
+                    id,
+                    hash
+                ));
+                newFacets.add(new FlowVelocityFilterFacet(
+                    idx,
+                    FLOW_VELOCITY_MAINCHANNEL_FILTERED,
+                    buildMainChannelName(artifact, context, d),
+                    ComputeType.ADVANCE,
+                    id,
+                    hash
+                ));
+                newFacets.add(new FlowVelocityFilterFacet(
+                    idx,
+                    FLOW_VELOCITY_TAU_FILTERED,
+                    buildTauName(artifact, context, d),
+                    ComputeType.ADVANCE,
+                    id,
+                    hash
+                ));
+            }
+            else if (d.getType().equals("total")) {
+                newFacets.add(new FlowVelocityFacet(
+                    idx,
+                    FLOW_VELOCITY_TOTALCHANNEL,
+                    buildFacetName(artifact, context, d, I18N_TOTALCHANNEL_FACET_RAW),
+                    ComputeType.ADVANCE,
+                    id,
+                    hash
+                ));
+                newFacets.add(new FlowVelocityFilterFacet(
+                    idx,
+                    FLOW_VELOCITY_TOTALCHANNEL_FILTERED,
+                    buildTotalChannelName(artifact, context, d),
+                    ComputeType.ADVANCE,
+                    id,
+                    hash
+                ));
+
+            }
+            else if(d.getType().equals("main_total")) {
+                 newFacets.add(new FlowVelocityFacet(
+                    idx,
+                    FLOW_VELOCITY_MAINCHANNEL,
+                    buildFacetName(artifact, context, d, I18N_MAINCHANNEL_FACET_RAW),
+                    ComputeType.ADVANCE,
+                    id,
+                    hash
+                ));
+                newFacets.add(new FlowVelocityFacet(
+                    idx,
+                    FLOW_VELOCITY_TAU,
+                    buildFacetName(artifact, context, d, I18N_TAU_FACET_RAW),
+                    ComputeType.ADVANCE,
+                    id,
+                    hash
+                ));
+                newFacets.add(new FlowVelocityFacet(
+                    idx,
+                    FLOW_VELOCITY_TOTALCHANNEL,
+                    buildFacetName(artifact, context, d, I18N_TOTALCHANNEL_FACET_RAW),
+                    ComputeType.ADVANCE,
+                    id,
+                    hash
+                ));
+                newFacets.add(new FlowVelocityFilterFacet(
+                    idx,
+                    FLOW_VELOCITY_MAINCHANNEL_FILTERED,
+                    buildMainChannelName(artifact, context, d),
+                    ComputeType.ADVANCE,
+                    id,
+                    hash
+                ));
+                newFacets.add(new FlowVelocityFilterFacet(
+                    idx,
+                    FLOW_VELOCITY_TAU_FILTERED,
+                    buildTauName(artifact, context, d),
+                    ComputeType.ADVANCE,
+                    id,
+                    hash
+                ));
+                newFacets.add(new FlowVelocityFilterFacet(
+                    idx,
+                    FLOW_VELOCITY_TOTALCHANNEL_FILTERED,
+                    buildTotalChannelName(artifact, context, d),
+                    ComputeType.ADVANCE,
+                    id,
+                    hash
+                ));
+            }
+
+            newFacets.add(new FlowVelocityFacet(
+                idx,
+                FLOW_VELOCITY_DISCHARGE,
+                buildDischargeName(artifact, context, d),
+                ComputeType.ADVANCE,
+                id,
+                hash
+            ));
+
+            idx++;
+        }
+
+        Facet csv = new DataFacet(
+            CSV, "CSV data", ComputeType.ADVANCE, hash, id);
+
+        // TODO ADD PDF FACET
+
+        newFacets.add(csv);
+
+        logger.debug("Created " + newFacets.size() + " new Facets.");
+
+        facets.addAll(newFacets);
+
+        return res;
+    }
+
+
+    protected String buildFacetName(
+        FLYSArtifact     flys,
+        CallContext      cc,
+        FlowVelocityData data,
+        String           resourceId
+    ) {
+        Object[] args = new Object[] {
+            data.getZone()
+        };
+
+        return Resources.getMsg(
+            cc.getMeta(),
+            resourceId,
+            resourceId,
+            args);
+    }
+
+
+    protected String buildMainChannelName(
+        FLYSArtifact     flys,
+        CallContext      cc,
+        FlowVelocityData data
+    ) {
+        return buildFacetName(flys, cc, data, I18N_MAINCHANNEL_FACET);
+    }
+
+
+    protected String buildTotalChannelName(
+        FLYSArtifact     flys,
+        CallContext      cc,
+        FlowVelocityData data
+    ) {
+        return buildFacetName(flys, cc, data, I18N_TOTALCHANNEL_FACET);
+    }
+
+
+    protected String buildDischargeName(
+        FLYSArtifact     flys,
+        CallContext      cc,
+        FlowVelocityData data
+    ) {
+        return buildFacetName(flys, cc, data, I18N_DISCHARGE_FACET);
+    }
+
+    protected String buildTauName(
+        FLYSArtifact     flys,
+        CallContext      cc,
+        FlowVelocityData data
+    ) {
+        return buildFacetName(flys, cc, data, I18N_TAU_FACET);
+    }
+
+    static {
+        // Active/deactivate facets.
+        FacetActivity.Registry.getInstance().register(
+            "minfo",
+            new FacetActivity() {
+                @Override
+                public Boolean isInitialActive(
+                    Artifact artifact,
+                    Facet    facet,
+                    String   output
+                ) {
+                    String name = facet.getName();
+                    if (name.equals(FLOW_VELOCITY_MAINCHANNEL_FILTERED) ||
+                        name.equals(FLOW_VELOCITY_TAU_FILTERED) ||
+                        name.equals(FLOW_VELOCITY_DISCHARGE) ||
+                        name.equals(FLOW_VELOCITY_TOTALCHANNEL_FILTERED)) {
+                        return Boolean.TRUE;
+                    }
+                    else if (name.equals(FLOW_VELOCITY_MAINCHANNEL) ||
+                        name.equals(FLOW_VELOCITY_TAU) ||
+                        name.equals(FLOW_VELOCITY_TOTALCHANNEL)) {
+                        return Boolean.FALSE;
+                    }
+                    else {
+                        return null;
+                    }
+                }
+            });
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/GaugeDischargeState.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,161 @@
+package org.dive4elements.river.artifacts.states;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+
+import org.dive4elements.river.artifacts.ChartArtifact;
+import org.dive4elements.river.artifacts.GaugeDischargeArtifact;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+import org.dive4elements.river.artifacts.model.GaugeDischargeFacet;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.WQKms;
+import org.dive4elements.river.artifacts.model.ReportFacet;
+import org.dive4elements.river.artifacts.model.EmptyFacet;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+
+import org.dive4elements.river.artifacts.resources.Resources;
+
+import org.dive4elements.river.model.Gauge;
+
+
+/**
+ * The only state for an GaugeDischargeState (River and km known).
+ */
+public class GaugeDischargeState
+extends      DefaultState
+implements   FacetTypes
+{
+    /** Developer-centric description of facet. */
+    public static final String I18N_DESCRIPTION = "facet.discharge.curve";
+
+    /** The logger that is used in this state. */
+    private static final Logger logger =
+        Logger.getLogger(GaugeDischargeState.class);
+
+
+    /**
+     * Create i18ned name for gaugedischargeFacet.
+     * @param artifact The artifact which has information about the gauge.
+     * @param meta used for i18n.
+     * @return localized name for gaugedischargefacet.
+     */
+    protected String createFacetName(GaugeDischargeArtifact artifact,
+        CallMeta meta) {
+
+        Gauge gauge = artifact.getGauge();
+        Object[] args = new Object[] {
+            gauge.getName(),
+            gauge.getStation()
+        };
+
+        String name = Resources.getMsg(
+            meta,
+            "chart.computed.discharge.curve.gauge",
+            "",
+            args);
+
+        return name;
+    }
+
+
+    /**
+     * Add an GaugeDischargeFacet to list of Facets.
+     *
+     * @param artifact Ignored.
+     * @param hash Ignored.
+     * @param context Ignored.
+     * @param meta CallMeta to be used for internationalization.
+     * @param facets List to add AnnotationFacet to.
+     *
+     * @return null.
+     */
+    @Override
+    public Object computeInit(
+        FLYSArtifact artifact,
+        String       hash,
+        Object       context,
+        CallMeta     meta,
+        List<Facet>  facets
+    ) {
+        logger.debug("GaugeDischargeState.computeInit()");
+
+        GaugeDischargeFacet facet = new GaugeDischargeFacet(
+            0,
+            DISCHARGE_CURVE,
+            createFacetName((GaugeDischargeArtifact) artifact, meta));
+
+        facets.add(facet);
+
+        return null;
+    }
+
+
+    /**
+     * 'Calculate' Discharge at Gauge.
+     */
+    @Override
+    public Object computeAdvance(
+        FLYSArtifact artifact,
+        String       hash,
+        CallContext  context,
+        List<Facet>  facets,
+        Object       old
+    ) {
+        if (artifact instanceof GaugeDischargeArtifact) {
+            logger.debug("GaugeDischargeState.computeAdvance()");
+            GaugeDischargeArtifact dischargeArtifact = (GaugeDischargeArtifact) artifact;
+
+            CalculationResult res;
+
+
+            if (old instanceof CalculationResult) {
+                res = (CalculationResult) old;
+            }
+            else {
+                res = dischargeArtifact.getDischargeCurveData();
+            }
+
+            WQKms[] wqkms = (WQKms[]) res.getData();
+
+            if (wqkms != null && facets != null) {
+                logger.debug("GaugeDischargeState.computeAdvance(): create facets");
+
+                GaugeDischargeFacet facet = new GaugeDischargeFacet(
+                    0,
+                    DISCHARGE_CURVE,
+                    createFacetName(dischargeArtifact, context.getMeta()));
+
+                facets.add(facet);
+
+                //facets.add(new DataFacet(CSV, "CSV data"));
+                //facets.add(new DataFacet(PDF, "PDF data"));
+
+                if (res.getReport().hasProblems()) {
+                    facets.add(new ReportFacet());
+                }
+            }
+            else {
+                if (wqkms == null)
+                    logger.debug("GaugeDischargeState.computeAdvance(): wqkms 0");
+                else
+                    logger.debug("GaugeDischargeState.computeAdvance(): facets 0");
+            }
+
+            return res;
+        }
+        else if (artifact instanceof ChartArtifact) {
+            ChartArtifact chart = (ChartArtifact)artifact;
+            facets.add(new EmptyFacet());
+            return null;
+        }
+        return null;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/GaugeTimerangeState.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,94 @@
+package org.dive4elements.river.artifacts.states;
+
+import java.util.Calendar;
+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.artifacts.FLYSArtifact;
+import org.dive4elements.river.backend.SessionHolder;
+import org.dive4elements.river.model.Gauge;
+import org.dive4elements.river.utils.FLYSUtils;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class GaugeTimerangeState extends IntRangeState {
+
+    /** Private logger. */
+    private static final Logger logger =
+            Logger.getLogger(GaugeTimerangeState.class);
+
+
+    /** Get 'min' and 'max'times of gauge time intervals. */
+    protected long[] getLowerUpper(FLYSArtifact flys) {
+        Gauge gauge = FLYSUtils.getReferenceGauge(flys);
+
+        if (gauge == null) {
+            logger.warn("No reference gauge specified!");
+            return new long[] { 0, 0 };
+        }
+
+        Session session = SessionHolder.HOLDER.get();
+
+        SQLQuery query = session.createSQLQuery(
+                "SELECT min(start_time) as min, max(stop_time) as max " +
+                        "FROM time_intervals WHERE id in " +
+                        "(SELECT time_interval_id FROM discharge_tables " +
+                "WHERE gauge_id =:gid)");
+
+        query.addScalar("min", StandardBasicTypes.CALENDAR);
+        query.addScalar("max", StandardBasicTypes.CALENDAR);
+
+        query.setInteger("gid", gauge.getId());
+
+        List<?> results = query.list();
+
+        if (results != null) {
+            Object[] res = (Object[]) results.get(0);
+
+            Calendar lo = (Calendar) res[0];
+            Calendar up = (Calendar) res[1];
+
+            if (lo != null && up != null) {
+                return new long[] { lo.getTimeInMillis(), up.getTimeInMillis() };
+            }
+        }
+
+        logger.warn("Could not determine time range for gauge: " + gauge);
+
+        return null;
+    }
+
+
+    @Override
+    protected Object getLower(FLYSArtifact flys) {
+        long[] lowerUpper = getLowerUpper(flys);
+
+        return lowerUpper != null ? lowerUpper[0] : 0;
+    }
+
+
+    @Override
+    protected Object getUpper(FLYSArtifact flys) {
+        long[] lowerUpper = getLowerUpper(flys);
+
+        return lowerUpper != null ? lowerUpper[1] : 0;
+    }
+
+
+    @Override
+    protected String getUIProvider() {
+        return "gaugetimerange";
+    }
+
+    @Override
+    protected String getType() {
+        return "longrange";
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/HWSBarriersState.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,359 @@
+package org.dive4elements.river.artifacts.states;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.apache.velocity.Template;
+
+import org.geotools.data.shapefile.ShapefileDataStore;
+
+import org.geotools.feature.FeatureCollection;
+import org.geotools.feature.FeatureCollections;
+
+import org.opengis.feature.simple.SimpleFeatureType;
+
+import org.opengis.feature.type.GeometryDescriptor;
+
+import org.w3c.dom.Element;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.artifacts.common.utils.FileTools;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+import org.dive4elements.river.artifacts.access.MapAccess;
+
+import org.dive4elements.river.artifacts.model.LayerInfo;
+
+import org.dive4elements.river.artifacts.model.map.HWS;
+import org.dive4elements.river.artifacts.model.map.HWSContainer;
+import org.dive4elements.river.artifacts.model.map.HWSFactory;
+
+import org.dive4elements.river.utils.ArtifactMapfileGenerator;
+import org.dive4elements.river.utils.FLYSUtils;
+import org.dive4elements.river.utils.GeometryUtils;
+import org.dive4elements.river.utils.MapfileGenerator;
+
+public class HWSBarriersState
+extends DefaultState
+{
+
+    /** The logger that is used in this class.*/
+    private static Logger logger = Logger.getLogger(HWSBarriersState.class);
+    private static final String HWS_SHAPEFILE_LINES = "hws-lines.shp";
+    private static final String HWS_SHAPEFILE_POINTS = "hws-points.shp";
+    private static final String USER_RGD_SHAPE     = "user-rgd.shp";
+    private static final String USER_RGD_ZIP       = "user-rgd.zip";
+    private static final String USER_RGD_FILENAME  = "user-rgd";
+    @Override
+    protected String getUIProvider() {
+        return "map_digitize";
+    }
+
+
+    @Override
+    protected Element createStaticData(
+        FLYSArtifact   flys,
+        ElementCreator creator,
+        CallContext    cc,
+        String         name,
+        String         value,
+        String         type
+    ) {
+        Element dataElement = creator.create("data");
+        creator.addAttr(dataElement, "name", name, true);
+        creator.addAttr(dataElement, "type", type, true);
+
+        Element itemElement = creator.create("item");
+        creator.addAttr(itemElement, "value", value, true);
+
+        creator.addAttr(itemElement, "label", "", true);
+        dataElement.appendChild(itemElement);
+
+        return dataElement;
+    }
+
+
+    @Override
+    public Object computeAdvance(
+        FLYSArtifact artifact,
+        String hash,
+        CallContext context,
+        List<Facet> facets,
+        Object old
+    ) {
+        File artifactDir = getDirectory(artifact);
+
+        if (artifactDir == null) {
+            logger.error("Could not create directory for HWS shapefile!");
+            return null;
+        }
+
+        MapAccess access = new MapAccess(artifact, context);
+        String river = access.getRiver();
+        HWSContainer hwsLines = HWSFactory.getHWSLines(river);
+        HWSContainer hwsPoints = HWSFactory.getHWSPoints(river);
+        List<String> selected = access.getHWS();
+
+        List<HWS> selectedLines = hwsLines.getHws(selected);
+        List<HWS> selectedPoints = hwsPoints.getHws(selected);
+
+        FeatureCollection collectionLines = FeatureCollections.newCollection();
+        SimpleFeatureType lineType = null;
+        for (HWS h : selectedLines) {
+            lineType = h.getFeatureType();
+            collectionLines.add(h.getFeature());
+        }
+        boolean successLines = false;
+        if (lineType != null && collectionLines.size() > 0) {
+            File shapeLines = new File(artifactDir, HWS_SHAPEFILE_LINES);
+            successLines = GeometryUtils.writeShapefile(
+                shapeLines, lineType, collectionLines);
+        }
+
+        FeatureCollection collectionPoints = FeatureCollections.newCollection();
+        SimpleFeatureType pointType = null;
+        for (HWS h : selectedPoints) {
+            pointType = h.getFeatureType();
+            collectionPoints.add(h.getFeature());
+        }
+        boolean successPoints = false;
+        if (pointType != null && collectionPoints.size() > 0) {
+            File shapePoints = new File(artifactDir, HWS_SHAPEFILE_POINTS);
+            successPoints =GeometryUtils.writeShapefile(
+                shapePoints, pointType, collectionPoints);
+        }
+
+        if (successLines) {
+            createMapfile(
+                artifact,
+                artifactDir,
+                MapfileGenerator.MS_LAYER_PREFIX + "hws-lines",
+                HWS_SHAPEFILE_LINES,
+                "LINE",
+                "31467",
+                "hws");
+        }
+        if (successPoints) {
+            createMapfile(
+                artifact,
+                artifactDir,
+                MapfileGenerator.MS_LAYER_PREFIX + "hws-points",
+                HWS_SHAPEFILE_POINTS,
+                "POINT",
+                "31467",
+                "hws");
+        }
+
+        String userRgd = artifact.getDataAsString("uesk.user-rgd");
+        if (!userRgd.equals("none")) {
+            if (extractUserShp(artifactDir)) {
+                try {
+                    ShapefileDataStore store = new ShapefileDataStore(
+                    new File(artifactDir.getCanonicalPath() +
+                        "/" + USER_RGD_SHAPE)
+                            .toURI().toURL());
+                    GeometryDescriptor desc =
+                        store.getSchema().getGeometryDescriptor();
+                    String type = desc.getType().getName().toString();
+                    String proj =
+                        desc.getCoordinateReferenceSystem().
+                            getCoordinateSystem().toString();
+                    int pos1 = proj.indexOf("EPSG\",\"");
+                    int pos2 = proj.indexOf("\"]]");
+                    String epsg = "";
+                    if (pos1 >= 0 && pos2 >= 0) {
+                        epsg =
+                            proj.substring(proj.indexOf("EPSG\",\"") + 7,
+                                proj.indexOf("\"]]"));
+                    }
+                    else {
+                        logger.warn("Could not read EPSG code from shapefile.");
+                        return null;
+                    }
+                    if (type.contains("Line")) {
+                        type = "LINE";
+                    }
+                    else if (type.contains("Poly")) {
+                        type = "POLYGON";
+                    }
+                    else {
+                        type = "POINT";
+                    }
+                    createMapfile(
+                        artifact,
+                        artifactDir,
+                        MapfileGenerator.MS_LAYER_PREFIX + USER_RGD_FILENAME,
+                        USER_RGD_SHAPE,
+                        type,
+                        epsg,
+                        "user-rgd");
+                }
+                catch (IOException e) {
+                    logger.warn("No mapfile for user-rgd created!");
+                }
+            }
+        }
+        return null;
+    }
+
+    private boolean extractUserShp(File dir) {
+        // TODO Auto-generated method stub
+        File archive = new File(dir, USER_RGD_ZIP);
+        boolean exists = archive.exists();
+        logger.debug("Zip file exists: " + exists);
+        if (exists) {
+            try {
+                File tmpDir = new File(dir, "usr_tmp");
+                FileTools.extractArchive(archive, tmpDir);
+                moveFiles(tmpDir, dir);
+                return true;
+            }
+            catch (IOException ioe) {
+                logger.warn("Zip archive " + dir + "/"
+                    + USER_RGD_ZIP + " could not be extracted.");
+                return false;
+            }
+        }
+        return false;
+    }
+
+    private void moveFiles(File source, final File target)
+    throws IOException
+    {
+        if (!source.exists()) {
+            return;
+        }
+        if (!target.exists()) {
+            target.mkdir();
+        }
+        FileTools.walkTree(source, new FileTools.FileVisitor() {
+            @Override
+            public boolean visit(File file) {
+                if (!file.isDirectory()) {
+                    String name = file.getName();
+                    String suffix = "";
+                    int pos = name.lastIndexOf('.');
+                    if (pos > 0 && pos < name.length() - 1) {
+                        suffix = name.substring(pos + 1);
+                    }
+                    else {
+                        return true;
+                    }
+                    try {
+                        FileTools.copyFile(file, new File(target, USER_RGD_FILENAME + "." + suffix));
+                    }
+                    catch (IOException ioe) {
+                        logger.warn ("Error while copying file " + file.getName());
+                        return true;
+                    }
+                }
+                return true;
+            }
+        });
+
+        FileTools.deleteRecursive(source);
+    }
+
+    private void createMapfile(
+        FLYSArtifact artifact,
+        File artifactDir,
+        String name,
+        String hwsShapefile,
+        String type,
+        String srid,
+        String group
+    ) {
+        LayerInfo info = new LayerInfo();
+        info.setName(name + artifact.identifier());
+        info.setType(type);
+        info.setDirectory(artifact.identifier());
+        info.setTitle(name);
+        info.setData(hwsShapefile);
+        info.setSrid(srid);
+        info.setGroupTitle(group);
+        MapfileGenerator generator = new ArtifactMapfileGenerator();
+        Template tpl = generator.getTemplateByName(MapfileGenerator.SHP_LAYER_TEMPLATE);
+        try {
+            File layer = new File(artifactDir.getCanonicalPath() + "/" + name);
+            generator.writeLayer(info, layer, tpl);
+            List<String> layers = new ArrayList<String>();
+            layers.add(layer.getAbsolutePath());
+            generator.generate();
+        }
+        catch(FileNotFoundException fnfe) {
+            logger.warn("Could not find mapfile for hws layer");
+        }
+        catch (Exception ioe) {
+            logger.warn("Could not create mapfile for hws layer");
+            logger.warn(Arrays.toString(ioe.getStackTrace()));
+        }
+    }
+
+
+    @Override
+    public void endOfLife(Artifact artifact, Object callContext) {
+        super.endOfLife(artifact, callContext);
+        logger.info("ScenarioSelect.endOfLife: " + artifact.identifier());
+
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+        removeDirectory(flys);
+    }
+
+
+    /**
+     * Removes the directory and all its content where the required data and the
+     * results of WSPLGEN are stored. Should be called in endOfLife().
+     */
+    // FIXME: I've seen this code somewhere else...
+    protected void removeDirectory(FLYSArtifact artifact) {
+        String shapePath = FLYSUtils.getXPathString(
+            FLYSUtils.XPATH_FLOODMAP_SHAPEFILE_DIR);
+
+        File artifactDir = new File(shapePath, artifact.identifier());
+
+        if (artifactDir.exists()) {
+            logger.debug("Delete directory: " + artifactDir.getAbsolutePath());
+            boolean success = FileTools.deleteRecursive(artifactDir);
+            if (!success) {
+                logger.warn("could not remove dir '" + artifactDir + "'");
+            }
+        }
+        else {
+            logger.debug("There is no directory to remove.");
+        }
+    }
+
+    /**
+     * Returns (and creates if not existing) the directory for storing WSPLEN
+     * data for the owner artifact.
+     *
+     * @param artifact The owner Artifact.
+     *
+     * @return the directory for WSPLEN data.
+     */
+    protected File getDirectory(FLYSArtifact artifact) {
+        String shapePath = FLYSUtils.getXPathString(
+            FLYSUtils.XPATH_FLOODMAP_SHAPEFILE_DIR);
+
+        File artifactDir = FileTools.getDirectory(
+            shapePath, artifact.identifier());
+
+        return artifactDir;
+    }
+
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/HWSDatacageState.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,90 @@
+package org.dive4elements.river.artifacts.states;
+
+import org.apache.log4j.Logger;
+import org.w3c.dom.Element;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+
+public class HWSDatacageState
+extends DefaultState
+{
+
+    private static final Logger logger = Logger.getLogger(HWSDatacageState.class);
+
+    @Override
+    protected String getUIProvider() {
+        return "hws_datacage_panel";
+    }
+
+
+    @Override
+    protected Element createStaticData(
+        FLYSArtifact   flys,
+        ElementCreator creator,
+        CallContext    cc,
+        String         name,
+        String         value,
+        String         type
+    ) {
+        Element dataElement = creator.create("data");
+        creator.addAttr(dataElement, "name", name, true);
+        creator.addAttr(dataElement, "type", type, true);
+
+        Element itemElement = creator.create("item");
+        creator.addAttr(itemElement, "value", value, true);
+
+        creator.addAttr(itemElement, "label", getLabel(cc, value), true);
+        dataElement.appendChild(itemElement);
+
+        return dataElement;
+    }
+
+
+    public static String getLabel(CallContext cc, String value) {
+        logger.debug("Create label for value: " + value);
+
+        return value;
+    }
+
+
+    @Override
+    public boolean validate(Artifact artifact)
+    throws IllegalArgumentException
+    {
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+        String hws = flys.getDataAsString("uesk.hws");
+        logger.debug("hws: " + hws);
+        return true;
+    }
+
+
+    /**
+     * Returns the DGM specified in the parameters of <i>flys</i>.
+     *
+     * @param flys The FLYSArtifact that knows the ID of a DGM.
+     *
+     * @throws IllegalArgumentException If the FLYSArtifact doesn't know the ID
+     * of a DGM.
+     *
+     * @return the DGM specified by FLYSArtifact's parameters.
+     */
+    public static String getHWS(FLYSArtifact flys)
+    throws IllegalArgumentException
+    {
+        String hws= flys.getDataAsString("uesk.hws");
+        if (hws == null) {
+            return null;
+        }
+
+        logger.debug("Found selected hws: '" + hws + "'");
+
+        return hws;
+    }
+
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/HistoricalDischargeComputeState.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,207 @@
+package org.dive4elements.river.artifacts.states;
+
+import java.text.NumberFormat;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.w3c.dom.Element;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.WINFOArtifact;
+import org.dive4elements.river.artifacts.access.HistoricalDischargeAccess;
+import org.dive4elements.river.artifacts.access.HistoricalDischargeAccess.EvaluationMode;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.DataFacet;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.HistoricalDischargeCurveFacet;
+import org.dive4elements.river.artifacts.model.HistoricalDischargeData;
+import org.dive4elements.river.artifacts.model.HistoricalDischargeDifferenceFacet;
+import org.dive4elements.river.artifacts.model.HistoricalDischargeFacet;
+import org.dive4elements.river.artifacts.model.HistoricalDischargeWQFacet;
+import org.dive4elements.river.artifacts.model.HistoricalWQKms;
+import org.dive4elements.river.artifacts.model.HistoricalWQTimerange;
+import org.dive4elements.river.artifacts.model.ReportFacet;
+import org.dive4elements.river.artifacts.model.WQKms;
+import org.dive4elements.river.artifacts.model.WQTimerange;
+import org.dive4elements.river.artifacts.resources.Resources;
+import org.dive4elements.river.model.TimeInterval;
+
+
+/**
+ * State to calculate historical discharge curves.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class HistoricalDischargeComputeState
+extends    DefaultState
+implements FacetTypes {
+
+    private static final Logger logger = Logger
+        .getLogger(HistoricalDischargeComputeState.class);
+
+    public static final String DEFAULT_UNIT = "cm";
+    public static final String I18N_WQ_CURVE_FACET_RANGE = "historical_discharge.wq.curve_range";
+    public static final String I18N_WQ_CURVE_FACET_SINCE = "historical_discharge.wq.curve_since";
+
+    @Override
+    protected void appendItems(Artifact artifact, ElementCreator creator,
+        String name, CallContext context, Element select) {
+        // TODO IMPLEMENT ME
+    }
+
+    @Override
+    public Object computeAdvance(FLYSArtifact artifact, String hash,
+        CallContext context, List<Facet> facets, Object old) {
+        logger.debug("HistoricalDischargeComputeState.computeAdvance");
+
+        WINFOArtifact winfo = (WINFOArtifact) artifact;
+
+        CalculationResult res = old instanceof CalculationResult ? (CalculationResult) old
+            : winfo.getHistoricalDischargeData();
+
+        if (facets == null) {
+            return res;
+        }
+
+        if (res.getReport().hasProblems()) {
+            facets.add(new ReportFacet(ComputeType.ADVANCE, hash, id));
+        }
+
+        HistoricalDischargeData data = (HistoricalDischargeData) res.getData();
+        HistoricalDischargeAccess access = new HistoricalDischargeAccess(
+            artifact);
+
+        WQTimerange[] wqts = (WQTimerange[]) data.getWQTimeranges();
+        if (wqts != null && wqts.length > 0) {
+            facets.add(new DataFacet(CSV, "CSV data", ComputeType.ADVANCE,
+                hash, id));
+
+            facets.add(new DataFacet(PDF, "PDF data", ComputeType.ADVANCE,
+                hash, id));
+
+            prepareFacets(facets, wqts, access);
+        }
+
+        WQKms[] wqs = (WQKms[]) data.getWQs();
+        if (wqs != null && wqs.length > 0) {
+            prepareWQFacets(context, facets, wqs, access, hash);
+        }
+
+        return res;
+    }
+
+    protected void prepareFacets(List<Facet> facets, WQTimerange[] wqts,
+        HistoricalDischargeAccess access) {
+        int i = 0;
+
+        for (WQTimerange wqt : wqts) {
+            logger.debug("Prepare facet for: " + wqt.getName());
+
+            EvaluationMode evalMode = access.getEvaluationMode();
+            if (evalMode == EvaluationMode.W) {
+                facets.add(new HistoricalDischargeFacet(i,
+                    HISTORICAL_DISCHARGE_Q, createFacetTitle(wqt)));
+
+                if (wqt instanceof HistoricalWQTimerange) {
+                    logger
+                        .debug("Create another facet for historical differences.");
+
+                    // TODO CREATE BETTER TITLE FOR FACETS (issue1180)
+                    facets.add(new HistoricalDischargeDifferenceFacet(i,
+                        HISTORICAL_DISCHARGE_Q_DIFF, "DIFF: " + wqt.getName()));
+                }
+            }
+            else {
+                facets.add(new HistoricalDischargeFacet(i,
+                    HISTORICAL_DISCHARGE_W, createFacetTitle(wqt)));
+
+                if (wqt instanceof HistoricalWQTimerange) {
+                    logger
+                        .debug("Create another facet for historical differences.");
+
+                    // TODO CREATE BETTER TITLE FOR FACETS
+                    facets.add(new HistoricalDischargeDifferenceFacet(i,
+                        HISTORICAL_DISCHARGE_W_DIFF, "DIFF: " + wqt.getName()));
+                }
+            }
+
+            i++;
+        }
+    }
+
+    protected void prepareWQFacets(CallContext cc, List<Facet> facets,
+        WQKms[] wqs, HistoricalDischargeAccess access, String hash) {
+        double[] ws = access.getWs();
+        double[] qs = access.getQs();
+
+        NumberFormat format = NumberFormat.getInstance(
+                        Resources.getLocale(cc.getMeta()));
+        for (int k = 0; k < ws.length; k++) {
+            facets.add(new HistoricalDischargeWQFacet(k,
+                HISTORICAL_DISCHARGE_WQ_W, "W=" + format.format(ws[k]), ComputeType.ADVANCE,
+                hash, getID(), ws[k]));
+        }
+
+        for (int k = 0; k < qs.length; k++) {
+            facets.add(new HistoricalDischargeWQFacet(k,
+                HISTORICAL_DISCHARGE_WQ_Q, "Q=" + format.format(qs[k]), ComputeType.ADVANCE,
+                hash, getID(), qs[k]));
+        }
+
+        int idx = 0;
+        for (WQKms wqkms : wqs) {
+            String title = null;
+            if (wqkms instanceof HistoricalWQKms) {
+                title = createDischargeCurveTitle(cc, (HistoricalWQKms) wqkms);
+            }
+            else {
+                title = wqkms.getName();
+            }
+
+            facets.add(new HistoricalDischargeCurveFacet(idx,
+                HISTORICAL_DISCHARGE_WQ_CURVE, title, ComputeType.ADVANCE,
+                getID(), hash));
+            idx++;
+        }
+    }
+
+    /** Create title for a Discharge-curve, including date. */
+    protected String createDischargeCurveTitle(CallContext cc,
+        HistoricalWQKms wqkms) {
+        TimeInterval timeInterval = wqkms.getTimeInterval();
+
+        if (timeInterval.getStopTime() != null) {
+            return Resources.format(cc.getMeta(), I18N_WQ_CURVE_FACET_RANGE,
+                I18N_WQ_CURVE_FACET_RANGE, timeInterval.getStartTime(),
+                timeInterval.getStopTime());
+        }
+        else {
+            return Resources.format(cc.getMeta(), I18N_WQ_CURVE_FACET_SINCE,
+                I18N_WQ_CURVE_FACET_SINCE, timeInterval.getStartTime());
+        }
+    }
+
+
+    /** Create string for facets name/description. */
+    protected String createFacetTitle(WQTimerange wqt) {
+        String name = wqt.getName();
+
+        return name != null && name.indexOf("W") >= 0 ? createFacetTitleW(wqt)
+            : createFacetTitleQ(wqt);
+    }
+
+    protected String createFacetTitleW(WQTimerange wqt) {
+        String name = wqt.getName();
+        return name + " " + DEFAULT_UNIT;
+    }
+
+    protected String createFacetTitleQ(WQTimerange wqt) {
+        return wqt.getName();
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/HistoricalDischargeState.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,153 @@
+package org.dive4elements.river.artifacts.states;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Element;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.ArtifactNamespaceContext;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator;
+
+import org.dive4elements.river.artifacts.resources.Resources;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class HistoricalDischargeState extends DefaultState {
+
+    private static final Logger logger =
+        Logger.getLogger(HistoricalDischargeState.class);
+
+    public static final String I18N_MODE_W = "historical.mode.w";
+    public static final String I18N_MODE_Q = "historical.mode.q";
+
+    public static final String DATA_MODE   = "historical_mode";
+    public static final String DATA_VALUES = "historical_values";
+    public static final int    DATA_MODE_W = 0;
+    public static final int    DATA_MODE_Q = 1;
+
+
+    @Override
+    protected String getUIProvider() {
+        return "wq_simple_array";
+    }
+
+    @Override
+    protected void appendItems(
+        Artifact       artifact,
+        ElementCreator creator,
+        String         name,
+        CallContext    context,
+        Element        select
+    ) {
+        if (name != null && name.equals(DATA_VALUES)) {
+            select.setAttributeNS(
+                ArtifactNamespaceContext.NAMESPACE_URI,
+                "art:type",
+                "doublearray");
+        }
+        else if (name != null && name.equals(DATA_MODE)) {
+            select.setAttributeNS(
+                ArtifactNamespaceContext.NAMESPACE_URI,
+                "art:type",
+                "intoptions");
+        }
+
+        super.appendItems(artifact, creator, name, context, select);
+    }
+
+
+    @Override
+    protected Element[] createItems(
+        ElementCreator creator,
+        Artifact       artifact,
+        String         name,
+        CallContext    context
+    ) {
+        logger.debug("createItems()");
+
+        if (name != null && name.equals(DATA_MODE)) {
+            return createModeItem(creator, artifact, name, context);
+        }
+        else if (name != null && name.equals(DATA_VALUES)) {
+            return createValuesItem(creator, artifact, name, context);
+        }
+
+        logger.warn("Tried to create item for invalid data: " + name);
+
+        return new Element[0];
+    }
+
+
+    /** Get label for display in client, depending on chosen W or Q input. */
+    @Override
+    protected String getLabelFor(
+        CallContext cc,
+        String      name,
+        String      value,
+        String      type
+    ) {
+        CallMeta meta = cc.getMeta();
+
+        if (name.equals(DATA_MODE)) {
+            if (value.equals(String.valueOf(DATA_MODE_W))) {
+                return Resources.getMsg(meta, I18N_MODE_W, I18N_MODE_W);
+            }
+            else {
+                return Resources.getMsg(meta, I18N_MODE_Q, I18N_MODE_Q);
+            }
+        }
+        else {
+            return value;
+        }
+    }
+
+
+    protected Element[] createModeItem(
+        ElementCreator creator,
+        Artifact       artifact,
+        String         name,
+        CallContext    context
+    ) {
+        logger.debug("createModeItem()");
+
+        CallMeta meta = context.getMeta();
+
+        Element modeW = createItem(
+            creator,
+            new String[] {
+                Resources.getMsg(meta, I18N_MODE_W, I18N_MODE_W),
+                String.valueOf(DATA_MODE_W) } );
+
+        Element modeQ = createItem(
+            creator,
+            new String[] {
+                Resources.getMsg(meta, I18N_MODE_Q, I18N_MODE_Q),
+                String.valueOf(DATA_MODE_Q) } );
+
+        return new Element[] { modeW, modeQ };
+    }
+
+
+    protected Element[] createValuesItem(
+        ElementCreator creator,
+        Artifact       artifact,
+        String         name,
+        CallContext    context
+    ) {
+        logger.debug("createValuesItem()");
+
+        Element valuesW = createItem(
+            creator, new String[] { "ws", "" } );
+
+        Element valuesQ = createItem(
+            creator, new String[] { "qs", "" } );
+
+        return new Element[] { valuesW, valuesQ };
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/InputDoubleState.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,39 @@
+package org.dive4elements.river.artifacts.states;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+
+/**
+ * State to keep a double value and validate it against a range.
+ */
+public class InputDoubleState extends MinMaxState {
+
+    private static final Logger logger = Logger.getLogger(InputDoubleState.class);
+
+
+    @Override
+    protected String getUIProvider() {
+        return "location_panel";
+    }
+
+
+    @Override
+    protected Object getLower(FLYSArtifact flys) {
+        return 0;
+    }
+
+
+    @Override
+    protected Object getUpper(FLYSArtifact flys) {
+        return 0;
+    }
+
+
+    @Override
+    protected String getType() {
+        return "double";
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/IntRangeState.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,39 @@
+package org.dive4elements.river.artifacts.states;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class IntRangeState extends MinMaxState {
+
+    private static final Logger logger = Logger.getLogger(IntRangeState.class);
+
+
+    @Override
+    protected String getUIProvider() {
+        return "timerange";
+    }
+
+
+    @Override
+    protected Object getLower(FLYSArtifact flys) {
+        return 0;
+    }
+
+
+    @Override
+    protected Object getUpper(FLYSArtifact flys) {
+        return 0;
+    }
+
+
+    @Override
+    protected String getType() {
+        return "intrange";
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/LocationDistanceSelect.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,152 @@
+package org.dive4elements.river.artifacts.states;
+
+import org.apache.log4j.Logger;
+
+import gnu.trove.TDoubleArrayList;
+
+import org.dive4elements.artifacts.Artifact;
+
+import org.dive4elements.artifactdatabase.data.StateData;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.WINFOArtifact;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class LocationDistanceSelect
+extends      ComputationRangeState
+{
+
+    /** The logger used in this class. */
+    private static Logger logger = Logger.getLogger(LocationDistanceSelect.class);
+
+    /** The name of the 'mode' field. */
+    public static final String MODE = "ld_mode";
+
+    /** The name of the 'locations' field. */
+    public static final String LOCATIONS = "ld_locations";
+
+
+    /**
+     * The default constructor that initializes an empty State object.
+     */
+    public LocationDistanceSelect() {
+    }
+
+
+    @Override
+    protected String getUIProvider() {
+        return "location_distance_panel";
+    }
+
+
+    /** Validates the range (or location). */
+    @Override
+    public boolean validate(Artifact artifact)
+    throws IllegalArgumentException
+    {
+        logger.debug("LocationDistanceSelect.validate");
+
+        FLYSArtifact flys = (FLYSArtifact)artifact;
+        StateData mode = getData(flys, MODE);
+        String mValue = mode != null ? (String)mode.getValue() : null;
+        if (mValue != null) {
+            if (mValue.equals("distance")) {
+                return super.validate(flys);
+            }
+            else {
+                return validateLocations(flys);
+            }
+        }
+        return false;
+    }
+
+
+    /** Validate selected locations. */
+    protected boolean validateLocations(FLYSArtifact flys)
+    throws    IllegalArgumentException
+    {
+        StateData dValues = getData(flys, LOCATIONS);
+        String    values  = dValues != null ? (String)dValues.getValue() : null;
+
+        if (values == null || values.length() == 0) {
+            throw new IllegalArgumentException("error_empty_state");
+        }
+
+        double[] absMinMax = getMinMax(flys);
+        double[] relMinMax = getMinMaxFromString(values);
+
+        if (relMinMax[0] < absMinMax[0] || relMinMax[0] > absMinMax[1]) {
+            throw new IllegalArgumentException("error_feed_from_out_of_range");
+        }
+
+        if (relMinMax[1] > absMinMax[1] || relMinMax[1] < absMinMax[0]) {
+            throw new IllegalArgumentException("error_feed_to_out_of_range");
+        }
+
+        return true;
+    }
+
+
+    /**
+     * Extracts the min/max values from String <i>s</i>. An
+     * IllegalArgumentException is thrown if there is a value that throws a
+     * NumberFormatException.
+     *
+     * @param s String that contains whitespace separated double values.
+     *
+     * @return a 2dmin array [min,max].
+     */
+    public static double[] getMinMaxFromString(String s)
+    throws IllegalArgumentException
+    {
+        String[] values = s.split(" ");
+
+        double[] minmax = new double[] {
+            Double.MAX_VALUE,
+            -Double.MAX_VALUE };
+
+        for (String v: values) {
+            try {
+                double value = Double.valueOf(v);
+
+                minmax[0] = minmax[0] < value ? minmax[0] : value;
+                minmax[1] = minmax[1] > value ? minmax[1] : value;
+            }
+            catch (NumberFormatException nfe) {
+                throw new IllegalArgumentException(
+                    "error_invalid_double_value");
+            }
+        }
+
+        return minmax;
+    }
+
+
+    public static double[] getLocations(WINFOArtifact flys) {
+        StateData data  = flys.getData(LOCATIONS);
+        String    value = data != null ? (String) data.getValue() : null;
+
+        if (value == null || value.length() == 0) {
+            logger.warn("No location data given.");
+            return null;
+        }
+
+        String[]         splitted = value.split(" ");
+        TDoubleArrayList values   = new TDoubleArrayList();
+
+        for (String split: splitted) {
+            try {
+                values.add(Double.valueOf(split));
+            }
+            catch (NumberFormatException nfe) {
+                logger.warn(nfe, nfe);
+            }
+        }
+
+        return values.toNativeArray();
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/LocationSelect.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,135 @@
+package org.dive4elements.river.artifacts.states;
+
+import gnu.trove.TDoubleArrayList;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Element;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+
+import org.dive4elements.artifactdatabase.data.StateData;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+
+/**
+ * This state is used to realize the input of multiple locations as string.
+ *
+ * The string should be a whitespace separated list of double values where each
+ * double value represents a location.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class LocationSelect extends LocationDistanceSelect {
+
+    /** The logger used in this class.*/
+    private static Logger logger = Logger.getLogger(LocationSelect.class);
+
+
+    public LocationSelect() {
+    }
+
+
+    /** UI Provider (which input method should the client provide to user. */
+    @Override
+    protected String getUIProvider() {
+        return "location_panel";
+    }
+
+
+    @Override
+    protected Element[] createItems(
+        XMLUtils.ElementCreator cr,
+        Artifact    artifact,
+        String      name,
+        CallContext context)
+    {
+        double[] minmax = getMinMax(artifact);
+
+        double minVal = Double.MIN_VALUE;
+        double maxVal = Double.MAX_VALUE;
+
+        if (minmax != null) {
+            minVal = minmax[0];
+            maxVal = minmax[1];
+        }
+        else {
+            logger.warn("Could not read min/max distance values!");
+        }
+
+        if (name.equals(LOCATIONS)) {
+            Element min = createItem(
+                cr,
+                new String[] {"min", new Double(minVal).toString()});
+
+            Element max = createItem(
+                cr,
+                new String[] {"max", new Double(maxVal).toString()});
+
+            return new Element[] { min, max };
+        }
+
+        return null;
+    }
+
+
+    /** Validates data from artifact. */
+    @Override
+    public boolean validate(Artifact artifact)
+    throws IllegalArgumentException
+    {
+        logger.debug("LocationSelect.validate");
+
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+        StateData    data = getData(flys, LOCATIONS);
+
+        String locationStr = data != null
+            ? (String) data.getValue()
+            : null;
+
+        if (locationStr == null || locationStr.length() == 0) {
+            logger.error("No locations given.");
+            throw new IllegalArgumentException("error_empty_state");
+        }
+
+        double[] minmax = getMinMax(artifact);
+        double[] mm     = extractLocations(locationStr);
+
+        logger.debug("Inserted min location: " + mm[0]);
+        logger.debug("Inserted max location: " + mm[mm.length-1]);
+
+        return validateBounds(minmax[0], minmax[1], mm[0], mm[mm.length-1], 0d);
+    }
+
+
+    /**
+     * This method takes a string that consist of whitespace separated double
+     * values and returns the double values as array.
+     *
+     * @param locationStr The locations inserted in this state.
+     *
+     * @return the locations as array.
+     */
+    protected double[] extractLocations(String locationStr) {
+        String[] tmp               = locationStr.split(" ");
+        TDoubleArrayList locations = new TDoubleArrayList();
+
+        for (String l: tmp) {
+            try {
+                locations.add(Double.parseDouble(l));
+            }
+            catch (NumberFormatException nfe) {
+                logger.warn(nfe, nfe);
+            }
+        }
+
+        locations.sort();
+
+        return locations.toNativeArray();
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/ManualPointsSingleState.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,141 @@
+package org.dive4elements.river.artifacts.states;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.json.JSONArray;
+import org.json.JSONException;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.ManualPointsArtifact;
+import org.dive4elements.river.artifacts.model.CrossSectionWaterLineFacet;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.ManualPointsFacet;
+import org.dive4elements.river.artifacts.resources.Resources;
+
+/**
+ * The only state for an ManualPointArtifact.
+ */
+public class ManualPointsSingleState
+extends      DefaultState
+implements   FacetTypes
+{
+    /** Developer-centric description of facet. */
+    public static final String I18N_DESCRIPTION
+        = "facet.longitudinal_section.manualpoint";
+
+    /** Part of data key. */
+    protected static final String DOT_DATA
+        = ".data";
+
+    /** Part of data key. */
+    protected static final String DOT_LINES
+        = ".lines";
+
+    /** The logger that is used in this state. */
+    private static final Logger logger =
+        Logger.getLogger(ManualPointsSingleState.class);
+
+
+    /**
+     * Add ManualPointsFacets to list of Facets.
+     *
+     * @param artifact Ignored.
+     * @param hash Ignored.
+     * @param meta CallMeta to be used for internationalization.
+     * @param facets List to add ManualPointsFacet to.
+     *
+     * @return null.
+     */
+    public Object compute(
+        FLYSArtifact artifact,
+        String       hash,
+        CallMeta     meta,
+        List<Facet>  facets
+    ) {
+        logger.debug("ManualPointsSingleState.compute()");
+        ManualPointsArtifact points = (ManualPointsArtifact) artifact;
+
+        // Add Facet per Diagram type if data given.
+        for (ChartType ct: ChartType.values()) {
+            // Handle points.
+            String pointData = points.getDataAsString(ct + "." + MANUALPOINTS +
+                DOT_DATA);
+            if (pointData != null && pointData.length() != 0
+                && !pointData.equals("[]")) {
+                String fName = ct + "." + MANUALPOINTS;
+                ManualPointsFacet facet = new ManualPointsFacet(
+                    0,
+                    fName,
+                    Resources.getMsg(meta, MANUALPOINTS, "Manual Points"));
+                facets.add(facet);
+                logger.debug("compute(): ManualPointsFacet for " + ct + " created");
+            }
+            else {
+                logger.debug("compute(): No points for " + ct);
+            }
+
+            // Handle lines.
+            String linesData = points.getDataAsString(ct + "." + MANUALPOINTS +
+                DOT_LINES);
+            if (linesData != null && linesData.length() != 0
+                && !linesData.equals("[]")) {
+                try {
+                    JSONArray lines = new JSONArray(linesData);
+                    for (int i = 0, P = lines.length(); i < P; i++) {
+                        JSONArray array = lines.getJSONArray(i);
+                        double y    = array.getDouble(0);
+                        String name = array.getString(1);
+                        String fName = ct + "." + MANUALLINE;
+                        logger.debug("have facet: " + y + " / " + name + " -> " + fName);
+                        CrossSectionWaterLineFacet facet = new CrossSectionWaterLineFacet(
+                            i,
+                            fName,
+                            name);
+
+                        facets.add(facet);
+                    }
+                }
+                catch(JSONException e){
+                    logger.error("Could not decode json.");
+                }
+
+            }
+            else {
+                //logger.debug("No points for " + ct);
+            }
+        }
+
+        return null;
+    }
+
+
+    /** Call compute. */
+    @Override
+    public Object computeInit(
+        FLYSArtifact artifact,
+        String       hash,
+        Object       context,
+        CallMeta     meta,
+        List<Facet>  facets
+     ) {
+        return compute(artifact, hash, meta, facets);
+    }
+
+
+    /** Call compute. */
+    @Override
+    public Object computeFeed(
+        FLYSArtifact artifact,
+        String hash,
+        CallContext context,
+        List<Facet> facets,
+        Object old
+    ) {
+        return compute(artifact, hash, context.getMeta(), facets);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/MiddleBedHeight.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,97 @@
+package org.dive4elements.river.artifacts.states;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.access.BedHeightAccess;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.DataFacet;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.MiddleBedHeightData;
+import org.dive4elements.river.artifacts.model.MiddleBedHeightFacet;
+import org.dive4elements.river.artifacts.model.MiddleBedHeightCalculation;
+
+
+/** State in which Middle Bed Heights are generated. */
+public class MiddleBedHeight extends DefaultState implements FacetTypes {
+
+    /** Private logger. */
+    private static final Logger logger = Logger.getLogger(MiddleBedHeight.class);
+
+
+    @Override
+    public Object computeAdvance(
+        FLYSArtifact artifact,
+        String       hash,
+        CallContext  context,
+        List<Facet>  facets,
+        Object       old
+    ) {
+        logger.debug("MiddleBedHeight.computeAdvance");
+
+        List<Facet> newFacets = new ArrayList<Facet>();
+
+        BedHeightAccess access = new BedHeightAccess(artifact, context);
+
+        CalculationResult res = old instanceof CalculationResult
+            ? (CalculationResult) old
+            : new MiddleBedHeightCalculation().calculate(access);
+
+        if (facets == null || res == null) {
+            return res;
+        }
+
+        MiddleBedHeightData[] data = (MiddleBedHeightData[]) res.getData();
+
+        logger.debug("Calculated " + data.length + " MiddleBedHeightData objects");
+
+        String id  = getID();
+        int    idx = 0;
+
+        for (MiddleBedHeightData d: data) {
+            if (d.getStartYear() == d.getEndYear()) {
+                newFacets.add(new MiddleBedHeightFacet(
+                    idx,
+                    MIDDLE_BED_HEIGHT_SINGLE,
+                    d.getSoundingName(context),
+                    ComputeType.ADVANCE,
+                    id,
+                    hash
+                ));
+            }
+            else {
+                newFacets.add(new MiddleBedHeightFacet(
+                    idx,
+                    MIDDLE_BED_HEIGHT_EPOCH,
+                    d.getSoundingName(context),
+                    ComputeType.ADVANCE,
+                    id,
+                    hash
+                ));
+            }
+
+            idx++;
+        }
+
+        Facet csv = new DataFacet(
+            CSV, "CSV data", ComputeType.ADVANCE, hash, id);
+
+        // TODO ADD PDF FACET
+
+        newFacets.add(csv);
+
+        logger.debug("Created " + newFacets.size() + " new Facets.");
+
+        facets.addAll(newFacets);
+
+        return res;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/MinMaxState.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,173 @@
+package org.dive4elements.river.artifacts.states;
+
+import org.w3c.dom.Element;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.artifacts.common.ArtifactNamespaceContext;
+import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator;
+
+import org.dive4elements.artifactdatabase.ProtocolUtils;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+
+/**
+ * State that holds minimun and maximum (for validation).
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public abstract class MinMaxState extends DefaultState {
+
+    private static final Logger logger = Logger.getLogger(MinMaxState.class);
+
+    @Override
+    protected void appendItems(
+        Artifact       artifact,
+        ElementCreator creator,
+        String         name,
+        CallContext    context,
+        Element        select
+    ) {
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        select.setAttributeNS(
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            "art:type",
+            getType());
+
+        String[] defMinMax = getDefaults(artifact, name);
+
+        Element min = ProtocolUtils.createArtNode(
+            creator,
+            "min",
+            new String[] { "value", "default" },
+            new String[] { String.valueOf(getLower(flys)), defMinMax[0] });
+
+        Element max = ProtocolUtils.createArtNode(
+            creator,
+            "max",
+            new String[] { "value", "default" },
+            new String[] { String.valueOf(getUpper(flys)), defMinMax[1] });
+
+        select.appendChild(min);
+        select.appendChild(max);
+    }
+
+
+    /**
+     * @param cc
+     * @param name
+     * @param value
+     * @param type
+     *
+     * @return
+     */
+    @Override
+    protected String getLabelFor(
+        CallContext cc,
+        String      name,
+        String      value,
+        String      type
+    ) {
+        if (type.indexOf("range") > 0) {
+            String[] minmax = extractRangeAsString(value);
+
+            if (minmax != null) {
+                return minmax[0] + " - " + minmax[1];
+            }
+        }
+
+        return super.getLabelFor(cc, name, value, type);
+    }
+
+
+    /**
+     * Returns a string array with [min,max] from <i>rawValue</i>.
+     * <i>rawValue</i> should be a string like "1999;2001".
+     *
+     * @param rawValue A string with min and max separated by a ';'.
+     *
+     * @return the min and max as string array ([min,max]).
+     */
+    protected String[] extractRangeAsString(String rawValue) {
+        return rawValue.split(";");
+    }
+
+
+    /**
+     * This method returns the default values for min and max. If the static
+     * field DefaultState.USE_DEFAULTS is set, the minimum and maximum inserted
+     * by the user is returned as string. Otherwise, the absolute minimum and
+     * maximum are returned.
+     *
+     * @param artifact The FLYSArtifact.
+     * @param name The name of the parameter.
+     *
+     * @return a string array [min,max] that contains the minimum and maximum
+     * values for the parameter <i>name</i>.
+     */
+    protected String[] getDefaults(Artifact artifact, String name) {
+        if (DefaultState.USE_DEFAULTS) {
+            String[] tmp = getMinMaxByParameter(artifact, name);
+
+            return tmp != null ? tmp : getMinMaxDefaults(artifact, name);
+        }
+        else {
+            return getMinMaxDefaults(artifact, name);
+        }
+    }
+
+
+    /**
+     * Returns a string array with minimum and maximum inserted by the user as
+     * [min,max].
+     *
+     * @param artifact The FLYSArtifact that stores the parameter.
+     * @param name The name of the parameter for raw min/max value string.
+     *
+     * @return a string array [min,max].
+     */
+    protected String[] getMinMaxByParameter(Artifact artifact, String name) {
+        FLYSArtifact flys     = (FLYSArtifact) artifact;
+        String       rawValue = flys.getDataAsString(name);
+
+        if (rawValue == null) {
+            logger.debug("No value for '" + rawValue + "' existing.");
+            return null;
+        }
+
+        logger.debug("Raw value for '" + name + "' = " + rawValue);
+
+        return extractRangeAsString(rawValue);
+    }
+
+
+    /**
+     * Returns a string array with absolute minimum and maximum as [min,max].
+     *
+     * @param artifact The FLYSArtifact (not used in this implementation).
+     * @param name The parameter name (not used in this implementation).
+     *
+     * @return a string array [min,max].
+     */
+    protected String[] getMinMaxDefaults(Artifact artifact, String name) {
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        Object lower = getLower(flys);
+        Object upper = getUpper(flys);
+
+        return new String[] { String.valueOf(lower), String.valueOf(upper) };
+    }
+
+
+    protected abstract Object getLower(FLYSArtifact flys);
+
+    protected abstract Object getUpper(FLYSArtifact flys);
+
+    protected abstract String getType();
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/MultiIntArrayState.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,101 @@
+package org.dive4elements.river.artifacts.states;
+
+import org.w3c.dom.Element;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator;
+import org.dive4elements.artifacts.common.model.KVP;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.resources.Resources;
+import org.dive4elements.river.utils.FLYSUtils;
+
+
+/**
+ * State that holds minimun and maximum (for validation).
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public abstract class MultiIntArrayState extends DefaultState {
+
+    private static final Logger logger =
+        Logger.getLogger(MultiIntArrayState.class);
+
+
+    @Override
+    protected void appendItems(
+        Artifact       artifact,
+        ElementCreator creator,
+        String         name,
+        CallContext    context,
+        Element        select
+    ) {
+        try {
+            creator.addAttr(select, "type", "intoptions", true);
+
+            for (KVP kvp: getOptions(artifact, name)) {
+                Element item = creator.create("item");
+                creator.addAttr(item, "label", kvp.getValue().toString(), true);
+                creator.addAttr(item, "value", kvp.getKey().toString(), true);
+
+                select.appendChild(item);
+            }
+        }
+        catch (IllegalArgumentException iae) {
+            logger.warn("Illegal argument", iae);
+        }
+    }
+
+
+    @Override
+    protected Element createStaticData(
+        FLYSArtifact   flys,
+        ElementCreator creator,
+        CallContext    cc,
+        String         name,
+        String         value,
+        String         type
+    ) {
+        Element data = creator.create("data");
+        creator.addAttr(data, "name",  name, true);
+        creator.addAttr(data, "type",  type, true);
+        creator.addAttr(data, "label",
+            Resources.getMsg(cc.getMeta(), name, name), true);
+
+        int[] values = FLYSUtils.intArrayFromString(value);
+
+        for (int val: values) {
+            try {
+                Element item = creator.create("item");
+                creator.addAttr(item, "value", String.valueOf(val), true);
+                creator.addAttr(item, "label", getLabelFor(cc, name, val), true);
+
+                data.appendChild(item);
+            }
+            catch (IllegalArgumentException iae) {
+                logger.warn("Cannot append item: " + val, iae);
+            }
+        }
+
+        return data;
+    }
+
+
+    protected abstract KVP<Integer, String>[] getOptions(
+        Artifact artifact,
+        String   parameterName
+    )
+    throws IllegalArgumentException;
+
+
+    protected abstract String getLabelFor(
+        CallContext cc,
+        String      parameterName,
+        int         value)
+    throws IllegalArgumentException;
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/MultiStringArrayState.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,96 @@
+package org.dive4elements.river.artifacts.states;
+
+import org.w3c.dom.Element;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator;
+import org.dive4elements.artifacts.common.model.KVP;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.resources.Resources;
+
+
+/**
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public abstract class MultiStringArrayState extends DefaultState {
+
+    private static final Logger logger =
+        Logger.getLogger(MultiStringArrayState.class);
+
+
+    @Override
+    protected void appendItems(
+        Artifact       artifact,
+        ElementCreator creator,
+        String         name,
+        CallContext    context,
+        Element        select
+    ) {
+        try {
+            creator.addAttr(select, "type", "options", true);
+
+            for (KVP kvp: getOptions(artifact, name, context)) {
+                Element item = creator.create("item");
+                creator.addAttr(item, "label", kvp.getValue().toString(), true);
+                creator.addAttr(item, "value", kvp.getKey().toString(), true);
+
+                select.appendChild(item);
+            }
+        }
+        catch (IllegalArgumentException iae) {
+            logger.warn("Illegal argument", iae);
+        }
+    }
+
+
+    @Override
+    protected Element createStaticData(
+        FLYSArtifact   flys,
+        ElementCreator creator,
+        CallContext    cc,
+        String         name,
+        String         value,
+        String         type
+    ) {
+        Element data = creator.create("data");
+        creator.addAttr(data, "name",  name, true);
+        creator.addAttr(data, "type",  type, true);
+        creator.addAttr(data, "label",
+            Resources.getMsg(cc.getMeta(), name, name), true);
+
+        String[] values = value.split(";");
+
+        for (String val: values) {
+            Element item = creator.create("item");
+            creator.addAttr(item, "value", val, true);
+            creator.addAttr(item, "label", getLabelFor(cc, name, val), true);
+
+            data.appendChild(item);
+        }
+
+        return data;
+    }
+
+
+    protected abstract KVP<String, String>[] getOptions(
+        Artifact artifact,
+        String   parameterName,
+        CallContext context
+    )
+    throws IllegalArgumentException;
+
+
+    protected abstract String getLabelFor(
+        CallContext cc,
+        String      parameterName,
+        String      value
+    )
+    throws IllegalArgumentException;
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/OutliersInput.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,42 @@
+package org.dive4elements.river.artifacts.states;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifactdatabase.data.StateData;
+import org.dive4elements.artifacts.CallContext;
+
+/**
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class OutliersInput extends DefaultState {
+
+    /** The logger used in this class. */
+    private static Logger logger = Logger.getLogger(OutliersInput.class);
+
+    public static final String PARAMETER_NAME = "outliers";
+
+
+    /**
+     * The default constructor that initializes an empty State object.
+     */
+    public OutliersInput() {
+    }
+
+
+    /** Tell UI how to allow for input. */
+    @Override
+    protected String getUIProvider() {
+        return "outliers_input";
+    }
+
+
+    @Override
+    protected String[] getDefaultsFor(CallContext context, StateData data) {
+        if (data != null && data.getName().equals(PARAMETER_NAME)) {
+            return new String[] {"3", "3"};
+        }
+
+        return null;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/OutputState.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,38 @@
+package org.dive4elements.river.artifacts.states;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.model.FacetTypes;
+
+
+public class OutputState extends DefaultState implements FacetTypes {
+
+    @Override
+    public Element describeStatic(
+        Artifact    artifact,
+        Document    document,
+        Node        root,
+        CallContext context,
+        String      uuid)
+    {
+        return null;
+    }
+
+
+    @Override
+    public Element describe(
+        Artifact    artifact,
+        Document    document,
+        Node        root,
+        CallContext context,
+        String      uuid)
+    {
+        return 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/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/PeriodSelect.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,21 @@
+package org.dive4elements.river.artifacts.states;
+
+public class PeriodSelect extends DefaultState {
+
+    public static final String UI_PROVIDER = "period_select";
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * The default constructor that initializes an empty State object.
+     */
+    public PeriodSelect() {
+    }
+
+    @Override
+    protected String getUIProvider() {
+        return UI_PROVIDER;
+    }
+
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/PeriodsSelect.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,26 @@
+package org.dive4elements.river.artifacts.states;
+
+import org.apache.log4j.Logger;
+
+/**
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class PeriodsSelect extends DefaultState {
+
+    /** The logger used in this class. */
+    private static Logger logger = Logger.getLogger(PeriodsSelect.class);
+
+
+    /**
+     * The default constructor that initializes an empty State object.
+     */
+    public PeriodsSelect() {
+    }
+
+    @Override
+    protected String getUIProvider() {
+        return "periods_select";
+    }
+
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/ProfileDistanceSelect.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,13 @@
+package org.dive4elements.river.artifacts.states;
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ProfileDistanceSelect extends DefaultState {
+
+    @Override
+    protected String getUIProvider() {
+        return "auto_integer";
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/QSectorSingleState.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,91 @@
+package org.dive4elements.river.artifacts.states;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.QSectorArtifact;
+
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.QSectorFacet;
+
+import org.dive4elements.river.artifacts.resources.Resources;
+
+/**
+ * The only state for an QSectorArtifact.
+ */
+public class QSectorSingleState
+extends      DefaultState
+implements   FacetTypes
+{
+    /** Developer-centric description of facet. */
+    public static final String I18N_DESCRIPTION
+        = "facet.qsector";
+
+    /** The logger that is used in this state. */
+    private static final Logger logger =
+        Logger.getLogger(QSectorSingleState.class);
+
+
+    /**
+     * Add QSectorFacets to list of Facets.
+     *
+     * @param artifact Ignored.
+     * @param hash Ignored.
+     * @param meta CallMeta to be used for internationalization.
+     * @param facets List to add QSectorFacet to.
+     *
+     * @return null.
+     */
+    public Object compute(
+        FLYSArtifact artifact,
+        String       hash,
+        CallMeta     meta,
+        List<Facet>  facets
+    ) {
+        logger.debug("QSectorSingleState.compute()");
+        QSectorArtifact points = (QSectorArtifact) artifact;
+
+        QSectorFacet qfacet = new QSectorFacet(
+            0,
+            QSECTOR,
+            Resources.getMsg(meta, "qsectors", "Q Sectors"));
+
+        facets.add(qfacet);
+
+        return null;
+    }
+
+
+    /** Call compute. */
+    @Override
+    public Object computeInit(
+        FLYSArtifact artifact,
+        String       hash,
+        Object       context,
+        CallMeta     meta,
+        List<Facet>  facets
+     ) {
+        return compute(artifact, hash, meta, facets);
+    }
+
+
+    /** Call compute. */
+    @Override
+    public Object computeFeed(
+        FLYSArtifact artifact,
+        String hash,
+        CallContext context,
+        List<Facet> facets,
+        Object old
+    ) {
+        return compute(artifact, hash, context.getMeta(), facets);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/RangeState.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,95 @@
+package org.dive4elements.river.artifacts.states;
+
+import org.dive4elements.artifacts.Artifact;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+import org.dive4elements.river.artifacts.access.RangeAccess;
+
+import org.apache.log4j.Logger;
+
+
+/**
+ * State in which km range is set.
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public abstract class RangeState extends DefaultState {
+
+    /** The logger that is used in this class. */
+    private Logger logger = Logger.getLogger(RangeState.class);
+
+
+    public RangeState() {
+    }
+
+    protected abstract double[] getMinMax(Artifact artifact);
+
+
+    protected boolean validateBounds(
+        double fromValid, double toValid,
+        double from,      double to)
+    throws IllegalArgumentException
+    {
+        if (from < fromValid) {
+            logger.error(
+                "Invalid 'from'. " + from + " is smaller than " + fromValid);
+            throw new IllegalArgumentException("error_feed_from_out_of_range");
+        }
+        else if (to > toValid) {
+            logger.error(
+                "Invalid 'to'. " + to + " is bigger than " + toValid);
+            throw new IllegalArgumentException("error_feed_to_out_of_range");
+        }
+
+        return true;
+    }
+
+
+    /**
+     * Validates a given range with a given valid range.
+     *
+     * @param fromValid Valid lower value of the range.
+     * @param toValid Valid upper value of the range.
+     * @param from The lower value.
+     * @param to The upper value.
+     * @param step The step width.
+     *
+     * @return true, if everything was fine, otherwise an exception is thrown.
+     */
+    protected boolean validateBounds(
+        double fromValid, double toValid,
+        double from,      double to,      double step)
+    throws IllegalArgumentException
+    {
+        logger.debug("RangeState.validateRange");
+
+        // XXX The step width is not validated at the moment!
+        return validateBounds(fromValid, toValid, from, to);
+    }
+
+
+    @Override
+    public boolean validate(Artifact artifact)
+    throws IllegalArgumentException
+    {
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        try {
+            RangeAccess rangeAccess = new RangeAccess(flys, null);
+            double from = rangeAccess.getFrom();
+            double to   = rangeAccess.getTo();
+            double step = rangeAccess.getStep();
+
+            double[] minmax = getMinMax(flys);
+
+            return validateBounds(minmax[0], minmax[1], from, to, step);
+        }
+        catch (NumberFormatException nfe) {
+            throw new IllegalArgumentException("error_invalid_double_value");
+        }
+        catch (NullPointerException npe) {
+            throw new IllegalArgumentException("error_empty_state");
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/ReferenceCurveState.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,87 @@
+package org.dive4elements.river.artifacts.states;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.WINFOArtifact;
+
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.DataFacet;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.ReferenceCurveFacet;
+import org.dive4elements.river.artifacts.model.ReportFacet;
+import org.dive4elements.river.artifacts.model.WWQQ;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+
+/** State of WINFO in which reference curves can be produced. */
+public class ReferenceCurveState
+extends      DefaultState
+implements   FacetTypes
+{
+    private static Logger logger = Logger.getLogger(ReferenceCurveState.class);
+
+
+    public ReferenceCurveState() {
+    }
+
+
+    @Override
+    public Object computeAdvance(
+        FLYSArtifact artifact,
+        String       hash,
+        CallContext  context,
+        List<Facet>  facets,
+        Object       old
+    ) {
+        if (!(artifact instanceof WINFOArtifact)) {
+            return null;
+        }
+
+        String id = getID();
+
+        WINFOArtifact winfo = (WINFOArtifact)artifact;
+
+        CalculationResult res = old instanceof CalculationResult
+            ? (CalculationResult)old
+            : winfo.getReferenceCurveData(context);
+
+        if (facets == null) {
+            return res;
+        }
+
+        WWQQ [] wws = (WWQQ [])res.getData();
+
+        for (int i = 0; i < wws.length; ++i) {
+            String wwsName = wws[i].getName();
+            facets.add(new ReferenceCurveFacet(i,
+                REFERENCE_CURVE,
+                wwsName
+                ));
+            facets.add(new ReferenceCurveFacet(i,
+                REFERENCE_CURVE_NORMALIZED,
+                wwsName
+                ));
+        }
+
+        if (wws.length > 0) {
+            logger.debug("Adding CSV and PDF data facet.");
+            Facet csv = new DataFacet (CSV, "CSV data", ComputeType.ADVANCE, hash, id);
+            Facet pdf = new DataFacet (PDF, "PDF data", ComputeType.ADVANCE, hash, id);
+            facets.add(csv);
+            facets.add(pdf);
+        }
+
+        if (res.getReport().hasProblems()) {
+            facets.add(new ReportFacet(ComputeType.ADVANCE, hash, id));
+        }
+
+        return res;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/ReferenceGaugeState.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,81 @@
+package org.dive4elements.river.artifacts.states;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.w3c.dom.Element;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+
+import org.dive4elements.river.model.Gauge;
+import org.dive4elements.river.model.River;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.utils.FLYSUtils;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ReferenceGaugeState extends DefaultState {
+
+    public static final String DATA_NAME = "reference_gauge";
+
+
+    @Override
+    protected Element[] createItems(
+        XMLUtils.ElementCreator cr,
+        Artifact    artifact,
+        String      name,
+        CallContext context)
+    {
+        River       river   = FLYSUtils.getRiver((FLYSArtifact) artifact);
+        List<Gauge> gauges  = river.getGauges();
+        Collections.sort(gauges);
+
+        int num = gauges != null ? gauges.size() : 0;
+
+        Element[] opts = new Element[num];
+
+        for (int i = 0; i < num; i++ ) {
+            Gauge g = gauges.get(i);
+
+            String gaugeName      = g.getName();
+            long   officialNumber = g.getOfficialNumber();
+
+            opts[i] = createItem(
+                cr, new String[] { gaugeName, String.valueOf(officialNumber) });
+        }
+
+        return opts;
+    }
+
+
+    @Override
+    protected String getLabelFor(
+        CallContext cc,
+        String      name,
+        String      value,
+        String      type
+    ) {
+        if (name.equals(DATA_NAME)) {
+            try {
+                long  number = Long.valueOf(value);
+                Gauge gauge  = Gauge.getGaugeByOfficialNumber(number);
+
+                if (gauge != null) {
+                    return gauge.getName();
+                }
+            }
+            catch (NumberFormatException nfe) {
+                // do nothing
+            }
+        }
+
+        return super.getLabelFor(cc, name, value, type);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/RiverSelect.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,166 @@
+package org.dive4elements.river.artifacts.states;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+
+import org.dive4elements.artifactdatabase.ProtocolUtils;
+import org.dive4elements.artifactdatabase.data.StateData;
+
+import org.dive4elements.river.model.River;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.model.RiverFactory;
+import org.dive4elements.river.artifacts.resources.Resources;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class RiverSelect extends DefaultState {
+
+    /** The logger used in this class. */
+    private static Logger logger = Logger.getLogger(RiverSelect.class);
+
+    /** Error message that is thrown if no river was found based on a given
+     * name.*/
+    public static final String ERROR_NO_SUCH_RIVER =
+        "error_feed_no_such_river";
+
+    /** Error message that is thrown if no river was found based on a given
+     * name.*/
+    public static final String ERROR_NO_RIVER_SELECTED =
+        "error_feed_no_river_selected";
+
+
+    /**
+     * The default constructor that initializes an empty State object.
+     */
+    public RiverSelect() {
+    }
+
+
+    /**
+     * Initialize the state based on the state node in the configuration.
+     *
+     * @param config The state configuration node.
+     */
+    public void setup(Node config) {
+        super.setup(config);
+    }
+
+
+    protected Element createData(
+        XMLUtils.ElementCreator cr,
+        Artifact    artifact,
+        StateData   data,
+        CallContext context)
+    {
+        Element select = ProtocolUtils.createArtNode(
+            cr, "select",
+            new String[] { "uiprovider" },
+            new String[] { "select_with_map" });
+        cr.addAttr(select, "name", data.getName(), true);
+
+        Element label = ProtocolUtils.createArtNode(
+            cr, "label", null, null);
+
+
+        // XXX: DEAD CODE
+        /*
+        Element choices = ProtocolUtils.createArtNode(
+            cr, "choices", null, null);
+        */
+
+        select.appendChild(label);
+
+        label.setTextContent(Resources.getMsg(
+            context.getMeta(),
+            getID(),
+            getID()));
+
+        return select;
+    }
+
+
+    @Override
+    protected Element[] createItems(
+        XMLUtils.ElementCreator cr,
+        Artifact    artifact,
+        String      name,
+        CallContext context)
+    {
+        List<River> rivers = RiverFactory.getRivers();
+        Element[] items    = new Element[rivers.size()];
+
+        int idx = 0;
+        for (River river: rivers) {
+            items[idx++] = createRiverItem(cr, river);
+        }
+
+        return items;
+    }
+
+
+    /**
+     * This method creates a node that represents a river item. This node
+     * contains the label and the value that describe the river.
+     *
+     * @param cr The ElementCreator.
+     * @param river The river.
+     *
+     * @return the element that contains the information about the river.
+     */
+    protected Element createRiverItem(XMLUtils.ElementCreator cr, River river) {
+        Element item  = ProtocolUtils.createArtNode(cr, "item", null, null);
+        Element label = ProtocolUtils.createArtNode(cr, "label", null, null);
+        Element value = ProtocolUtils.createArtNode(cr, "value", null, null);
+
+        label.setTextContent(river.getName());
+        value.setTextContent(river.getName());
+
+        item.appendChild(label);
+        item.appendChild(value);
+
+        return item;
+    }
+
+
+    @Override
+    public boolean validate(Artifact artifact)
+    throws IllegalArgumentException
+    {
+        logger.debug("RiverSelect.validate");
+
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        StateData dRiver = getData(flys, "river");
+
+        if (dRiver == null || dRiver.getValue() == null) {
+            throw new IllegalArgumentException(ERROR_NO_RIVER_SELECTED);
+        }
+
+        River river = RiverFactory.getRiver((String) dRiver.getValue());
+
+        if (river == null) {
+            throw new IllegalArgumentException(ERROR_NO_SUCH_RIVER);
+        }
+
+        return true;
+    }
+
+
+    @Override
+    protected String getUIProvider() {
+        return "river_panel";
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/SQRelation.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,295 @@
+package org.dive4elements.river.artifacts.states;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifactdatabase.state.FacetActivity;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.artifacts.common.utils.StringUtils;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+import org.dive4elements.river.artifacts.access.SQRelationAccess;
+
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.DataFacet;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+
+import org.dive4elements.river.artifacts.model.sq.SQCurveFacet;
+import org.dive4elements.river.artifacts.model.sq.SQFractionResult;
+import org.dive4elements.river.artifacts.model.sq.SQMeasurementFacet;
+import org.dive4elements.river.artifacts.model.sq.SQOutlierCurveFacet;
+import org.dive4elements.river.artifacts.model.sq.SQOutlierFacet;
+import org.dive4elements.river.artifacts.model.sq.SQOutlierMeasurementFacet;
+import org.dive4elements.river.artifacts.model.sq.SQOverviewFacet;
+import org.dive4elements.river.artifacts.model.sq.SQRelationCalculation;
+import org.dive4elements.river.artifacts.model.sq.SQResult;
+
+import org.dive4elements.river.artifacts.resources.Resources;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+/**
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class SQRelation extends DefaultState implements FacetTypes {
+
+    private static Logger log = Logger.getLogger(SQRelation.class);
+
+
+    public static final String I18N_FACET_CURVE =
+        "facet.sq_relation.curve";
+
+    public static final String I18N_FACET_MEASUREMENTS =
+        "facet.sq_relation.measurements";
+
+    public static final String I18N_FACET_OUTLIERS =
+        "facet.sq_relation.outliers";
+
+    public static final String I18N_FACET_OUTLIER_CURVE =
+        "facet.sq_relation.outlier.curve";
+
+    public static final String I18N_FACET_OUTLIER_MEASUREMENT =
+        "facet.sq_relation.outlier.measurement";
+
+    public static final int CURVE_INDEX               = 0;
+    public static final int MEASURREMENT_INDEX        = 1;
+    public static final int OUTLIER_INDEX             = 2;
+    public static final int OUTLIER_CURVE_INDEX       = 3;
+    public static final int OUTLIER_MEASUREMENT_INDEX = 4;
+
+    public static final String [][] FACET_NAMES = {
+        { SQ_A_CURVE, SQ_B_CURVE, SQ_C_CURVE,
+          SQ_D_CURVE, SQ_E_CURVE, SQ_F_CURVE
+        },
+        { SQ_A_MEASUREMENT, SQ_B_MEASUREMENT, SQ_C_MEASUREMENT,
+          SQ_D_MEASUREMENT, SQ_E_MEASUREMENT, SQ_F_MEASUREMENT
+        },
+        { SQ_A_OUTLIER, SQ_B_OUTLIER, SQ_C_OUTLIER,
+          SQ_D_OUTLIER, SQ_E_OUTLIER, SQ_F_OUTLIER
+        },
+        { SQ_A_OUTLIER_CURVE, SQ_B_OUTLIER_CURVE, SQ_C_OUTLIER_CURVE,
+          SQ_D_OUTLIER_CURVE, SQ_E_OUTLIER_CURVE, SQ_F_OUTLIER_CURVE
+        },
+        { SQ_A_OUTLIER_MEASUREMENT, SQ_B_OUTLIER_MEASUREMENT,
+          SQ_C_OUTLIER_MEASUREMENT, SQ_D_OUTLIER_MEASUREMENT,
+          SQ_E_OUTLIER_MEASUREMENT, SQ_F_OUTLIER_MEASUREMENT
+        }
+    };
+
+
+    static {
+        // Active/deactivate facets.
+        FacetActivity.Registry.getInstance().register(
+            "minfo",
+            new FacetActivity() {
+                @Override
+                public Boolean isInitialActive(
+                    Artifact artifact,
+                    Facet    facet,
+                    String   output
+                ) {
+                    String name = facet.getName();
+
+                    if (StringUtils.contains(
+                        name, FACET_NAMES[CURVE_INDEX])
+                    ||  StringUtils.contains(
+                        name, FACET_NAMES[OUTLIER_INDEX])
+                    ||  StringUtils.contains(
+                        name, FACET_NAMES[MEASURREMENT_INDEX])
+                    ) {
+                        // TODO: Only the last should be active.
+                        return Boolean.TRUE;
+                    }
+
+                    if (StringUtils.contains(
+                        name, FACET_NAMES[OUTLIER_CURVE_INDEX])
+                     || StringUtils.contains(
+                        name, FACET_NAMES[OUTLIER_MEASUREMENT_INDEX])
+                    ) {
+                        return Boolean.FALSE;
+                    }
+
+                    return null;
+                }
+            });
+    }
+
+
+    public SQRelation() {
+    }
+
+
+    @Override
+    public Object computeAdvance(
+        FLYSArtifact artifact,
+        String       hash,
+        CallContext  context,
+        List<Facet>  facets,
+        Object       old
+    ) {
+        log.debug("SQRelation.computeAdvance");
+
+        CalculationResult res = old instanceof CalculationResult
+            ? (CalculationResult)old
+            : new SQRelationCalculation(
+                new SQRelationAccess(artifact)).calculate();
+
+        if (facets == null) {
+            return res;
+        }
+
+        SQResult [] sqr = (SQResult [])res.getData();
+        if (sqr == null) {
+            return res;
+        }
+
+        createFacets(context, facets, sqr, hash);
+
+        Facet csv = new DataFacet(
+            CSV, "CSV data", ComputeType.ADVANCE, hash, id);
+
+        Facet pdf = new DataFacet(
+            PDF, "PDF data", ComputeType.ADVANCE, hash, id);
+
+        facets.add(csv);
+        facets.add(pdf);
+
+        return res;
+    }
+
+
+    protected void createFacets(
+        CallContext context,
+        List<Facet> container,
+        SQResult[]  sqr,
+        String      hash
+    ) {
+        boolean debug = log.isDebugEnabled();
+
+        CallMeta meta    = context.getMeta();
+        String   stateId = getID();
+        for (int i = 0; i < 6; i++) {
+            container.add(new SQOverviewFacet(
+                i,
+                i,
+                "sq_chart_overview",
+                Resources.getMsg(
+                    meta,
+                    I18N_FACET_CURVE,
+                    I18N_FACET_CURVE
+                ),
+                hash,
+                getID()
+            ));
+        }
+        for (int res = 0, n = sqr.length; res < n; res++) {
+
+            for (int i = 0; i < SQResult.NUMBER_FRACTIONS; i++) {
+                SQFractionResult result = sqr[res].getFraction(i);
+
+                if (result == null) {
+                    log.warn("Fraction at index " + i + " is empty!");
+                    continue;
+                }
+
+                container.add(new SQCurveFacet(
+                    res,
+                    i,
+                    getFractionFacetname(CURVE_INDEX, i),
+                    Resources.getMsg(
+                        meta,
+                        I18N_FACET_CURVE,
+                        I18N_FACET_CURVE
+                    ),
+                    hash,
+                    stateId
+                ));
+
+                for (int j = 0, C = result.numIterations()-1; j < C; j++) {
+
+                    Object [] round = new Object [] { j + 1 };
+
+                    int index = res;
+                    index     = index << 16;
+                    index     = index + j;
+
+                    if (debug) {
+                        log.debug("new outliers facet (index=" +index+ ")");
+                        log.debug("   result index = " + res);
+                        log.debug("   fraction idx = " + i);
+                        log.debug("   iteration    = " + j);
+                    }
+
+                    container.add(new SQOutlierFacet(
+                        index,
+                        i,
+                        getFractionFacetname(OUTLIER_INDEX, i),
+                        Resources.getMsg(
+                            meta,
+                            I18N_FACET_OUTLIERS,
+                            I18N_FACET_OUTLIERS,
+                            round
+                        ),
+                        hash,
+                        stateId
+                    ));
+
+                    container.add(new SQOutlierCurveFacet(
+                        index,
+                        i,
+                        getFractionFacetname(OUTLIER_CURVE_INDEX, i),
+                        Resources.getMsg(
+                            meta,
+                            I18N_FACET_OUTLIER_CURVE,
+                            I18N_FACET_OUTLIER_CURVE,
+                            round
+                        ),
+                        hash,
+                        stateId
+                    ));
+
+                    container.add(new SQOutlierMeasurementFacet(
+                        index,
+                        i,
+                        getFractionFacetname(OUTLIER_MEASUREMENT_INDEX, i),
+                        Resources.getMsg(
+                            meta,
+                            I18N_FACET_OUTLIER_MEASUREMENT,
+                            I18N_FACET_OUTLIER_MEASUREMENT,
+                            round
+                        ),
+                        hash,
+                        stateId
+                    ));
+                } // for all outliers
+
+                container.add(new SQMeasurementFacet(
+                    res,
+                    i,
+                    getFractionFacetname(MEASURREMENT_INDEX, i),
+                    Resources.getMsg(
+                        meta,
+                        I18N_FACET_MEASUREMENTS,
+                        I18N_FACET_MEASUREMENTS
+                    ),
+                    hash,
+                    stateId
+                ));
+            } // for all fractions
+        } // for all results
+    }
+
+    protected static String getFractionFacetname(int type, int idx) {
+        if (log.isDebugEnabled()) {
+            log.debug("getFractionFacetname(): " + type + " | " + idx);
+        }
+        type %= FACET_NAMES.length;
+        return FACET_NAMES[type][idx % FACET_NAMES[type].length];
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/ScenarioSelect.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,138 @@
+package org.dive4elements.river.artifacts.states;
+
+import org.dive4elements.artifactdatabase.ProtocolUtils;
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.artifacts.common.utils.FileTools;
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.resources.Resources;
+import org.dive4elements.river.utils.FLYSUtils;
+
+import java.io.File;
+
+import org.apache.log4j.Logger;
+import org.w3c.dom.Element;
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ScenarioSelect extends DefaultState {
+
+    /** The logger that is used in this class.*/
+    private static Logger logger = Logger.getLogger(ScenarioSelect.class);
+
+
+    public static final String FIELD_MODE     = "scenario";
+
+    public static final String SCENARIO_CURRENT   = "scenario.current";
+    public static final String SCENARIO_POTENTIEL = "scenario.potentiel";
+    public static final String SCENARIO_SCENRAIO  = "scenario.scenario";
+
+    public static final String[] SCENARIOS = {
+        SCENARIO_CURRENT,
+        SCENARIO_POTENTIEL,
+        SCENARIO_SCENRAIO };
+
+
+    @Override
+    protected String getUIProvider() {
+        return "";
+    }
+
+    @Override
+    protected Element[] createItems(
+        XMLUtils.ElementCreator cr,
+        Artifact    artifact,
+        String      name,
+        CallContext context)
+    {
+        CallMeta meta = context.getMeta();
+
+        if (name.equals(FIELD_MODE)) {
+            Element[] scenarios = new Element[SCENARIOS.length];
+
+            int i = 0;
+
+            for (String scenario: SCENARIOS) {
+                scenarios[i++] = createItem(
+                    cr, new String[] {
+                        Resources.getMsg(meta, scenario, scenario),
+                        scenario
+                    });
+            }
+
+            return scenarios;
+        }
+        else {
+            FLYSArtifact flys = (FLYSArtifact) artifact;
+            String       data = flys.getDataAsString(name);
+
+            return new Element[] { createItem(
+                cr,
+                new String[] {
+                    Resources.getMsg(meta, name, name),
+                    data
+                }
+            )};
+        }
+    }
+
+
+    @Override
+    protected Element createItem(XMLUtils.ElementCreator cr, Object obj) {
+        Element item  = ProtocolUtils.createArtNode(cr, "item", null, null);
+        Element label = ProtocolUtils.createArtNode(cr, "label", null, null);
+        Element value = ProtocolUtils.createArtNode(cr, "value", null, null);
+
+        String[] arr = (String[]) obj;
+
+        label.setTextContent(arr[0]);
+        value.setTextContent(arr[1]);
+
+        item.appendChild(label);
+        item.appendChild(value);
+
+        return item;
+    }
+
+
+
+    @Override
+    public void endOfLife(Artifact artifact, Object callContext) {
+        super.endOfLife(artifact, callContext);
+        logger.info("ScenarioSelect.endOfLife: " + artifact.identifier());
+
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+        removeDirectory(flys);
+    }
+
+
+    /**
+     * Removes the directory and all its content where the required data and the
+     * results of WSPLGEN are stored. Should be called in endOfLife().
+     */
+    // FIXME: I've seen this code somewhere else...
+    protected void removeDirectory(FLYSArtifact artifact) {
+        String shapePath = FLYSUtils.getXPathString(
+            FLYSUtils.XPATH_FLOODMAP_SHAPEFILE_DIR);
+
+        File artifactDir = new File(shapePath, artifact.identifier());
+
+        if (artifactDir.exists()) {
+            logger.debug("Delete directory: " + artifactDir.getAbsolutePath());
+            boolean success = FileTools.deleteRecursive(artifactDir);
+            if (!success) {
+                logger.warn("could not remove dir '" + artifactDir + "'");
+            }
+        }
+        else {
+            logger.debug("There is no directory to remove.");
+        }
+    }
+
+
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/SoundingsSelect.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,208 @@
+package org.dive4elements.river.artifacts.states;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.artifacts.common.model.KVP;
+
+import org.dive4elements.river.model.BedHeightEpoch;
+import org.dive4elements.river.model.BedHeightSingle;
+import org.dive4elements.river.model.River;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.utils.FLYSUtils;
+
+
+public class SoundingsSelect extends MultiStringArrayState {
+
+    public static final String SOUNDINGS = "soundings";
+
+    public static final String PREFIX_SINGLE = "single-";
+
+    public static final String PREFIX_EPOCH = "epoch-";
+
+    /** Private logger. */
+    private static final Logger logger = Logger.getLogger(SoundingsSelect.class);
+
+
+    @Override
+    public String getUIProvider() {
+        return "parameter-matrix";
+    }
+
+
+    @Override
+    protected KVP<String, String>[] getOptions(
+        Artifact artifact,
+        String   parameterName,
+        CallContext context
+    )
+    throws IllegalArgumentException
+    {
+        logger.debug("Get options for parameter: '" + parameterName + "'");
+
+        if (!testParameterName(parameterName)) {
+            throw new IllegalArgumentException(
+                "Invalid parameter for state: '" + parameterName + "'");
+        }
+
+        River river = FLYSUtils.getRiver((FLYSArtifact) artifact);
+        double lo = ((FLYSArtifact) artifact).getDataAsDouble("ld_from");
+        double hi = ((FLYSArtifact) artifact).getDataAsDouble("ld_to");
+
+        double kmLo = Math.min(lo, hi);
+        double kmHi = Math.max(lo, hi);
+
+        List<KVP<String, String>> kvp = new ArrayList<KVP<String, String>>();
+
+        appendSingles(river, kmLo, kmHi, kvp);
+        appendEpochs(river, kmLo, kmHi, kvp);
+
+        return kvp.toArray(new KVP[kvp.size()]);
+    }
+
+
+    protected void appendSingles(
+        River river,
+        double kmLo,
+        double kmHi,
+        List<KVP<String, String>> kvp
+    ) {
+        List<BedHeightSingle> singles =
+            BedHeightSingle.getBedHeightSingles(river, kmLo, kmHi);
+
+        if (singles != null) {
+            int size = singles.size();
+
+            logger.debug("Found " + size + " singles.");
+
+            for (int i = 0; i < size; i++) {
+                BedHeightSingle s = singles.get(i);
+
+                String id    = PREFIX_SINGLE + s.getId();
+                String value = s.getDescription();
+
+                kvp.add(new KVP(id, value));
+            }
+        }
+    }
+
+
+    protected void appendEpochs(
+        River river,
+        double kmLo,
+        double kmHi,
+        List<KVP<String, String>> kvp
+    ) {
+        List<BedHeightEpoch> epochs =
+            BedHeightEpoch.getBedHeightEpochs(river, kmLo, kmHi);
+
+        if (epochs != null) {
+            int size = epochs.size();
+
+            logger.debug("Found " + size + " epochs.");
+
+            for (int i = 0; i < size; i++) {
+                BedHeightEpoch e = epochs.get(i);
+
+                String id    = PREFIX_EPOCH + e.getId();
+                String value = e.getDescription();
+
+                kvp.add(new KVP(id, value));
+            }
+        }
+    }
+
+
+    @Override
+    protected String getLabelFor(
+        CallContext cc,
+        String      parameterName,
+        String      value
+    ) throws IllegalArgumentException
+    {
+        if (!testParameterName(parameterName)) {
+            throw new IllegalArgumentException(
+                "Invalid parameter for state: '" + parameterName + "'");
+        }
+
+        if (value.indexOf(PREFIX_SINGLE) >= 0) {
+            return getLabelForSingle(cc, value);
+        }
+        else if (value.indexOf(PREFIX_EPOCH) >= 0) {
+            return getLabelForEpoch(cc, value);
+        }
+
+        return value;
+    }
+
+
+    protected String getLabelForSingle(CallContext cc, String value) {
+        String id = value.replace(PREFIX_SINGLE, "");
+        try {
+            BedHeightSingle s = BedHeightSingle.getBedHeightSingleById(
+                Integer.parseInt(id));
+
+            if (s != null) {
+                return s.getDescription();
+            }
+            else {
+                return "no value for '" + id + "'";
+            }
+        }
+        catch (NumberFormatException nfe) {
+            logger.warn("Could not parse id from string '" + id + "'", nfe);
+        }
+
+        return "n.A.";
+    }
+
+
+    protected String getLabelForEpoch(CallContext cc, String value) {
+        String id = value.replace(PREFIX_EPOCH, "");
+        try {
+            BedHeightEpoch e = BedHeightEpoch.getBedHeightEpochById(
+                Integer.parseInt(id));
+
+            if (e != null) {
+                return e.getDescription();
+            }
+            else {
+                return "no value for '" + id + "'";
+            }
+        }
+        catch (NumberFormatException nfe) {
+            logger.warn("Could not parse id from string '" + id + "'", nfe);
+        }
+
+        return "n.A.";
+    }
+
+
+    /**
+     * This method might be used to test, if a parameter name is handled by this
+     * state.
+     *
+     * @param parameterName The name of a parameter.
+     *
+     * @return true, if parameterName is one of <i>MAIN_CHANNEL</i> or
+     * <i>TOTAL_CHANNEL</i>. Otherwise false.
+     */
+    protected boolean testParameterName(String parameterName) {
+        if (parameterName == null || parameterName.length() == 0) {
+            return false;
+        }
+        else if (parameterName.equals(SOUNDINGS)) {
+            return true;
+        }
+        else {
+            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/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/StateFactory.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,122 @@
+package org.dive4elements.river.artifacts.states;
+
+import javax.xml.xpath.XPathConstants;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import org.dive4elements.artifactdatabase.data.DefaultStateData;
+import org.dive4elements.artifactdatabase.state.State;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class StateFactory {
+
+    /** The logger used in this class */
+    private static Logger logger = Logger.getLogger(StateFactory.class);
+
+    /** The XPath to the classname of the state */
+    public static final String XPATH_STATE = "@state";
+
+    /** The XPath to the data items of the state relative to the state node. */
+    public static final String XPATH_DATA = "data";
+
+    /** The XPath to the data name relative to the data node.*/
+    public static final String XPATH_DATA_NAME = "@name";
+
+    /** The XPath to the data type relative to the data node.*/
+    public static final String XPATH_DATA_TYPE = "@type";
+
+    /** The XPath to the data description relative to the data node.*/
+    public static final String XPATH_DATA_DESCRIPTION = "@description";
+
+
+    /**
+     * Creates a new State based on the configured class provided by
+     * <code>stateConf</code>.
+     *
+     * @param stateConf The configuration of the state.
+     *
+     * @return a State.
+     */
+    public static State createState(Node stateConf) {
+        String clazz = (String) XMLUtils.xpath(
+            stateConf, XPATH_STATE, XPathConstants.STRING);
+
+        State state = null;
+
+        try {
+            logger.debug("Create a new State for class: " + clazz);
+            state = (State) Class.forName(clazz).newInstance();
+            state.setup(stateConf);
+
+            initializeStateData(state, stateConf);
+        }
+        catch (InstantiationException ie) {
+            logger.error(ie, ie);
+        }
+        catch (IllegalAccessException iae) {
+            logger.error(iae, iae);
+        }
+        catch (ClassNotFoundException cnfe) {
+            logger.error(cnfe, cnfe);
+        }
+
+        return state;
+    }
+
+
+    /**
+     * This method extracts the configured input data of a state and adds new
+     * StateData objects to the State.
+     *
+     * @param state The state.
+     * @param stateConf The state configuration node.
+     */
+    protected static void initializeStateData(State state, Node stateConf) {
+        NodeList dataList = (NodeList) XMLUtils.xpath(
+            stateConf, XPATH_DATA, XPathConstants.NODESET);
+
+        if (dataList == null || dataList.getLength() == 0) {
+            logger.debug("The state has no input data configured.");
+
+            return;
+        }
+
+        int items = dataList.getLength();
+
+        logger.debug("The state has " + items + " data items configured.");
+
+        for (int i = 0; i < items; i++) {
+            Node data = dataList.item(i);
+
+            String name = (String) XMLUtils.xpath(
+                data, XPATH_DATA_NAME, XPathConstants.STRING);
+            String type = (String) XMLUtils.xpath(
+                data, XPATH_DATA_TYPE, XPathConstants.STRING);
+            String desc = (String) XMLUtils.xpath(
+                data, XPATH_DATA_DESCRIPTION, XPathConstants.STRING);
+
+            if (name == null || name.length() == 0) {
+                logger.warn("No name for data item at pos " + i + " found.");
+                continue;
+            }
+
+            if (type == null || type.length() == 0) {
+                logger.warn("No type for data item at pos " + i + " found.");
+                logger.warn("Default type 'string' used.");
+                type = "string";
+            }
+
+            logger.debug("add StateData '" + name + "' (type '" + type + "')");
+            state.addData(name, new DefaultStateData(name, desc, type));
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/StaticHYKState.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,110 @@
+package org.dive4elements.river.artifacts.states;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.HYKArtifact;
+
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.HYKFacet;
+import org.dive4elements.river.artifacts.model.HYKFactory;
+
+/**
+ * Only state of a HYKArtifact.
+ */
+public class StaticHYKState
+extends      DefaultState
+implements   FacetTypes
+{
+    /** The logger that is used in this state. */
+    transient private static final Logger logger = Logger.getLogger(StaticHYKState.class);
+
+
+    /**
+     * From this state can not be continued.
+     */
+    @Override
+    protected String getUIProvider() {
+        return "noinput";
+    }
+
+
+    /**
+     * Compute, create Facets, do the same stuff as all the other states do.
+     */
+    protected Object compute(
+        HYKArtifact   hyk,
+        CallMeta      metaLocale,
+        String        hash,
+        List<Facet>   facets,
+        Object        old
+    ) {
+        logger.debug("StaticHYKState.compute");
+        String id = getID();
+
+        // Prepare comparison against cached result.
+        List<HYKFactory.Zone> resZones = old instanceof List
+            ? (List<HYKFactory.Zone>)old
+            : null;
+
+        // TODO Compare against cached object.
+
+        // Get Zones from HYKFactory
+        List<HYKFactory.Zone> zones = (List<HYKFactory.Zone>)
+            HYKFactory.getHYKs(hyk.getHykId(), hyk.getKm());
+
+        if (facets == null) {
+            logger.debug("StaticHYKState.compute no facets");
+            return zones;
+        }
+
+        // Spawn Facets.
+        Facet facet = new HYKFacet(0, HYKFactory.getHykName(hyk.getHykId()));
+        facets.add(facet);
+
+        return zones;
+    }
+
+
+    /**
+     * Get data, create the facets.
+     *
+     * @param context Ignored.
+     */
+    @Override
+    public Object computeFeed(
+        FLYSArtifact artifact,
+        String       hash,
+        CallContext  context,
+        List<Facet>  facets,
+        Object       old
+    ) {
+        return compute((HYKArtifact) artifact, context.getMeta(),
+            hash, facets, old);
+    }
+
+
+    /**
+     * Create the facets.
+     * @param context Ignored.
+     */
+    @Override
+    public Object computeInit(
+        FLYSArtifact artifact,
+        String       hash,
+        Object       context,
+        CallMeta     meta,
+        List<Facet>  facets
+    ) {
+        return compute((HYKArtifact) artifact, meta, hash, facets,
+            null);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/StaticState.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,134 @@
+package org.dive4elements.river.artifacts.states;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.river.artifacts.model.FacetTypes;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifactdatabase.state.DefaultOutput;
+import org.dive4elements.artifactdatabase.state.Output;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.CallMeta;
+
+/**
+ * Yet, a non-abstract DefaultState.
+ */
+public class StaticState
+extends      DefaultState
+implements   FacetTypes
+{
+    /** The logger that is used in this state. */
+    private static final Logger logger = Logger.getLogger(StaticState.class);
+
+    private static String uiprovider;
+
+
+    public StaticState() {
+        super();
+    }
+
+    /**
+     * Trivial constructor, sets id and description.
+     * @param id String used for both id and description.
+     */
+    public StaticState(String id) {
+        this(id, id);
+    }
+
+
+    public StaticState(String id, String description) {
+        super();
+        setID(id);
+        setDescription(description);
+    }
+
+    public void addDefaultChartOutput(String nameDesc, List<Facet> facets) {
+        DefaultOutput output = new DefaultOutput(nameDesc,
+            nameDesc, "image/png", facets, "chart");
+        getOutputs().add(output);
+    }
+
+    public static void addDefaultChartOutput(
+        DefaultState state,
+        String nameDesc,
+        List<Facet> facets
+    ) {
+        DefaultOutput output = new DefaultOutput(nameDesc,
+            nameDesc, "image/png", facets, "chart");
+        state.getOutputs().add(output);
+    }
+
+
+    /**
+     * Do nothing (override to include your logic).
+     * @param facets List of facets (to add to).
+     */
+    public Object staticCompute(List<Facet> facets, FLYSArtifact artifact) {
+        return staticCompute(facets);
+    }
+
+    public Object staticCompute(List<Facet> facets) {
+        return null;
+    }
+
+
+    /** Call staticCompute to allow easy adjustments. */
+    @Override
+    public Object computeAdvance(
+        FLYSArtifact artifact,
+        String       hash,
+        CallContext  context,
+        List<Facet>  facets,
+        Object       old
+    ) {
+        return staticCompute(facets, artifact);
+    }
+
+
+    /** Call staticCompute to allow easy adjustments. */
+    @Override
+    public Object computeFeed(
+        FLYSArtifact artifact,
+        String       hash,
+        CallContext  context,
+        List<Facet>  facets,
+        Object       old
+    ) {
+        return staticCompute(facets, artifact);
+    }
+
+
+    /** Call staticCompute to allow easy adjustments. */
+    @Override
+    public Object computeInit(
+        FLYSArtifact artifact,
+        String       hash,
+        Object       context,
+        CallMeta     meta,
+        List<Facet>  facets
+    ) {
+        return staticCompute(facets, artifact);
+    }
+
+    public void addOutput(Output out) {
+        super.addOutput(out);
+    }
+
+    @Override
+    protected String getUIProvider() {
+        return this.uiprovider;
+    }
+
+    /**
+     * Allow to set the uiprovider for displaying the static data
+     */
+    public void setUIProvider(String uiprovider) {
+        this.uiprovider = uiprovider;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/StaticWQKmsState.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,145 @@
+package org.dive4elements.river.artifacts.states;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.StaticWQKmsArtifact;
+
+import org.dive4elements.river.artifacts.model.CrossSectionWaterLineFacet;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.RelativePointFacet;
+import org.dive4elements.river.artifacts.model.WQKms;
+import org.dive4elements.river.artifacts.model.WQKmsFacet;
+
+/**
+ * Only state of WQKmsArtifact.
+ */
+public class StaticWQKmsState
+extends      DefaultState
+implements   FacetTypes
+{
+    /** The logger that is used in this state. */
+    private static Logger logger = Logger.getLogger(StaticWQKmsState.class);
+
+
+    /**
+     * From this state can not be continued.
+     */
+    @Override
+    protected String getUIProvider() {
+        return "noinput";
+    }
+
+
+    /**
+     * Compute, create Facets, do the same stuff as all the other states do.
+     */
+    protected Object compute(
+        StaticWQKmsArtifact winfo,
+        CallMeta      metaLocale,
+        String        hash,
+        List<Facet>   facets,
+        Object        old
+    ) {
+        String id = getID();
+
+        WQKms res = old instanceof WQKms
+            ? (WQKms)old
+            : winfo.getWQKms();
+
+        WQKms wqkms = res;
+
+        if (facets == null) {
+            return res;
+        }
+
+        /*
+         * TODO: re-enable HEIGHTMARKS_POINTS-thing
+
+           String name;
+           if (parts[0].equals(HEIGHTMARKS_POINTS)) {
+               name = HEIGHTMARKS_POINTS;
+           }
+           else {
+               name = STATIC_WQKMS;
+           }
+        */
+
+        String wkmsName = wqkms.getName();
+        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);
+
+        wkmsName = "W (" + wkmsName + ")";
+
+        Facet csFacet = new CrossSectionWaterLineFacet(0,
+             wkmsName);
+        facets.add(csFacet);
+
+        Facet wfacet = new WQKmsFacet(
+            STATIC_WQKMS_W,
+            wkmsName
+            /*
+            // TODO re-enable translations.
+            Resources.getMsg(
+                metaLocale,
+                wkmsName,
+                wkmsName)*/);
+        facets.add(wfacet);
+
+        return res;
+    }
+
+
+    /**
+     * Get data, create the facets.
+     *
+     * @param context Ignored.
+     */
+    @Override
+    public Object computeFeed(
+        FLYSArtifact artifact,
+        String       hash,
+        CallContext  context,
+        List<Facet>  facets,
+        Object       old
+    ) {
+        return compute((StaticWQKmsArtifact) artifact, context.getMeta(),
+            hash, facets, old);
+    }
+
+
+    /**
+     * Create the facets.
+     * @param context Ignored.
+     */
+    @Override
+    public Object computeInit(
+        FLYSArtifact artifact,
+        String       hash,
+        Object       context,
+        CallMeta     meta,
+        List<Facet>  facets
+    ) {
+        return compute((StaticWQKmsArtifact) artifact, meta, hash, facets,
+            null);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/UserRGDState.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,46 @@
+package org.dive4elements.river.artifacts.states;
+
+import org.w3c.dom.Element;
+
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+
+public class UserRGDState
+extends DefaultState
+{
+   @Override
+    protected String getUIProvider() {
+        return "user_rgd_panel";
+    }
+
+    @Override
+    protected Element createStaticData(
+        FLYSArtifact   flys,
+        ElementCreator creator,
+        CallContext    cc,
+        String         name,
+        String         value,
+        String         type
+    ) {
+        Element dataElement = creator.create("data");
+        creator.addAttr(dataElement, "name", name, true);
+        creator.addAttr(dataElement, "type", type, true);
+
+        Element itemElement = creator.create("item");
+        creator.addAttr(itemElement, "value", value, true);
+
+        creator.addAttr(itemElement, "label", getLabel(cc, value), true);
+        dataElement.appendChild(itemElement);
+
+        return dataElement;
+    }
+
+    public static String getLabel(CallContext cc, String value) {
+
+        return value;
+    }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/WDifferencesState.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,191 @@
+package org.dive4elements.river.artifacts.states;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifactdatabase.data.StateData;
+
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.StaticWKmsArtifact;
+import org.dive4elements.river.artifacts.WINFOArtifact;
+import org.dive4elements.river.artifacts.ChartArtifact;
+
+import org.dive4elements.river.artifacts.math.WKmsOperation;
+
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.DataFacet;
+import org.dive4elements.river.artifacts.model.DifferenceCurveFacet;
+import org.dive4elements.river.artifacts.model.EmptyFacet;
+
+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.utils.FLYSUtils;
+import org.dive4elements.river.utils.StringUtil;
+
+
+public class WDifferencesState
+extends      DefaultState
+implements   FacetTypes
+{
+    /** The logger that is used in this state. */
+    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() {
+        return "noinput";
+    }
+
+
+    @Override
+    public boolean validate(Artifact artifact)
+    throws IllegalArgumentException
+    {
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        StateData data = flys.getData("diffids");
+
+        if (data == null) {
+            throw new IllegalArgumentException("diffids is empty");
+        }
+
+        // TODO: Also validate format.
+
+        return true;
+    }
+
+
+    /**
+     * Access the data (wkms).
+     */
+    protected WKms getWKms(String mingle, CallContext context) {
+        String[] def  = mingle.split(";");
+        String   uuid = def[0];
+        String   name = def[1];
+        int      idx  = Integer.parseInt(def[2]);
+
+        if (name.startsWith("staticwkms")) {
+            StaticWKmsArtifact staticWKms =
+                (StaticWKmsArtifact) FLYSUtils.getArtifact(
+                    uuid,
+                    context);
+            logger.debug("WDifferencesState obtain data from StaticWKms");
+            WKms wkms = staticWKms.getWKms(idx);
+            if (wkms == null)
+                logger.error("No WKms from artifact.");
+            return wkms;
+        }
+
+        WINFOArtifact flys = (WINFOArtifact) FLYSUtils.getArtifact(
+            uuid,
+            context);
+
+        if (flys == null) {
+            logger.warn("One of the artifacts (1) for diff calculation could not be loaded");
+            return null;
+        }
+        else{
+            WQKms[] wqkms = (WQKms[]) flys.getWaterlevelData().
+                                              getData();
+            if (wqkms == null)
+            logger.warn("not  waterlevels in artifact");
+            else if (wqkms.length < idx)
+            logger.warn("not enough waterlevels in artifact");
+            return wqkms[idx];
+        }
+    }
+
+
+    /**
+     * Return CalculationResult with Array of WKms that are difference of
+     * Waterlevels. Add respective facets (DifferencesCurveFacet, DataFacet).
+     */
+    @Override
+    public Object computeAdvance(
+        FLYSArtifact artifact,
+        String       hash,
+        CallContext  context,
+        List<Facet>  facets,
+        Object       old
+    ) {
+        if (artifact instanceof ChartArtifact) {
+            ChartArtifact chart = (ChartArtifact)artifact;
+            facets.add(new EmptyFacet());
+            return null;
+        }
+        WINFOArtifact winfo = (WINFOArtifact) artifact;
+        String id = getID();
+
+        // Load the Artifacts/facets that we want to subtract and display.
+        // Expected format is:
+        // [42537f1e-3522-42ef-8968-635b03d8e9c6;longitudinal_section.w;0]#[1231f2-....]
+        String diffids = winfo.getDataAsString("diffids");
+        logger.debug("WDifferencesState has: " + diffids);
+        String datas[] = diffids.split("#");
+
+        // Validate the Data-Strings.
+        for (String s: datas) {
+            if (!WaterlevelSelectState.isValueValid(s)) {
+                // TODO: escalate.
+            }
+        }
+
+        if (datas.length < 2) {
+            // TODO crash with style
+        }
+
+        List<WKms> wkmss = new ArrayList<WKms>();
+
+        for(int i = 0; i < datas.length; i+=2) {
+            // e.g.:
+            // 42537f1e-3522-42ef-8968-635b03d8e9c6;longitudinal_section.w;1
+            WKms minuendWKms = getWKms(StringUtil.unbracket(datas[i+0]),
+                context);
+            WKms subtrahendWKms = getWKms(StringUtil.unbracket(datas[i+1]),
+                context);
+
+            String facetName = "diff ()";
+
+            if (minuendWKms != null && subtrahendWKms != null) {
+                facetName = StringUtil.wWrap(minuendWKms.getName())
+                    + " - " + StringUtil.wWrap(subtrahendWKms.getName());
+                WKms wkms = WKmsOperation.SUBTRACTION.operate(minuendWKms,
+                     subtrahendWKms);
+                wkms.setName(facetName);
+                wkmss.add(wkms);
+                logger.debug("WKMSSubtraction happened");
+            }
+            if (facets != null) {
+                facets.add(new DifferenceCurveFacet(i/2, W_DIFFERENCES, facetName,
+                    ComputeType.ADVANCE, id, hash));
+            }
+        }
+
+        if (facets != null) {
+            facets.add(new DataFacet(CSV, "CSV data"));
+            facets.add(new DataFacet(PDF, "PDF data"));
+            logger.debug("Adding facets in WDifferencesState.");
+        }
+        else {
+            logger.debug("Not adding facets in WDifferencesState.");
+        }
+
+        // TODO Evaluate whether null is okay as reports.
+        WKms[] diffs = wkmss.toArray(new WKms[wkmss.size()]);
+        CalculationResult result = new CalculationResult(diffs, null);
+        return result;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/WMSBackgroundState.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,156 @@
+package org.dive4elements.river.artifacts.states;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.xpath.XPathConstants;
+
+import org.apache.log4j.Logger;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.artifacts.common.utils.Config;
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.model.map.WMSLayerFacet;
+import org.dive4elements.river.artifacts.resources.Resources;
+
+
+public class WMSBackgroundState extends OutputState {
+
+    public static final String I18N_DESCRIPTION = "floodmap.wmsbackground";
+
+    public static final String XPATH_SRID =
+        "/artifact-database/floodmap/river[@name=$name]/srid/@value";
+
+    public static final String XPATH_WMS_URL =
+        "/artifact-database/floodmap/river[@name=$name]/background-wms/@url";
+
+    public static final String XPATH_WMS_LAYER =
+        "/artifact-database/floodmap/river[@name=$name]/background-wms/@layers";
+
+
+    protected String url;
+    protected String layer;
+    protected String srid;
+
+    protected Document cfg;
+
+    protected Map<String, String> variables;
+
+
+    private static final Logger logger = Logger.getLogger(WMSBackgroundState.class);
+
+
+    @Override
+    public void setup(Node config) {
+        super.setup(config);
+
+        logger.debug("WMSBackgroundState.setup()");
+    }
+
+
+    @Override
+    public Object computeInit(
+        FLYSArtifact artifact,
+        String       hash,
+        Object       context,
+        CallMeta     meta,
+        List<Facet>  facets
+    ) {
+        logger.debug("WMSBackgroundState.computeInit()");
+
+        initVariables(artifact);
+
+        if (url == null || layer == null) {
+            // XXX I don't remember why 'srid', 'url' and 'layer' are member
+            // variables. I think the reason was buffering those values.
+            srid  = getSrid();
+            url   = getUrl();
+            layer = getLayer();
+        }
+
+        if (url == null || layer == null) {
+            logger.warn("No background layers currently configured:");
+            logger.warn("... add config for WMS url: " + XPATH_WMS_URL);
+            logger.warn("... add config for WMS layer: " + XPATH_WMS_LAYER);
+            return null;
+        }
+
+        WMSLayerFacet facet = new WMSLayerFacet(
+            0,
+            getFacetType(),
+            getTitle(meta),
+            ComputeType.INIT,
+            getID(), hash,
+            url);
+
+        facet.addLayer(layer);
+        facet.setSrid(srid);
+
+        facets.add(facet);
+
+        return null;
+    }
+
+
+    protected Document getConfig() {
+        if (cfg == null) {
+            cfg = Config.getConfig();
+        }
+
+        return cfg;
+    }
+
+
+    protected void initVariables(FLYSArtifact artifact) {
+        String river = artifact.getDataAsString("river");
+
+        variables = new HashMap<String, String>();
+        variables.put("name", river);
+    }
+
+
+    protected String getFacetType() {
+        return FLOODMAP_WMSBACKGROUND;
+    }
+
+
+    protected String getSrid() {
+        return (String) XMLUtils.xpath(
+            getConfig(),
+            XPATH_SRID,
+            XPathConstants.STRING,
+            null,
+            variables);
+    }
+
+
+    protected String getUrl() {
+        return (String) XMLUtils.xpath(
+            getConfig(),
+            XPATH_WMS_URL,
+            XPathConstants.STRING,
+            null,
+            variables);
+    }
+
+
+    protected String getLayer() {
+        return (String) XMLUtils.xpath(
+            getConfig(),
+            XPATH_WMS_LAYER,
+            XPathConstants.STRING,
+            null,
+            variables);
+    }
+
+
+    protected String getTitle(CallMeta meta) {
+        return Resources.getMsg(meta, I18N_DESCRIPTION, I18N_DESCRIPTION);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/WQAdapted.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,476 @@
+package org.dive4elements.river.artifacts.states;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Comparator;
+import java.util.Collections;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Element;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.artifactdatabase.ProtocolUtils;
+import org.dive4elements.artifactdatabase.data.StateData;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+
+import org.dive4elements.river.artifacts.access.RangeAccess;
+
+import org.dive4elements.river.model.Gauge;
+import org.dive4elements.river.model.Range;
+import org.dive4elements.river.model.River;
+import org.dive4elements.river.model.Wst;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+import org.dive4elements.river.artifacts.model.RangeWithValues;
+import org.dive4elements.river.artifacts.model.WstFactory;
+import org.dive4elements.river.utils.FLYSUtils;
+
+
+/**
+ * State to input W/Q data.
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class WQAdapted extends DefaultState {
+
+    /** The logger used in this state.*/
+    private static Logger logger = Logger.getLogger(WQAdapted.class);
+
+    public static final String FIELD_WQ_MODE = "wq_isq";
+
+    public static final String FIELD_WQ_VALUES = "wq_values";
+
+    public static final class GaugeOrder implements Comparator<Gauge> {
+        private int order;
+
+        public GaugeOrder(boolean up) {
+            order = up ? 1 : -1;
+        }
+
+        public int compare(Gauge a, Gauge b) {
+            return order * a.getRange().getA().compareTo(b.getRange().getA());
+        }
+    } // class GaugeOrder
+
+    public static final GaugeOrder GAUGE_UP   = new GaugeOrder(true);
+    public static final GaugeOrder GAUGE_DOWN = new GaugeOrder(false);
+
+
+    /** Trivial, empty constructor. */
+    public WQAdapted() {
+    }
+
+
+    /**
+     * This method creates one element for each gauge of the selected river that
+     * is intersected by the given kilometer range. Each element is a tuple of
+     * (from;to) where <i>from</i> is the lower bounds of the gauge or the lower
+     * kilometer range. <i>to</i> is the upper bounds of the gauge or the upper
+     * kilometer range.
+     *
+     * @param cr The ElementCreator.
+     * @param artifact The FLYS artifact.
+     * @param name The name of the data item.
+     * @param context The CallContext.
+     *
+     * @return a list of elements that consist of tuples of the intersected
+     * gauges of the selected river.
+     */
+    @Override
+    protected Element[] createItems(
+        XMLUtils.ElementCreator cr,
+        Artifact    artifact,
+        String      name,
+        CallContext context)
+    {
+        logger.debug("WQAdapted.createItems");
+
+        if (name != null && name.equals(FIELD_WQ_MODE)) {
+            return createModeItems(cr, artifact, name, context);
+        }
+        else if (name != null && name.equals(FIELD_WQ_VALUES)) {
+            return createValueItems(cr, artifact, name, context);
+        }
+        else {
+            logger.warn("Unknown data object: " + name);
+            return null;
+        }
+    }
+
+
+    /** Creates "Q" and "W" items. */
+    protected Element[] createModeItems(
+        XMLUtils.ElementCreator cr,
+        Artifact    artifact,
+        String      name,
+        CallContext context)
+    {
+        logger.debug("WQAdapted.createModeItems");
+
+        Element w = createItem(cr, new String[] { "w", "W" });
+        Element q = createItem(cr, new String[] { "q", "Q" });
+
+        return new Element[] { w, q };
+    }
+
+
+    /** Create the items for input to the ranges per mode. */
+    protected Element[] createValueItems(
+        XMLUtils.ElementCreator cr,
+        Artifact    artifact,
+        String      name,
+        CallContext context)
+    {
+        logger.debug("WQAdapted.createValueItems");
+
+        FLYSArtifact flysArtifact = (FLYSArtifact) artifact;
+
+        RangeAccess rangeAccess = new RangeAccess(flysArtifact, context);
+        double[]    dist   = rangeAccess.getKmRange();
+        // TODO use Access to get River and gauges.
+        River       river  = FLYSUtils.getRiver(flysArtifact);
+        Wst         wst    = WstFactory.getWst(river);
+        List<Gauge> gauges = FLYSUtils.getGauges(flysArtifact);
+
+        int num = gauges != null ? gauges.size() : 0;
+
+        if (num == 0) {
+            logger.warn("Selected distance matches no gauges.");
+            return null;
+        }
+
+        List<Element> elements = new ArrayList<Element>();
+
+        double rangeFrom = dist[0];
+        double rangeTo   = dist[1];
+
+        if (rangeFrom < rangeTo) {
+            Collections.sort(gauges, GAUGE_UP);
+            for (Gauge gauge: gauges) {
+                Range range = gauge.getRange();
+                double lower = range.getA().doubleValue();
+                double upper = range.getB().doubleValue();
+
+                // If gauge out of range, skip it.
+                if (upper <= rangeFrom || lower >= rangeTo) {
+                    continue;
+                }
+
+                double from = lower < rangeFrom ? rangeFrom : lower;
+                double to   = upper > rangeTo   ? rangeTo   : upper;
+
+                double[] mmQ = determineMinMaxQ(gauge, wst);
+                double[] mmW = gauge.determineMinMaxW();
+
+                elements.add(createItem(
+                    cr, new String[] { from + ";" + to, gauge.getName()}, mmQ, mmW));
+            }
+        }
+        else {
+            Collections.sort(gauges, GAUGE_DOWN);
+            rangeFrom = dist[1];
+            rangeTo   = dist[0];
+            for (Gauge gauge: gauges) {
+                Range range = gauge.getRange();
+                double lower = range.getA().doubleValue();
+                double upper = range.getB().doubleValue();
+
+                double from = lower < rangeFrom ? rangeFrom : lower;
+                double to   = upper > rangeTo   ? rangeTo   : upper;
+
+                // TODO probably need to continue out if oof range (see above).
+
+                double[] mmQ = determineMinMaxQ(gauge, wst);
+                double[] mmW = gauge.determineMinMaxW();
+
+                elements.add(createItem(
+                    cr, new String[] { to + ";" + from, gauge.getName()}, mmQ, mmW));
+            }
+        }
+
+        Element[] els = new Element[elements.size()];
+        return elements.toArray(els);
+    }
+
+
+    protected Element createItem(XMLUtils.ElementCreator cr, Object obj) {
+        return createItem(cr, obj, null, null);
+    }
+
+
+    /** In obj: 0 is label, 1 is value. */
+    protected Element createItem(
+        XMLUtils.ElementCreator cr,
+        Object   obj,
+        double[] q,
+        double[] w)
+    {
+        Element item  = ProtocolUtils.createArtNode(cr, "item", null, null);
+        Element label = ProtocolUtils.createArtNode(cr, "label", null, null);
+        Element value = ProtocolUtils.createArtNode(cr, "value", null, null);
+
+        String[] arr = (String[]) obj;
+
+        label.setTextContent(arr[0]);
+        value.setTextContent(arr[1]);
+
+        item.appendChild(label);
+        item.appendChild(value);
+
+        if (q != null) {
+            Element qRange = createRangeElement(cr, q, "Q");
+            item.appendChild(qRange);
+        }
+
+        if (w != null) {
+            Element wRange = createRangeElement(cr, w, "W");
+            item.appendChild(wRange);
+        }
+
+        return item;
+    }
+
+
+    protected Element createRangeElement(
+        XMLUtils.ElementCreator cr,
+        double[] mm,
+        String   type)
+    {
+        Element range = ProtocolUtils.createArtNode(
+            cr, "range",
+            new String[] {"type"},
+            new String[] {type});
+
+        Element min = ProtocolUtils.createArtNode(cr, "min", null, null);
+        min.setTextContent(String.valueOf(mm[0]));
+
+        Element max = ProtocolUtils.createArtNode(cr, "max", null, null);
+        max.setTextContent(String.valueOf(mm[1]));
+
+        range.appendChild(min);
+        range.appendChild(max);
+
+        return range;
+    }
+
+
+    /**
+     * Determines the min and max Q value for the given gauge. If no min and
+     * max values could be determined, this method will return
+     * [Double.MIN_VALUE, Double.MAX_VALUE].
+     *
+     * @param gauge
+     * @param wst
+     *
+     * @return the min and max Q values for the given gauge.
+     */
+    protected double[] determineMinMaxQ(Gauge gauge, Wst wst) {
+        logger.debug("WQAdapted.determineMinMaxQ");
+
+        double[] minmaxQ = gauge != null
+            ? wst.determineMinMaxQ(gauge.getRange())
+            : null;
+
+        double minQ = minmaxQ != null ? minmaxQ[0] : Double.MIN_VALUE;
+        double maxQ = minmaxQ != null ? minmaxQ[1] : Double.MAX_VALUE;
+
+        return new double[] { minQ, maxQ };
+    }
+
+
+    /** Indicate client which input elements to use. */
+    @Override
+    protected String getUIProvider() {
+        return "wq_panel_adapted";
+    }
+
+
+    @Override
+    public boolean validate(Artifact artifact)
+    throws IllegalArgumentException
+    {
+        logger.debug("WQAdapted.validate");
+
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+        StateData    data = getData(flys, FIELD_WQ_MODE);
+
+        String mode = data != null ? (String) data.getValue() : null;
+        boolean isQ = mode != null
+            ? Boolean.valueOf(mode)
+            : false;
+
+        if (!isQ) {
+            return validateW(artifact);
+        }
+        else if (isQ) {
+            return validateQ(artifact);
+        }
+        else {
+            throw new IllegalArgumentException("error_feed_no_wq_mode_selected");
+        }
+    }
+
+
+    protected boolean validateW(Artifact artifact)
+    throws IllegalArgumentException
+    {
+        logger.debug("WQAdapted.validateW");
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        RangeWithValues[] rwvs = extractInput(getData(flys, "wq_values"));
+
+        if (rwvs == null) {
+            throw new IllegalArgumentException("error_missing_wq_data");
+        }
+
+        List<Gauge> gauges = FLYSUtils.getGauges((FLYSArtifact) artifact);
+
+        for (Gauge gauge: gauges) {
+            Range range  = gauge.getRange();
+            double lower = range.getA().doubleValue();
+            double upper = range.getB().doubleValue();
+
+            for (RangeWithValues rwv: rwvs) {
+                if (lower <= rwv.getStart() && upper >= rwv.getEnd()) {
+                    compareWsWithGauge(gauge, rwv.getValues());
+                }
+            }
+        }
+
+        return true;
+    }
+
+
+    protected boolean validateQ(Artifact artifact)
+    throws IllegalArgumentException
+    {
+        logger.debug("WQAdapted.validateQ");
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        RangeWithValues[] rwvs = extractInput(getData(flys, "wq_values"));
+
+        if (rwvs == null) {
+            throw new IllegalArgumentException("error_missing_wq_data");
+        }
+
+        List<Gauge> gauges = FLYSUtils.getGauges(flys);
+        River        river = FLYSUtils.getRiver(flys);
+        Wst            wst = WstFactory.getWst(river);
+
+        for (Gauge gauge: gauges) {
+            Range range  = gauge.getRange();
+            double lower = range.getA().doubleValue();
+            double upper = range.getB().doubleValue();
+
+            for (RangeWithValues rwv: rwvs) {
+                if (lower <= rwv.getStart() && upper >= rwv.getEnd()) {
+                    compareQsWithGauge(wst, gauge, rwv.getValues());
+                }
+            }
+        }
+
+        return true;
+    }
+
+
+    protected boolean compareQsWithGauge(Wst wst, Gauge gauge, double[] qs)
+    throws IllegalArgumentException
+    {
+        double[] minmax = gauge != null
+            ? wst.determineMinMaxQ(gauge.getRange())
+            : null;
+
+        if (minmax == null) {
+            logger.warn("Could not determine min/max Q of gauge.");
+            return true;
+        }
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("Validate Qs with:");
+            logger.debug("-- Gauge: " + gauge.getName());
+            logger.debug("-- Gauge min: " + minmax[0]);
+            logger.debug("-- Gauge max: " + minmax[1]);
+        }
+
+        for (double q: qs) {
+            if (q < minmax[0] || q > minmax[1]) {
+                throw new IllegalArgumentException(
+                    "error_feed_q_values_invalid");
+            }
+        }
+
+        return true;
+    }
+
+
+    protected boolean compareWsWithGauge(Gauge gauge, double[] ws)
+    throws IllegalArgumentException
+    {
+        double[] minmax = gauge != null
+            ? gauge.determineMinMaxW()
+            : null;
+
+        if (minmax == null) {
+            logger.warn("Could not determine min/max W of gauge.");
+            return true;
+        }
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("Validate Ws with:");
+            logger.debug("-- Gauge: " + gauge.getName());
+            logger.debug("-- Gauge min: " + minmax[0]);
+            logger.debug("-- Gauge max: " + minmax[1]);
+        }
+
+        for (double w: ws) {
+            if (w < minmax[0] || w > minmax[1]) {
+                throw new IllegalArgumentException(
+                    "error_feed_w_values_invalid");
+            }
+        }
+
+        return true;
+    }
+
+
+    protected RangeWithValues[] extractInput(StateData data) {
+        if (data == null) {
+            return null;
+        }
+
+        String dataString = (String) data.getValue();
+        String[]   ranges = dataString.split(":");
+
+        List<RangeWithValues> rwv = new ArrayList<RangeWithValues>();
+
+        for (String range: ranges) {
+            String[] parts = range.split(";");
+
+            double lower = Double.parseDouble(parts[0]);
+            double upper = Double.parseDouble(parts[1]);
+
+            String[] values = parts[3].split(",");
+
+            int      num = values.length;
+            double[] res = new double[num];
+
+            for (int i = 0; i < num; i++) {
+                try {
+                    res[i] = Double.parseDouble(values[i]);
+                }
+                catch (NumberFormatException nfe) {
+                    logger.warn(nfe, nfe);
+                }
+            }
+
+            rwv.add(new RangeWithValues(lower, upper, res));
+        }
+
+        return rwv.toArray(new RangeWithValues[rwv.size()]);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/WQSelect.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,767 @@
+package org.dive4elements.river.artifacts.states;
+
+import java.text.NumberFormat;
+
+import gnu.trove.TDoubleArrayList;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Element;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator;
+
+import org.dive4elements.artifactdatabase.ProtocolUtils;
+import org.dive4elements.artifactdatabase.data.StateData;
+
+import org.dive4elements.river.model.Gauge;
+import org.dive4elements.river.model.River;
+import org.dive4elements.river.model.Wst;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.WINFOArtifact;
+
+import org.dive4elements.river.artifacts.model.WstFactory;
+import org.dive4elements.river.artifacts.model.WstValueTable;
+import org.dive4elements.river.artifacts.resources.Resources;
+
+import org.dive4elements.river.utils.FLYSUtils;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class WQSelect extends DefaultState {
+
+    /** The logger used in this class. */
+    private static Logger logger = Logger.getLogger(WQSelect.class);
+
+    /** The default step width for Qs. */
+    public static final String DEFAULT_STEP_Q = "50";
+
+    /** The default step width for Qs. */
+    public static final String DEFAULT_STEP_W = "30";
+
+    /** The max number of steps for Qs and Ws. */
+    public static final int MAX_STEPS = 30;
+
+    /** The name of the 'mode' field. */
+    public static final String WQ_MODE = "wq_isq";
+
+    /** Them name fo the 'free' field. */
+    public static final String WQ_FREE = "wq_isfree";
+
+    /** The name of the 'selection' field. */
+    public static final String WQ_SELECTION = "wq_isrange";
+
+    /** The name of the 'from' field. */
+    public static final String WQ_FROM = "wq_from";
+
+    /** The name of the 'to' field. */
+    public static final String WQ_TO = "wq_to";
+
+    /** The name of the 'step' field. */
+    public static final String WQ_STEP = "wq_step";
+
+    /** The name of the 'single' field. */
+    public static final String WQ_SINGLE = "wq_single";
+
+
+    /**
+     * The default constructor that initializes an empty State object.
+     */
+    public WQSelect() {
+    }
+
+
+    @Override
+    protected Element createStaticData(
+        FLYSArtifact   flys,
+        ElementCreator creator,
+        CallContext    cc,
+        String         name,
+        String         value,
+        String         type
+    ) {
+        if (!name.equals(WQ_SINGLE)) {
+            return super.createStaticData(flys, creator, cc, name, value, type);
+        }
+
+        Boolean isQ = flys.getDataAsBoolean(WQ_MODE);
+        Boolean isFree = flys.getDataAsBoolean(WQ_FREE);
+
+        WINFOArtifact winfo = (WINFOArtifact) flys;
+
+        Element dataElement = creator.create("data");
+        creator.addAttr(dataElement, "name", name, true);
+        creator.addAttr(dataElement, "type", type, true);
+
+        Element itemElement = creator.create("item");
+        creator.addAttr(itemElement, "value", value, true);
+
+        String label;
+
+        if (!isQ || isFree) {
+            label = getLabel(winfo, cc, value);
+        }
+        else {
+            label = getSpecialLabel(winfo, cc, value);
+        }
+
+        creator.addAttr(itemElement, "label", label, true);
+
+        dataElement.appendChild(itemElement);
+
+        return dataElement;
+    }
+
+
+    protected static String getLabel(
+        WINFOArtifact winfo,
+        CallContext   cc,
+        String        raw
+    ) {
+        String[] values = raw.split(" ");
+
+        if (values.length < 1) {
+            return null;
+        }
+
+        StringBuilder label = new StringBuilder();
+
+        NumberFormat nf = NumberFormat.getInstance(
+            Resources.getLocale(cc.getMeta()));
+
+        for (String value: values) {
+            try {
+                double v = Double.parseDouble(value.trim());
+
+                String formatted = nf.format(v);
+
+                if (label.length() > 0) {
+                    label.append(';');
+                }
+                label.append(formatted);
+            }
+            catch (NumberFormatException nfe) {
+                // do nothing here
+            }
+        }
+
+        return label.toString();
+    }
+
+
+    protected static String getSpecialLabel(
+        WINFOArtifact winfo,
+        CallContext   cc,
+        String        raw
+    ) {
+        String[] values = raw.split(" ");
+
+        if (values.length < 1) {
+            return null;
+        }
+
+        NumberFormat nf = NumberFormat.getInstance(
+            Resources.getLocale(cc.getMeta()));
+
+        Gauge gauge = winfo.getGauge();
+
+        boolean debug = logger.isDebugEnabled();
+
+        StringBuilder label = new StringBuilder();
+
+        for (String value: values) {
+            try {
+                double v = Double.parseDouble(value.trim());
+
+                String tmp = nf.format(v);
+                String mv  = FLYSUtils.getNamedMainValue(gauge, v);
+
+                if (mv != null && mv.length() > 0) {
+                    tmp = mv + ": " + tmp;
+                    if (debug) {
+                        logger.debug("Add main value: '" + mv + "'");
+                    }
+                }
+                if (label.length() > 0) {
+                    label.append(';');
+                }
+                label.append(tmp);
+            }
+            catch (NumberFormatException nfe) {
+                // do nothing here
+            }
+        }
+
+        return label.toString();
+    }
+
+
+    @Override
+    protected Element createData(
+        XMLUtils.ElementCreator cr,
+        Artifact    artifact,
+        StateData   data,
+        CallContext context)
+    {
+        Element select = ProtocolUtils.createArtNode(
+            cr, "select", null, null);
+
+        cr.addAttr(select, "name", data.getName(), true);
+
+        Element label = ProtocolUtils.createArtNode(
+            cr, "label", null, null);
+
+        // XXX: DEAD CODE
+        /*
+        Element choices = ProtocolUtils.createArtNode(
+            cr, "choices", null, null);
+        */
+
+        label.setTextContent(Resources.getMsg(
+            context.getMeta(),
+            data.getName(),
+            data.getName()));
+
+        select.appendChild(label);
+
+        return select;
+    }
+
+
+    @Override
+    protected Element[] createItems(
+        XMLUtils.ElementCreator cr,
+        Artifact    artifact,
+        String      name,
+        CallContext context)
+    {
+        double[] minmaxW     = determineMinMaxW(artifact);
+        double[] minmaxWFree = determineMinMaxWFree(artifact);
+        double[] minmaxQ     = determineMinMaxQAtGauge(artifact);
+        double[] minmaxQFree = determineMinMaxQ(artifact);
+
+        if (name.equals("wq_from")) {
+            Element minW = createItem(cr, new String[] {
+                "minW",
+                String.valueOf(minmaxW[0])});
+
+            Element minQ = createItem(cr, new String[] {
+                "minQ",
+                String.valueOf(minmaxQ[0])});
+
+            Element minQFree = createItem(cr, new String[] {
+                "minQFree",
+                String.valueOf(minmaxQFree[0])});
+
+            Element minWFree = createItem(cr, new String[] {
+                "minWFree",
+                String.valueOf(minmaxWFree[0])});
+
+            return new Element[] { minW, minQ, minQFree, minWFree };
+        }
+        else if (name.equals("wq_to")) {
+            Element maxW = createItem(cr, new String[] {
+                "maxW",
+                String.valueOf(minmaxW[1])});
+
+            Element maxQ = createItem(cr, new String[] {
+                "maxQ",
+                String.valueOf(minmaxQ[1])});
+
+            Element maxQFree = createItem(cr, new String[] {
+                "maxQFree",
+                String.valueOf(minmaxQFree[1])});
+
+            Element maxWFree = createItem(cr, new String[] {
+                "maxWFree",
+                String.valueOf(minmaxWFree[1])});
+
+            return new Element[] { maxW, maxQ, maxQFree, maxWFree };
+        }
+        else {
+            Element stepW = createItem(
+                cr, new String[] {
+                    "stepW",
+                    String.valueOf(getStepsW(minmaxW[0], minmaxW[1]))});
+            Element stepQ = createItem(
+                cr, new String[] {
+                    "stepQ",
+                    String.valueOf(getStepsQ(minmaxQ[0], minmaxQ[1]))});
+            Element stepQFree = createItem(
+                cr, new String[] {
+                    "stepQFree",
+                    String.valueOf(getStepsQ(minmaxQFree[0], minmaxQFree[1]))});
+            Element stepWFree = createItem(
+                cr, new String[] {
+                    "stepWFree",
+                    String.valueOf(getStepsW(minmaxWFree[0], minmaxWFree[1]))});
+
+            return new Element[] { stepW, stepQ, stepQFree, stepWFree };
+        }
+    }
+
+
+    protected static double getStepsW(double min, double max) {
+        double diff = min < max ? max - min : min - max;
+        double step = diff / MAX_STEPS;
+
+        if (step < 10) {
+            return getSteps(step, 1);
+        }
+        else if (step < 100) {
+            return getSteps(step, 10);
+        }
+        else if (step < 1000) {
+            return getSteps(step, 100);
+        }
+        else {
+            return step;
+        }
+    }
+
+
+    protected static double getStepsQ(double min, double max) {
+        double diff = min < max ? max - min : min - max;
+        double step = diff / MAX_STEPS;
+
+        if (step < 10) {
+            return getSteps(step, 1);
+        }
+        else if (step < 100) {
+            return getSteps(step, 10);
+        }
+        else if (step < 1000) {
+            return getSteps(step, 100);
+        }
+        else {
+            return step;
+        }
+    }
+
+
+    protected static double getSteps(double steps, double factor) {
+        int    fac  = (int) (steps / factor);
+        double diff = steps - fac * factor;
+
+        if (diff == 0) {
+            return steps;
+        }
+
+        return factor * (fac + 1);
+    }
+
+
+    protected Element createItem(XMLUtils.ElementCreator cr, Object obj) {
+        Element item  = ProtocolUtils.createArtNode(cr, "item", null, null);
+        Element label = ProtocolUtils.createArtNode(cr, "label", null, null);
+        Element value = ProtocolUtils.createArtNode(cr, "value", null, null);
+
+        String[] arr = (String[]) obj;
+
+        label.setTextContent(arr[0]);
+        value.setTextContent(arr[1]);
+
+        item.appendChild(label);
+        item.appendChild(value);
+
+        return item;
+    }
+
+
+    @Override
+    protected String getUIProvider() {
+        return "wq_panel";
+    }
+
+
+    /**
+     * Determines the min and max W value for the current gauge. If no min and
+     * max values could be determined, this method will return
+     * [Double.MIN_VALUE, Double.MAX_VALUE].
+     *
+     * @param artifact The FLYSArtifact.
+     *
+     * @return the min and max W values for the current gauge.
+     */
+    protected double[] determineMinMaxW(Artifact artifact) {
+        logger.debug("WQSelect.determineCurrentGauge");
+
+        Gauge    gauge   = ((WINFOArtifact) artifact).getGauge();
+        double[] minmaxW = gauge != null ? gauge.determineMinMaxW() : null;
+
+        double minW = minmaxW != null ? minmaxW[0] : Double.MIN_VALUE;
+        double maxW = minmaxW != null ? minmaxW[1] : Double.MAX_VALUE;
+
+        return new double[] { minW, maxW };
+    }
+
+
+    /**
+     * Determines the min and max W value. If no min and
+     * max values could be determined, this method will return
+     * [Double.MIN_VALUE, Double.MAX_VALUE].
+     *
+     * @param artifact The FLYSArtifact.
+     *
+     * @return the min and max W values.
+     */
+    protected double[] determineMinMaxWFree(Artifact artifact) {
+        logger.debug("WQSelect.determineMinMaxWFree");
+
+        WINFOArtifact winfo = (WINFOArtifact) artifact;
+        WstValueTable valueTable = winfo.getWstValueTable();
+
+        double[] minmaxW = null;
+        if(valueTable != null) {
+            double[] km = null;
+            if(winfo.isRange()) {
+                km = winfo.getFromToStep();
+                // Use the start km to determine the min max values.
+                minmaxW = valueTable.getMinMaxW(km[0]);
+            }
+            else {
+                km = winfo.getKms();
+                minmaxW = valueTable.getMinMaxW(km[0]);
+            }
+        }
+        return minmaxW != null
+            ? minmaxW
+            : new double[] { Double.MIN_VALUE, Double.MAX_VALUE };
+    }
+
+
+    /**
+     * Determines the min and max Q value for the current gauge. If no min and
+     * max values could be determined, this method will return
+     * [Double.MIN_VALUE, Double.MAX_VALUE].
+     *
+     * @param artifact The FLYSArtifact.
+     *
+     * @return the min and max Q values for the current gauge.
+     */
+    protected double[] determineMinMaxQAtGauge(Artifact artifact) {
+        logger.debug("WQSelect.determineMinMaxQAtGauge");
+
+        WINFOArtifact flysArtifact = (WINFOArtifact) artifact;
+
+        River river = FLYSUtils.getRiver(flysArtifact);
+        Gauge gauge = flysArtifact.getGauge();
+        Wst   wst   = WstFactory.getWst(river);
+
+        double[] minmaxQ = gauge != null
+            ? wst.determineMinMaxQ(gauge.getRange())
+            : null;
+
+        double minQ = minmaxQ != null ? minmaxQ[0] : Double.MIN_VALUE;
+        double maxQ = minmaxQ != null ? minmaxQ[1] : Double.MAX_VALUE;
+
+        return new double[] { minQ, maxQ };
+    }
+
+
+    /**
+     * Determines the min and max Q value for the current kilometer range. If no
+     * min and max values could be determined, this method will return
+     *
+     * @param artifact The FLYSArtifact.
+     *
+     * @return the min and max Q values for the current kilometer range.
+     */
+    protected double[] determineMinMaxQ(Artifact artifact) {
+        logger.debug("WQSelect.determineMinMaxQ");
+
+        WINFOArtifact winfo = (WINFOArtifact) artifact;
+        WstValueTable valueTable = winfo.getWstValueTable();
+
+        double[] minmaxQ = null;
+        if(valueTable != null) {
+            double[] km = null;
+            if(winfo.isRange()) {
+                km = winfo.getFromToStep();
+                minmaxQ = valueTable.getMinMaxQ(km[0], km[1], km[2]);
+            }
+            else {
+                km = winfo.getKms();
+                minmaxQ = valueTable.getMinMaxQ(km[0]);
+                for (int i = 1; i < km.length; i++) {
+                    double[] tmp = valueTable.getMinMaxQ(km[i]);
+                    if(tmp[0] < minmaxQ[0]) {
+                        minmaxQ[0] = tmp[0];
+                    }
+                    if(tmp[1] > minmaxQ[1]) {
+                        minmaxQ[1] = tmp[1];
+                    }
+                }
+            }
+        }
+        return minmaxQ != null
+            ? minmaxQ
+            : new double[] { Double.MIN_VALUE, Double.MAX_VALUE };
+    }
+
+
+    @Override
+    public boolean validate(Artifact artifact)
+    throws IllegalArgumentException
+    {
+        logger.debug("WQSelect.validate");
+
+        WINFOArtifact flys = (WINFOArtifact) artifact;
+
+        StateData data       = getData(flys, WQ_SELECTION);
+        boolean isRange = data != null
+            ? Boolean.valueOf((String) data.getValue())
+            : false;
+
+
+
+        if (!isRange) {
+            return validateSingle(artifact);
+        }
+        else {
+            return validateRange(artifact);
+        }
+    }
+
+
+    protected boolean validateBounds(
+        double fromValid, double toValid,
+        double from,      double to,      double step)
+    throws IllegalArgumentException
+    {
+        logger.debug("RangeState.validateRange");
+
+        if (from < fromValid) {
+            logger.error(
+                "Invalid 'from'. " + from + " is smaller than " + fromValid);
+            throw new IllegalArgumentException("error_feed_from_out_of_range");
+        }
+        else if (to > toValid) {
+            logger.error(
+                "Invalid 'to'. " + to + " is bigger than " + toValid);
+            throw new IllegalArgumentException("error_feed_to_out_of_range");
+        }
+
+        return true;
+    }
+
+
+    protected boolean validateSingle(Artifact artifact)
+    throws    IllegalArgumentException
+    {
+        logger.debug("WQSelect.validateSingle");
+
+        WINFOArtifact flys = (WINFOArtifact) artifact;
+        StateData    data = getData(flys, WQ_SINGLE);
+
+        String tmp = data != null ? (String) data.getValue() : null;
+
+        if (tmp == null || tmp.length() == 0) {
+            throw new IllegalArgumentException("error_empty_state");
+        }
+
+        String[] strValues = tmp.split(" ");
+        TDoubleArrayList all = new TDoubleArrayList();
+
+        for (String strValue: strValues) {
+            try {
+                all.add(Double.parseDouble(strValue));
+            }
+            catch (NumberFormatException nfe) {
+                logger.warn(nfe, nfe);
+            }
+        }
+
+        all.sort();
+
+        FLYSUtils.WQ_MODE mode = FLYSUtils.getWQMode(flys);
+
+        logger.debug("WQ Mode: " + mode);
+
+        double[] minmax = null;
+
+        if (mode == FLYSUtils.WQ_MODE.WGAUGE) {
+            minmax = determineMinMaxW(artifact);
+        }
+        else if (mode == FLYSUtils.WQ_MODE.QGAUGE) {
+            minmax = determineMinMaxQAtGauge(artifact);
+        }
+        else if (mode == FLYSUtils.WQ_MODE.QFREE) {
+            minmax = determineMinMaxQ(artifact);
+        }
+        else {
+            minmax = determineMinMaxWFree(artifact);
+        }
+
+        double min = all.get(0);
+        double max = all.get(all.size()-1);
+
+        logger.debug("Inserted min value = " + min);
+        logger.debug("Inserted max value = " + max);
+
+        return validateBounds(minmax[0], minmax[1], min, max, 0d);
+    }
+
+
+    protected boolean validateRange(Artifact artifact)
+    throws    IllegalArgumentException
+    {
+        logger.debug("WQSelect.validateRange");
+
+        WINFOArtifact     flys = (WINFOArtifact) artifact;
+        FLYSUtils.WQ_MODE mode = FLYSUtils.getWQMode(flys);
+
+        if (mode == null) {
+            throw new IllegalArgumentException("error_feed_invalid_wq_mode");
+        }
+
+        StateData dFrom = flys.getData(WQ_FROM);
+        StateData dTo   = flys.getData(WQ_TO);
+        StateData dStep = flys.getData(WQ_STEP);
+
+        String fromStr = dFrom != null ? (String) dFrom.getValue() : null;
+        String toStr   = dTo != null ? (String) dTo.getValue() : null;
+        String stepStr = dStep != null ? (String) dStep.getValue() : null;
+
+        if (fromStr == null || toStr == null || stepStr == null) {
+            throw new IllegalArgumentException("error_empty_state");
+        }
+
+        try {
+            double from = Double.parseDouble(fromStr);
+            double to   = Double.parseDouble(toStr);
+            double step = Double.parseDouble(stepStr);
+
+            if (mode == FLYSUtils.WQ_MODE.WGAUGE) {
+                return validateGaugeW(artifact, from, to, step);
+            }
+            else if (mode == FLYSUtils.WQ_MODE.QGAUGE) {
+                return validateGaugeQ(artifact, from, to, step);
+            }
+            else if (mode == FLYSUtils.WQ_MODE.QFREE) {
+                return validateFreeQ(artifact, from, to, step);
+            }
+            else if (mode == FLYSUtils.WQ_MODE.WFREE) {
+                return validateFreeW(artifact, from, to, step);
+            }
+            else {
+                throw new IllegalArgumentException(
+                    "error_feed_invalid_wq_mode");
+            }
+        }
+        catch (NumberFormatException nfe) {
+            throw new IllegalArgumentException("error_feed_number_format");
+        }
+    }
+
+
+    /**
+     * Validates the inserted W values.
+     *
+     * @param artifact The owner artifact.
+     * @param from The lower value of the W range.
+     * @param to The upper value of the W range.
+     * @param step The step width.
+     *
+     * @return true, if everything was fine, otherwise an exception is thrown.
+     */
+    protected boolean validateGaugeW(
+        Artifact    artifact,
+        double from,
+        double to,
+        double step)
+    throws    IllegalArgumentException
+    {
+        logger.debug("WQSelect.validateGaugeW");
+
+        double[] minmaxW = determineMinMaxW(artifact);
+
+        return validateBounds(minmaxW[0], minmaxW[1], from, to, step);
+    }
+
+
+    /**
+     * Validates the inserted Q values based on the Q range for the current
+     * gauge.
+     *
+     * @param artifact The owner artifact.
+     * @param from The lower value of the Q range.
+     * @param to The upper value of the Q range.
+     * @param step The step width.
+     *
+     * @return true, if everything was fine, otherwise an exception is thrown.
+     */
+    protected boolean validateGaugeQ(
+        Artifact artifact,
+        double   from,
+        double   to,
+        double   step)
+    throws IllegalArgumentException
+    {
+        logger.debug("WQSelect.validateGaugeQ");
+
+        double[] minmaxQ = determineMinMaxQAtGauge(artifact);
+
+        return validateBounds(minmaxQ[0], minmaxQ[1], from, to, step);
+    }
+
+
+    /**
+     * Validates the inserted Q values based on the Q range for the current
+     * kilometer range.
+     *
+     * @param artifact The owner artifact.
+     * @param from The lower value of the Q range.
+     * @param to The upper value of the Q range.
+     * @param step The step width.
+     *
+     * @return true, if everything was fine, otherwise an exception is thrown.
+     */
+    protected boolean validateFreeQ(
+        Artifact artifact,
+        double   from,
+        double   to,
+        double   step)
+    throws IllegalArgumentException
+    {
+        logger.debug("WQSelect.validateFreeQ");
+
+        double[] minmaxQ = determineMinMaxQ(artifact);
+
+        return validateBounds(minmaxQ[0], minmaxQ[1], from, to, step);
+    }
+
+
+    /**
+     * Validates the inserted W values based on the W range for the current
+     * kilometer range.
+     *
+     * @param artifact The owner artifact.
+     * @param from The lower value of the W range.
+     * @param to The upper value of the W range.
+     * @param step The step width.
+     *
+     * @return true, if everything was fine, otherwise an exception is thrown.
+     */
+    protected boolean validateFreeW(
+        Artifact artifact,
+        double   from,
+        double   to,
+        double   step)
+    throws IllegalArgumentException
+    {
+        logger.debug("WQSelect.validateFreeW");
+
+        double[] minmaxW = determineMinMaxWFree(artifact);
+
+        return validateBounds(minmaxW[0], minmaxW[1], from, to, step);
+    }
+
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/WaterlevelGroundDifferences.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,157 @@
+package org.dive4elements.river.artifacts.states;
+
+import org.w3c.dom.Element;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+
+import org.dive4elements.artifactdatabase.data.StateData;
+import org.dive4elements.artifactdatabase.ProtocolUtils;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class WaterlevelGroundDifferences extends RangeState {
+
+    public static final String LOWER_FIELD  = "diff_from";
+    public static final String UPPER_FIELD  = "diff_to";
+    public static final String DIFF_FIELD   = "diff_diff";
+
+    public static final double DEFAULT_STEP = 0d;
+
+
+    private static Logger logger =
+        Logger.getLogger(WaterlevelGroundDifferences.class);
+
+
+    protected String getLowerField() {
+        return LOWER_FIELD;
+    }
+
+
+    protected String getUpperField() {
+        return UPPER_FIELD;
+    }
+
+
+    protected String getStepField() {
+        return DIFF_FIELD;
+    }
+
+
+    @Override
+    protected double[] getMinMax(Artifact artifact) {
+        return new double[] { -Double.MAX_VALUE, Double.MAX_VALUE };
+    }
+
+
+    @Override
+    protected String getUIProvider() {
+        return "waterlevel_ground_panel";
+    }
+
+
+    protected double getDefaultStep() {
+        return DEFAULT_STEP;
+    }
+
+
+    @Override
+    protected Element[] createItems(
+        XMLUtils.ElementCreator cr,
+        Artifact    artifact,
+        String      name,
+        CallContext context)
+    {
+        double[] minmax = getMinMax(artifact);
+
+        double minVal = Double.MIN_VALUE;
+        double maxVal = Double.MAX_VALUE;
+
+        if (minmax != null) {
+            minVal = minmax[0];
+            maxVal = minmax[1];
+        }
+        else {
+            logger.warn("Could not read min/max distance values!");
+        }
+
+        if (name.equals(LOWER_FIELD)) {
+            Element min = createItem(
+                cr,
+                new String[] {"min", new Double(minVal).toString()});
+
+            return new Element[] { min };
+        }
+        else if (name.equals(UPPER_FIELD)) {
+            Element max = createItem(
+                cr,
+                new String[] {"max", new Double(maxVal).toString()});
+
+            return new Element[] { max };
+        }
+        else {
+            Element step = createItem(
+                cr,
+                new String[] {"step", String.valueOf(getDefaultStep())});
+            return new Element[] { step };
+        }
+    }
+
+
+    protected Element createItem(XMLUtils.ElementCreator cr, Object obj) {
+        Element item  = ProtocolUtils.createArtNode(cr, "item", null, null);
+        Element label = ProtocolUtils.createArtNode(cr, "label", null, null);
+        Element value = ProtocolUtils.createArtNode(cr, "value", null, null);
+
+        String[] arr = (String[]) obj;
+
+        label.setTextContent(arr[0]);
+        value.setTextContent(arr[1]);
+
+        item.appendChild(label);
+        item.appendChild(value);
+
+        return item;
+    }
+
+    @Override
+    public boolean validate(Artifact artifact)
+    throws IllegalArgumentException
+    {
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        StateData dFrom = getData(flys, getLowerField());
+        StateData dTo   = getData(flys, getUpperField());
+        StateData dStep = getData(flys, getStepField());
+
+        String fromStr = dFrom != null ? (String) dFrom.getValue() : null;
+        String toStr   = dTo   != null ? (String) dTo.getValue()   : null;
+        String stepStr = dStep != null ? (String) dStep.getValue() : null;
+
+        if (fromStr == null || toStr == null || stepStr == null) {
+            throw new IllegalArgumentException("error_empty_state");
+        }
+
+        try {
+            double from = Double.parseDouble(fromStr);
+            double to   = Double.parseDouble(toStr);
+            double step = Double.parseDouble(stepStr);
+
+            double[] minmax = getMinMax(flys);
+
+            return validateBounds(minmax[0], minmax[1], from, to, step);
+        }
+        catch (NumberFormatException nfe) {
+            throw new IllegalArgumentException("error_invalid_double_value");
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/WaterlevelInfoState.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,145 @@
+package org.dive4elements.river.artifacts.states;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.WINFOArtifact;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.ReportFacet;
+import org.dive4elements.river.artifacts.model.WaterlevelFacet;
+import org.dive4elements.river.artifacts.model.WQKms;
+
+import org.dive4elements.river.artifacts.model.DataFacet;
+import org.dive4elements.river.artifacts.model.CrossSectionWaterLineFacet;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+
+
+public class WaterlevelInfoState
+extends      DefaultState
+implements   FacetTypes
+{
+    /** The logger that is used in this state. */
+    private static Logger logger = Logger.getLogger(WaterlevelInfoState.class);
+
+
+    @Override
+    protected String getUIProvider() {
+        return "noinput";
+    }
+
+
+    @Override
+    public Object computeInit(
+        FLYSArtifact artifact,
+        String       hash,
+        Object       context,
+        CallMeta     meta,
+        List<Facet>  facets
+    ) {
+        return compute((WINFOArtifact) artifact, hash, facets, null);
+    }
+
+
+    protected Object compute(
+        WINFOArtifact winfo,
+        String        hash,
+        List<Facet>   facets,
+        Object        old
+    ) {
+        logger.debug("WaterlevelInfoState.compute");
+        String id = getID();
+
+        CalculationResult res = old instanceof CalculationResult
+            ? (CalculationResult)old
+            : winfo.getWaterlevelData();
+
+        if (facets == null) {
+            return res;
+        }
+
+        WQKms [] wqkms = (WQKms [])res.getData();
+
+        for (int i = 0; i < wqkms.length; i++) {
+            String nameW = null;
+            String nameQ = null;
+
+            if (winfo.isQ()) {
+                nameQ = wqkms[i].getName();
+                nameW = "W(" + nameQ + ")";
+            }
+            else {
+                nameW = wqkms[i].getName();
+                nameQ = "Q(" + nameQ + ")";
+            }
+
+            logger.debug("WaterlevelInfoState Create facet: " + nameW);
+            logger.debug("WaterlevelInfoState Create facet: " + nameQ);
+
+            Facet w = new WaterlevelFacet(
+                i, LONGITUDINAL_W, nameW, ComputeType.ADVANCE, id, hash);
+            Facet q = new WaterlevelFacet(
+                i, LONGITUDINAL_Q, nameQ, ComputeType.ADVANCE, id, hash);
+
+            facets.add(w);
+            facets.add(q);
+        }
+
+        if (wqkms.length > 0) {
+            Facet wst = new DataFacet(
+                WST, "WST data", ComputeType.ADVANCE, hash, id);
+            Facet csv = new DataFacet(
+                CSV, "CSV data", ComputeType.ADVANCE, hash, id);
+
+            facets.add(wst);
+            facets.add(csv);
+        }
+
+        if (res.getReport().hasProblems()) {
+            facets.add(new ReportFacet(ComputeType.ADVANCE, hash, id));
+        }
+
+        // TODO Adjust to WaterlevelState - implementation.
+        facets.add(new CrossSectionWaterLineFacet(0, "Q=" + winfo.getDataAsString("wq_single")));
+
+        // Assume to be in wq_single mode.
+        return res;
+    }
+
+
+    /**
+     * @param context Ignored.
+     */
+    @Override
+    public Object computeFeed(
+        FLYSArtifact artifact,
+        String       hash,
+        CallContext  context,
+        List<Facet>  facets,
+        Object       old
+    ) {
+        return compute((WINFOArtifact) artifact, hash, facets, old);
+    }
+
+
+    /**
+     * @param context Ignored.
+     */
+    @Override
+    public Object computeAdvance(
+        FLYSArtifact artifact,
+        String       hash,
+        CallContext  context,
+        List<Facet>  facets,
+        Object       old
+    ) {
+        return compute((WINFOArtifact) artifact, hash, facets, old);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/WaterlevelPairSelectState.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,162 @@
+package org.dive4elements.river.artifacts.states;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Element;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.artifactdatabase.ProtocolUtils;
+import org.dive4elements.artifactdatabase.state.Facet;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.resources.Resources;
+
+import org.dive4elements.river.utils.StringUtil;
+
+/**
+ * State in which the user selects 1 to n pairs of Waterlevels and alikes.
+ */
+public class WaterlevelPairSelectState
+extends      DefaultState
+implements   FacetTypes
+{
+    /** The logger that is used in this state. */
+    private static Logger logger = Logger.getLogger(
+         WaterlevelPairSelectState.class);
+
+
+    /** Trivial constructor. */
+    public WaterlevelPairSelectState() {
+    }
+
+
+    /** Specify to display a datacage_twin_panel. */
+    @Override
+    protected String getUIProvider() {
+        return "datacage_twin_panel";
+    }
+
+
+    /**
+     * Overridden to do nothing.
+     */
+    @Override
+    public Object computeAdvance(
+        FLYSArtifact artifact,
+        String       hash,
+        CallContext  context,
+        List<Facet>  facets,
+        Object       old
+    ) {
+        //Get data and do stuff, do not calculate
+        return "";
+    }
+
+
+    /**
+     * Create elements for document (prepopulated with data, if any).
+     * @param artifact FLYSArtifact to get data from.
+     * @param name DataName, expceted to be "diffids".
+     */
+    @Override
+    protected Element[] createItems(
+        ElementCreator cr,
+        Artifact    artifact,
+        String      name,
+        CallContext context)
+    {
+        logger.debug("createItems: " + name);
+        if (name.equals("diffids")) {
+            Element item  = ProtocolUtils.createArtNode(cr, "item", null, null);
+            Element label = ProtocolUtils.createArtNode(cr, "label", null, null);
+            Element value = ProtocolUtils.createArtNode(cr, "value", null, null);
+            FLYSArtifact flys = (FLYSArtifact) artifact;
+            String s = flys.getDataAsString("diffids");
+            value.setTextContent(s);
+            item.appendChild(label);
+            item.appendChild(value);
+            return new Element[] { item };
+        }
+        return new Element[] {};
+    }
+
+
+    /**
+     * Creats the data element used for the static part of DESCRIBE document.
+     */
+    @Override
+    protected Element createStaticData(
+        FLYSArtifact   flys,
+        ElementCreator creator,
+        CallContext    cc,
+        String         name,
+        String         value,
+        String         type
+    ) {
+        Element dataElement = creator.create("data");
+        creator.addAttr(dataElement, "name", name, true);
+        creator.addAttr(dataElement, "type", type, true);
+
+        Element itemElement = creator.create("item");
+        creator.addAttr(itemElement, "value", value, true);
+
+        String[] labels = getLabels(cc, value);
+        Object[] obj    = new Object[] { labels[0] };
+
+        // TODO own i18n
+        String attrValue = Resources.getMsg(
+            cc.getMeta(), "wsp.selected.string", "wsp.selected.string", obj);
+        //I18N_STATIC_KEY, I18N_STATIC_KEY, obj);
+
+        creator.addAttr(itemElement, "label", attrValue, true);
+        dataElement.appendChild(itemElement);
+
+        return dataElement;
+    }
+
+
+    /**
+     * Get name to display for selected watelerlevels (for example "Q=123")
+     * from the CalculationResult.
+     */
+    public static String[] getLabels(CallContext cc, String value) {
+        String[] recommendations = value.split("#");
+        String displayString = "";
+
+        // Walk over all selected recommendations and create label
+        // like "W (Q=1) - W (Q=2)".
+        for (int i = 0; i < recommendations.length; i+=2) {
+            String[] minuendParts = StringUtil
+                .unbracket(recommendations[i+0])
+                .split(";");
+            if(minuendParts.length >= 4) {
+                displayString += "(" + minuendParts[3];
+            }
+            else {
+                displayString += "([error]";
+            }
+
+            displayString += " - ";
+
+            String[] subtrahendParts = StringUtil
+                .unbracket(recommendations[i+1])
+                .split(";");
+            if(subtrahendParts.length >= 4) {
+                displayString += subtrahendParts[3] + ") ";
+            }
+            else {
+                displayString += "[error])";
+            }
+        }
+
+        return new String[] { displayString };
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/WaterlevelSelectState.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,192 @@
+package org.dive4elements.river.artifacts.states;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Element;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.artifactdatabase.data.DefaultStateData;
+import org.dive4elements.artifactdatabase.data.StateData;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.WQKms;
+import org.dive4elements.river.artifacts.model.extreme.ExtremeResult;
+import org.dive4elements.river.artifacts.resources.Resources;
+import org.dive4elements.river.utils.FLYSUtils;
+import org.dive4elements.river.utils.StringUtil;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class WaterlevelSelectState extends DefaultState {
+
+    private static final Logger logger =
+        Logger.getLogger(WaterlevelSelectState.class);
+
+    public static final String SPLIT_CHAR = ";";
+
+    public static final String WINFO_WSP_STATE_ID = "state.winfo.waterlevel";
+
+    public static final String I18N_STATIC_KEY = "wsp.selected.string";
+
+
+    @Override
+    protected String getUIProvider() {
+        return "wsp_datacage_panel";
+    }
+
+
+    /**
+     * @param flys ignored
+     * @param cc   ignrored
+     */
+    @Override
+    public StateData transform(
+        FLYSArtifact flys,
+        CallContext  cc,
+        StateData    stateData,
+        String       name,
+        String       val
+    ) {
+        if (!isValueValid(val)) {
+            logger.error("The given input string is not valid: '" + val + "'");
+            return null;
+        }
+
+        return new DefaultStateData(name, null, null, StringUtil.unbracket(val));
+    }
+
+
+    @Override
+    public boolean validate(Artifact artifact)
+    throws IllegalArgumentException
+    {
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        StateData data = flys.getData("wsp");
+
+        if (data == null) {
+            throw new IllegalArgumentException("WSP is empty");
+        }
+
+        return true;
+    }
+
+
+    @Override
+    protected Element createStaticData(
+        FLYSArtifact   flys,
+        ElementCreator creator,
+        CallContext    cc,
+        String         name,
+        String         value,
+        String         type
+    ) {
+        Element dataElement = creator.create("data");
+        creator.addAttr(dataElement, "name", name, true);
+        creator.addAttr(dataElement, "type", type, true);
+
+        Element itemElement = creator.create("item");
+        creator.addAttr(itemElement, "value", value, true);
+
+        String[] labels = getLabels(cc, value);
+        Object[] obj    = new Object[] { labels[0] };
+
+        String attrValue = Resources.getMsg(
+            cc.getMeta(), I18N_STATIC_KEY, I18N_STATIC_KEY, obj);
+
+        creator.addAttr(itemElement, "label", attrValue, true);
+        dataElement.appendChild(itemElement);
+
+        return dataElement;
+    }
+
+
+    /**
+     * Get name to display for selected watelerlevel (for example "Q=123")
+     * from the CalculationResult.
+     */
+    public static String[] getLabels(CallContext cc, String value) {
+        String[] parts = value.split(SPLIT_CHAR);
+
+        FLYSArtifact artifact = FLYSUtils.getArtifact(parts[0], cc);
+
+        CalculationResult rawData = (CalculationResult) artifact.compute(
+            cc,
+            null,
+            WINFO_WSP_STATE_ID,
+            ComputeType.ADVANCE,
+            false);
+
+        WQKms[] wqkms;
+
+        if (rawData.getData() instanceof ExtremeResult) {
+            wqkms = ((ExtremeResult) rawData.getData()).getWQKms();
+        }
+        else {
+            wqkms = (WQKms[]) rawData.getData();
+        }
+
+        int idx = -1;
+        try {
+            idx = Integer.parseInt(parts[2]);
+        }
+        catch (NumberFormatException nfe) { /* do nothing */ }
+
+        String name = wqkms[idx].getName();
+
+        return new String[] { StringUtil.wWrap(name) };
+    }
+
+
+    /**
+     * Validates the given String. A valid string for this state requires the
+     * format: "UUID;FACETNAME;FACETINDEX".
+     *
+     * @param value The string value requires validation.
+     *
+     * @return true, if the string applies the specified format, otherwise
+     * false.
+     */
+    public static boolean isValueValid(String value) {
+        logger.debug("Validate string: '" + value + "'");
+
+        value = StringUtil.unbracket(value);
+
+        logger.debug("Validate substring: '" + value + "'");
+
+        if (value == null || value.length() == 0) {
+            return false;
+        }
+
+        String[] parts = value.split(SPLIT_CHAR);
+
+        if (parts == null || parts.length < 3) {
+            return false;
+        }
+
+        if (parts[0] == null || parts[0].length() == 0) {
+            return false;
+        }
+
+        if (parts[1] == null || parts[1].length() == 0) {
+            return false;
+        }
+
+        try {
+            Integer.parseInt(parts[2]);
+        }
+        catch (NumberFormatException nfe) {
+            logger.error("Index is not a valid integer!", nfe);
+        }
+
+        return true;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/WaterlevelState.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,152 @@
+package org.dive4elements.river.artifacts.states;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.ChartArtifact;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.WINFOArtifact;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.CrossSectionWaterLineFacet;
+import org.dive4elements.river.artifacts.model.DataFacet;
+import org.dive4elements.river.artifacts.model.EmptyFacet;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.ReportFacet;
+import org.dive4elements.river.artifacts.model.WQKms;
+import org.dive4elements.river.artifacts.model.WaterlevelFacet;
+import org.dive4elements.river.utils.FLYSUtils;
+
+/** State in which a waterlevel has been calculated. */
+public class WaterlevelState
+extends      DefaultState
+implements   FacetTypes
+{
+    /** The logger that is used in this state. */
+    private static Logger logger = Logger.getLogger(WaterlevelState.class);
+
+
+    /**
+     * From this state can only be continued trivially.
+     */
+    @Override
+    protected String getUIProvider() {
+        return "continue";
+    }
+
+
+    /**
+     * Compute result or returned object from cache, create facets.
+     * @param old Object that was cached.
+     */
+    protected Object compute(
+        WINFOArtifact winfo,
+        CallContext   cc,
+        String        hash,
+        List<Facet>   facets,
+        Object        old
+    ) {
+        String id = getID();
+
+        CalculationResult res = old instanceof CalculationResult
+            ? (CalculationResult) old
+            : winfo.getWaterlevelData();
+
+        if (facets == null) {
+            return res;
+        }
+
+        boolean debug = logger.isDebugEnabled();
+
+        WQKms [] wqkms = (WQKms []) res.getData();
+
+        for (int i = 0; i < wqkms.length; i++) {
+            String name = wqkms[i].getName();
+
+            String nameW = FLYSUtils.createWspWTitle(winfo, cc, name);
+            String nameQ = FLYSUtils.createWspQTitle(winfo, cc, name);
+
+            // Hotfix for theme names. Themes with the same name cause problems
+            // aggregating chart legend items.
+            if (i > 0 && name.equals(wqkms[i - 1].getName())) {
+                nameW += "; Q=" + wqkms[i].get(0, new double[3])[1];
+                nameQ += " = " + wqkms[i].get(0, new double[3])[1];
+            }
+
+            if (debug) {
+                logger.debug("Create facet: " + nameW);
+                logger.debug("Create facet: " + nameQ);
+            }
+
+            Facet w = new WaterlevelFacet(
+                i, LONGITUDINAL_W, nameW, ComputeType.ADVANCE, id, hash);
+            Facet q = new WaterlevelFacet(
+                i, LONGITUDINAL_Q, nameQ, ComputeType.ADVANCE, id, hash);
+
+            facets.add(new CrossSectionWaterLineFacet(i, nameW));
+
+            facets.add(w);
+            facets.add(q);
+        }
+
+        if (wqkms.length > 0) {
+            Facet wst = new DataFacet(
+                WST, "WST data", ComputeType.ADVANCE, hash, id);
+            Facet csv = new DataFacet(
+                CSV, "CSV data", ComputeType.ADVANCE, hash, id);
+            Facet pdf = new DataFacet(
+                PDF, "PDF data", ComputeType.ADVANCE, hash, id);
+
+            facets.add(wst);
+            facets.add(csv);
+            facets.add(pdf);
+        }
+
+        if (res.getReport().hasProblems()) {
+            facets.add(new ReportFacet(ComputeType.ADVANCE, hash, id));
+        }
+
+        return res;
+    }
+
+
+    /**
+     * @param context Ignored.
+     */
+    @Override
+    public Object computeFeed(
+        FLYSArtifact artifact,
+        String       hash,
+        CallContext  context,
+        List<Facet>  facets,
+        Object       old
+    ) {
+        if (artifact instanceof ChartArtifact) {
+            facets.add(new EmptyFacet());
+            return null;
+        }
+        return compute((WINFOArtifact) artifact, context, hash, facets, old);
+    }
+
+
+    /**
+     * @param context Ignored.
+     */
+    @Override
+    public Object computeAdvance(
+        FLYSArtifact artifact,
+        String       hash,
+        CallContext  context,
+        List<Facet>  facets,
+        Object       old
+    ) {
+        if (artifact instanceof ChartArtifact) {
+            facets.add(new EmptyFacet());
+            return null;
+        }
+        return compute((WINFOArtifact) artifact, context, hash, facets, old);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/extreme/ExtremeCompute.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,116 @@
+package org.dive4elements.river.artifacts.states.extreme;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+import org.dive4elements.river.artifacts.access.ExtremeAccess;
+
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.CrossSectionWaterLineFacet;
+import org.dive4elements.river.artifacts.model.DataFacet;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.ReportFacet;
+import org.dive4elements.river.artifacts.model.WaterlevelFacet;
+import org.dive4elements.river.artifacts.model.WQKms;
+
+import org.dive4elements.river.artifacts.model.extreme.ExtremeCurveFacet;
+import org.dive4elements.river.artifacts.model.extreme.ExtremeCalculation;
+import org.dive4elements.river.artifacts.model.extreme.ExtremeResult;
+
+import org.dive4elements.river.artifacts.resources.Resources;
+import org.dive4elements.river.artifacts.states.DefaultState;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+
+/** State in which to deliver extreme value analysis result. */
+public class ExtremeCompute
+extends      DefaultState
+implements   FacetTypes
+{
+    /** Private logger. */
+    private static Logger logger = Logger.getLogger(ExtremeCompute.class);
+
+
+    public ExtremeCompute() {
+    }
+
+
+    @Override
+    public Object computeAdvance(
+        FLYSArtifact artifact,
+        String       hash,
+        CallContext  context,
+        List<Facet>  facets,
+        Object       old
+    ) {
+        logger.debug("ExtremeCompute.computeAdvance");
+
+        CalculationResult res;
+
+        ExtremeAccess access = new ExtremeAccess(artifact, context);
+
+        if (old instanceof CalculationResult) {
+            res = (CalculationResult)old;
+        }
+        else {
+            ExtremeCalculation calc = new ExtremeCalculation(access);
+            res = calc.calculate();
+        }
+
+        if (facets == null) {
+            return res;
+        }
+
+        if (res.getReport().hasProblems()) {
+            facets.add(new ReportFacet());
+        }
+
+        ExtremeResult eres = (ExtremeResult) res.getData();
+        WQKms [] wqkms = (WQKms []) eres.getWQKms();
+
+        if (wqkms == null) {
+            logger.error("No computation result!");
+            return res;
+        }
+
+        for (int i = 0; i < wqkms.length; i++) {
+            String name = wqkms[i].getName();
+            // The name already contains "W(...)".
+            String qname = name.replace("W(","Q=");
+            qname = qname.substring(0,qname.length()-1);
+
+            Facet w = new WaterlevelFacet(
+                i, LONGITUDINAL_W, name, ComputeType.ADVANCE, id, hash);
+            Facet q = new WaterlevelFacet(
+                i, LONGITUDINAL_Q, qname, ComputeType.ADVANCE, id, hash);
+            Facet csFacet = new CrossSectionWaterLineFacet(i, name);
+
+            facets.add(w);
+            facets.add(q);
+            facets.add(csFacet);
+        }
+
+
+        facets.add(
+            new DataFacet(CSV, "CSV data", ComputeType.ADVANCE, hash, id));
+        facets.add(
+            new DataFacet(PDF, "PDF data", ComputeType.ADVANCE, hash, id));
+        facets.add(
+            new DataFacet(WST, "WST data", ComputeType.ADVANCE, hash, id));
+
+        facets.add(new ExtremeCurveFacet(Resources.getMsg(context.getMeta(),
+                                         "extreme_wq_curve", "extreme_wq_curve")));
+        facets.add(new ExtremeCurveFacet(Resources.getMsg(context.getMeta(),
+                                         "extreme_wq_base_curve", "extreme_wq_base_curve")
+                                         , true));
+
+        return res;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/extreme/ExtremeQInput.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,245 @@
+package org.dive4elements.river.artifacts.states.extreme;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Element;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.access.ExtremeAccess;
+
+import org.dive4elements.artifactdatabase.ProtocolUtils;
+import org.dive4elements.artifactdatabase.data.StateData;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+
+import org.dive4elements.river.artifacts.model.RiverFactory;
+import org.dive4elements.river.artifacts.model.WstValueTable;
+/*
+import org.dive4elements.river.model.Gauge;
+import org.dive4elements.river.model.Wst;
+import org.dive4elements.river.utils.FLYSUtils;
+*/
+import org.dive4elements.river.artifacts.model.Range;
+import org.dive4elements.river.model.River;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+import org.dive4elements.river.artifacts.model.RangeWithValues;
+import org.dive4elements.river.artifacts.states.DefaultState;
+import org.dive4elements.river.artifacts.model.WstValueTableFactory;
+
+
+/** TODO Subclass WQAdapted. */
+
+/**
+ * State to input Q data in segments for extreme value calculations..
+ * The data item ranges is expected to have this format <from1>;<to1>;<value1>:<from2>;<to2>;<value2>:...
+ * (;;;:;;;:;;;:...)
+ */
+public class ExtremeQInput extends DefaultState {
+
+    /** The logger used in this state.*/
+    private static Logger logger = Logger.getLogger(ExtremeQInput.class);
+
+
+    /** Trivial, empty constructor. */
+    public ExtremeQInput() {
+    }
+
+
+    /**
+     * Create one element for each 'segment' of the selected river that
+     * is within the given kilometer range (TODO). Each element is a tuple of
+     * (from;to) where <i>from</i> is the lower bounds of the segment or the
+     * lower kilometer range. <i>to</i> is the upper bounds of the segment or
+     * the upper kilometer range.
+     *
+     * @param cr The ElementCreator.
+     * @param artifact The FLYS artifact.
+     * @param name The name of the data item.
+     * @param context The CallContext.
+     *
+     * @return a list of elements that consist of tuples of the intersected
+     *         segments of the selected river.
+     */
+    @Override
+    protected Element[] createItems(
+        XMLUtils.ElementCreator cr,
+        Artifact    artifact,
+        String      name,
+        CallContext context)
+    {
+        logger.debug("ExtremeQInput.createItems: " + name);
+
+        FLYSArtifact flysArtifact = (FLYSArtifact) artifact;
+
+        ExtremeAccess access = new ExtremeAccess(flysArtifact, context);
+        River river = RiverFactory.getRiver(access.getRiver());
+        WstValueTable wstValueTable = WstValueTableFactory.getTable(river);
+
+        List<Range> ranges   = wstValueTable.findSegments(access.getFrom(),
+            access.getTo());
+
+        int num = ranges != null ? ranges.size() : 0;
+
+        if (num == 0) {
+            logger.warn("Selected distance matches no segments.");
+            return null;
+        }
+
+        List<Element> elements = new ArrayList<Element>();
+
+        for (Range range: ranges) {
+            elements.add(createItem(
+                cr, new String[] { range.getStart() + ";" + range.getEnd(), ""}, new double[] {0,100000}));
+        }
+
+        Element[] els = new Element[elements.size()];
+
+        return elements.toArray(els);
+    }
+
+
+    /** Create sub-item ('row') of data thing. */
+    protected Element createItem(
+        XMLUtils.ElementCreator cr,
+        Object   obj,
+        double[] q
+        )
+    {
+        Element item  = ProtocolUtils.createArtNode(cr, "item", null, null);
+        Element label = ProtocolUtils.createArtNode(cr, "label", null, null);
+        Element value = ProtocolUtils.createArtNode(cr, "value", null, null);
+
+        String[] arr = (String[]) obj;
+
+        label.setTextContent(arr[0]);
+        value.setTextContent(arr[1]);
+
+        item.appendChild(label);
+        item.appendChild(value);
+
+        if (q != null) {
+            Element qRange = createRangeElement(cr, q, "Q");
+            item.appendChild(qRange);
+        }
+
+        return item;
+    }
+
+
+    /**
+     * Create elements to set min and max values of segments q (just min makes
+     * sense for extremes.
+     */
+    protected Element createRangeElement(
+        XMLUtils.ElementCreator cr,
+        double[] mm,
+        String   type)
+    {
+        Element range = ProtocolUtils.createArtNode(
+            cr, "range",
+            new String[] {"type"},
+            new String[] {type});
+
+        Element min = ProtocolUtils.createArtNode(cr, "min", null, null);
+        min.setTextContent(String.valueOf(mm[0]));
+
+        Element max = ProtocolUtils.createArtNode(cr, "max", null, null);
+        max.setTextContent(String.valueOf(mm[1]));
+
+        range.appendChild(min);
+        range.appendChild(max);
+
+        return range;
+    }
+
+
+    @Override
+    protected String getUIProvider() {
+        return "q_segmented_panel";
+    }
+
+
+    /** Validate given data (return true). */
+    @Override
+    public boolean validate(Artifact artifact)
+    throws IllegalArgumentException
+    {
+        logger.debug("ExtremeQInput.validate");
+
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+        logger.debug("ExtremeQInput: " + getData(flys, "ranges"));
+
+        /*
+        // TODO sort out what has to be validated (prevent negative values?).
+        RangeWithValues[] rwvs = extractInput(getData(flys, "ranges"));
+
+        if (rwvs == null) {
+            throw new IllegalArgumentException("error_missing_wq_data");
+        }
+
+        List<Gauge> gauges = FLYSUtils.getGauges(flys);
+        River        river = FLYSUtils.getRiver(flys);
+        Wst            wst = WstFactory.getWst(river);
+
+        for (Gauge gauge: gauges) {
+            Range range  = gauge.getRange();
+            double lower = range.getA().doubleValue();
+            double upper = range.getB().doubleValue();
+
+            for (RangeWithValues rwv: rwvs) {
+                if (lower <= rwv.getStart() && upper >= rwv.getEnd()) {
+                    compareQsWithGauge(wst, gauge, rwv.getValues());
+                }
+            }
+        }
+        */
+
+        return true;
+    }
+
+
+    /** Form RangeWithValue-Array from state data. */
+    protected RangeWithValues[] extractInput(StateData data) {
+        if (data == null) {
+            return null;
+        }
+
+        String dataString = (String) data.getValue();
+        String[]   ranges = dataString.split(":");
+
+        List<RangeWithValues> rwv = new ArrayList<RangeWithValues>();
+
+        for (String range: ranges) {
+            String[] parts = range.split(";");
+
+            double lower = Double.parseDouble(parts[0]);
+            double upper = Double.parseDouble(parts[1]);
+
+            String[] values = parts[2].split(",");
+
+            int      num = values.length;
+            double[] res = new double[num];
+
+            for (int i = 0; i < num; i++) {
+                try {
+                    res[i] = Double.parseDouble(values[i]);
+                }
+                catch (NumberFormatException nfe) {
+                    logger.warn(nfe, nfe);
+                }
+            }
+
+            rwv.add(new RangeWithValues(lower, upper, res));
+        }
+
+        return rwv.toArray(new RangeWithValues[rwv.size()]);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/extreme/PercentInput.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,44 @@
+package org.dive4elements.river.artifacts.states.extreme;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifactdatabase.data.StateData;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.states.DefaultState;
+
+
+/**
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class PercentInput extends DefaultState {
+
+    /** The logger used in this class. */
+    private static Logger logger = Logger.getLogger(PercentInput.class);
+
+    public static final String PARAMETER_NAME = "percent";
+
+
+    /**
+     * The default constructor that initializes an empty State object.
+     */
+    public PercentInput () {
+    }
+
+
+    /** Tell UI what to display for input. */
+    @Override
+    protected String getUIProvider() {
+        return "percent_input";
+    }
+
+
+    @Override
+    protected String[] getDefaultsFor(CallContext context, StateData data) {
+        if (data != null && data.getName().equals(PARAMETER_NAME)) {
+            return new String[] {"5", "5"};
+        }
+
+        return null;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/fixation/AnalysisPeriods.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,28 @@
+package org.dive4elements.river.artifacts.states.fixation;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.river.artifacts.states.DefaultState;
+
+/**
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class AnalysisPeriods extends DefaultState {
+
+    /** The logger used in this class. */
+    private static Logger logger = Logger.getLogger(AnalysisPeriods.class);
+
+
+    /**
+     * The default constructor that initializes an empty State object.
+     */
+    public AnalysisPeriods() {
+    }
+
+    @Override
+    protected String getUIProvider() {
+        return "fix.period_ana_panel";
+    }
+
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/fixation/EventSelect.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,95 @@
+package org.dive4elements.river.artifacts.states.fixation;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Element;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator;
+
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.states.DefaultState;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.utils.FLYSUtils;
+
+import org.dive4elements.river.artifacts.model.FixingsOverview;
+import org.dive4elements.river.artifacts.model.FixingsOverviewFactory;
+import org.dive4elements.river.artifacts.model.FixingsOverview.IdFilter;
+import org.dive4elements.river.artifacts.model.FixingsOverview.Fixing;
+
+
+/**
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class EventSelect extends DefaultState {
+
+    /** The logger used in this class. */
+    private static Logger logger = Logger.getLogger(EventSelect.class);
+
+
+    /**
+     * The default constructor that initializes an empty State object.
+     */
+    public EventSelect() {
+    }
+
+    @Override
+    protected String getUIProvider() {
+        return "fix.event_panel";
+    }
+
+    @Override
+    protected Element createStaticData(
+        FLYSArtifact   flys,
+        ElementCreator creator,
+        CallContext    cc,
+        String         name,
+        String         value,
+        String         type
+    ) {
+        int[] array = FLYSUtils.intArrayFromString(value);
+
+        Element dataElement = creator.create("data");
+        creator.addAttr(dataElement, "name", name, true);
+        creator.addAttr(dataElement, "type", type, true);
+
+        String river = FLYSUtils.getRiver(flys).getName();
+
+        FixingsOverview overview = FixingsOverviewFactory.getOverview(river);
+
+        for (int i = 0; i < array.length; i++) {
+            Element itemElement = creator.create("item");
+            creator.addAttr(
+                itemElement,
+                "value",
+                String.valueOf(array[i]),
+                true);
+
+            creator.addAttr(
+                itemElement,
+                "label",
+                getLabel(cc, array[i], overview),
+                true);
+            dataElement.appendChild(itemElement);
+        }
+        return dataElement;
+    }
+
+
+    public static String getLabel(
+        CallContext cc,
+        int value,
+        FixingsOverview overview
+    ) {
+        logger.debug("Create label for value: " + value);
+
+        IdFilter filter = new IdFilter(value);
+        List<Fixing.Column> columns = overview.filter(null, filter);
+        return  columns.isEmpty()
+            ? ""
+            : columns.get(0).getDescription();
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/fixation/FixAnalysisCompute.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,330 @@
+package org.dive4elements.river.artifacts.states.fixation;
+
+import java.text.DateFormat;
+import java.util.Date;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifactdatabase.state.FacetActivity;
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.access.FixAnalysisAccess;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.DataFacet;
+import org.dive4elements.river.artifacts.model.DateRange;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.ReportFacet;
+import org.dive4elements.river.artifacts.model.fixings.FixAnalysisCalculation;
+import org.dive4elements.river.artifacts.model.fixings.FixAnalysisEventsFacet;
+import org.dive4elements.river.artifacts.model.fixings.FixAnalysisPeriodsFacet;
+import org.dive4elements.river.artifacts.model.fixings.FixAnalysisResult;
+import org.dive4elements.river.artifacts.model.fixings.FixAvSectorFacet;
+import org.dive4elements.river.artifacts.model.fixings.FixDerivateFacet;
+import org.dive4elements.river.artifacts.model.fixings.FixDeviationFacet;
+import org.dive4elements.river.artifacts.model.fixings.FixLongitudinalAnalysisFacet;
+import org.dive4elements.river.artifacts.model.fixings.FixLongitudinalAvSectorFacet;
+import org.dive4elements.river.artifacts.model.fixings.FixLongitudinalDeviationFacet;
+import org.dive4elements.river.artifacts.model.fixings.FixLongitudinalReferenceFacet;
+import org.dive4elements.river.artifacts.model.fixings.FixOutlierFacet;
+import org.dive4elements.river.artifacts.model.fixings.FixReferenceEventsFacet;
+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.IdGenerator;
+
+/**
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class FixAnalysisCompute
+extends      DefaultState
+implements   FacetTypes
+{
+    /** The log used in this class. */
+    private static Logger log = Logger.getLogger(FixAnalysisCompute.class);
+
+    private static final String I18N_REFERENCEPERIOD_SHORT = "fix.reference.period.event.short";
+
+    private static final String I18N_ANALYSISPERIODS = "fix.analysis.periods";
+
+    private static final String I18N_DERIVATIVE = "fix.derivative";
+
+    private static final String I18N_OUTLIER = "fix.outlier";
+
+    private static final String I18N_ANALYSIS = "fix.analysis.short";
+
+    private static final String I18N_DEVIATION = "fix.deviation";
+
+    private static final String I18N_REFERENCEDEVIATION = "fix.reference.deviation";
+
+    private static final String I18N_REFERENCEPERIOD = "state.fix.analysis.referenceperiod";
+
+    public static final String [] SECTOR_LABELS = {
+        "fix.mnq",
+        "fix.mq",
+        "fix.mhq",
+        "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(
+            "fixanalysis",
+            new FacetActivity() {
+                @Override
+                public Boolean isInitialActive(
+                    Artifact artifact,
+                    Facet    facet,
+                    String   output
+                ) {
+                    if (output.contains(FacetTypes.ChartType.FLSC.toString())) {
+                        // Longitudinal section chart
+                        String name = facet.getName();
+
+                        if (name.contains(FacetTypes.FIX_ANALYSIS_EVENTS_DWT)
+                         || name.contains(FacetTypes.FIX_ANALYSIS_EVENTS_LS)
+                         || name.contains(FacetTypes.FIX_ANALYSIS_EVENTS_WQ)
+                         || name.contains(FacetTypes.FIX_REFERENCE_EVENTS_DWT)
+                         || name.contains(FacetTypes.FIX_REFERENCE_EVENTS_LS)
+                         || name.contains(FacetTypes.FIX_REFERENCE_EVENTS_WQ)
+                        ) {
+                            return Boolean.FALSE;
+                        }
+                    }
+                    if (output.contains(FacetTypes.ChartType.FDWC.toString())
+                        && facet.getName().contains(FacetTypes.FIX_SECTOR_AVERAGE_DWT)) {
+                        return Boolean.FALSE;
+                    }
+
+                    return Boolean.TRUE;
+                }
+            });
+    }
+
+
+    /**
+     * The default constructor that initializes an empty State object.
+     */
+    public FixAnalysisCompute() {
+    }
+
+
+    @Override
+    public Object computeAdvance(
+        FLYSArtifact artifact,
+        String       hash,
+        CallContext  context,
+        List<Facet>  facets,
+        Object       old
+    ) {
+        log.debug("FixAnalysisCompute.computeAdvance");
+
+        CalculationResult res;
+
+        FixAnalysisAccess access =
+            new FixAnalysisAccess(artifact, context);
+
+        if (old instanceof CalculationResult) {
+            res = (CalculationResult)old;
+        }
+        else {
+            FixAnalysisCalculation calc = new FixAnalysisCalculation(access);
+            res = calc.calculate();
+        }
+
+        if (facets == null) {
+            return res;
+        }
+
+        if (res.getReport().hasProblems()) {
+            facets.add(new ReportFacet(ComputeType.ADVANCE, hash, id));
+        }
+
+        FixAnalysisResult fr = (FixAnalysisResult)res.getData();
+        if (fr == null) {
+            return res;
+        }
+
+        facets.add(
+            new DataFacet(CSV, "CSV data", ComputeType.ADVANCE, hash, id));
+        facets.add(
+            new DataFacet(
+                FIX_PARAMETERS, "parameters", ComputeType.ADVANCE, hash, id));
+        facets.add(
+            new DataFacet(AT, "AT data", ComputeType.ADVANCE, hash, id));
+
+        int maxId = -100;
+
+        int sectorMask = fr.getUsedSectorsInAnalysisPeriods();
+
+        int qsS = access.getQSectorStart();
+        int qsE = access.getQSectorEnd();
+
+        // TODO: i18n
+        DateFormat df = DateFormat.getDateInstance(DateFormat.MEDIUM);
+
+        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());
+
+            for (int j = qsS; j <= qsE; j++) {
+
+                // Only emit facets for sectors that really have data.
+                if ((sectorMask & (1 << j)) == 0) {
+                    continue;
+                }
+
+                String sector = SECTOR_LABELS[j];
+                String description = "\u0394W (" +
+                    Resources.getMsg(context.getMeta(),
+                        sector,
+                        sector) +
+                        ")";
+
+                int sectorNdx = j - qsS;
+                int facetNdx = i << 2;
+                facetNdx = facetNdx | j;
+
+                if (facetNdx > maxId) {
+                    maxId = facetNdx;
+                }
+
+                facets.add(
+                    new FixAvSectorFacet(
+                        facetNdx,
+                        FIX_SECTOR_AVERAGE_DWT + "_" + sectorNdx,
+                        description));
+                facets.add(
+                    new FixLongitudinalAvSectorFacet(
+                        facetNdx,
+                        FIX_SECTOR_AVERAGE_LS + "_" + sectorNdx,
+                        description + ":" + startDate + " - " + endDate));
+                // TODO: i18n
+                String dev = "Abweichung: " + description;
+                facets.add(
+                    new FixLongitudinalAvSectorFacet(
+                        facetNdx,
+                        FIX_SECTOR_AVERAGE_LS_DEVIATION + "_" + sectorNdx,
+                        dev));
+                facets.add(
+                    new FixAvSectorFacet(
+                        facetNdx,
+                        FIX_SECTOR_AVERAGE_WQ + "_" + sectorNdx,
+                        description));
+
+            }
+
+            String eventDesc =
+                Resources.getMsg(context.getMeta(),
+                                 I18N_ANALYSIS,
+                                 I18N_ANALYSIS);
+
+            int k = 0;
+            for (Date d: fr.getAnalysisEventsDates(i)) {
+                int anaNdx = i << 8;
+                anaNdx = anaNdx | k;
+                facets.add(new FixAnalysisEventsFacet(anaNdx,
+                    FIX_ANALYSIS_EVENTS_DWT,
+                    eventDesc + (i+1) + " - " + df.format(d)));
+                facets.add(new FixLongitudinalAnalysisFacet(anaNdx,
+                    FIX_ANALYSIS_EVENTS_LS,
+                    eventDesc + (i+1) + " - " + df.format(d)));
+                facets.add(new FixAnalysisEventsFacet(anaNdx,
+                    FIX_ANALYSIS_EVENTS_WQ,
+                    eventDesc + (i+1) +" - " + df.format(d)));
+                k++;
+            }
+        }
+
+        IdGenerator idg = new IdGenerator(maxId + 1);
+
+        String i18n_ref = Resources.getMsg(context.getMeta(),
+                I18N_REFERENCEPERIOD_SHORT,
+                I18N_REFERENCEPERIOD_SHORT);
+        String i18n_dev = Resources.getMsg(context.getMeta(),
+                I18N_REFERENCEDEVIATION,
+                I18N_REFERENCEDEVIATION);
+
+        int i = 0;
+        for (Date d: fr.getReferenceEventsDates()) {
+            int refNdx = idg.next() << 8;
+            refNdx |=  i;
+            facets.add(new FixReferenceEventsFacet(refNdx,
+                FIX_REFERENCE_EVENTS_DWT,
+                i18n_ref + " - " + df.format(d)));
+            refNdx = idg.next() << 8;
+            refNdx = refNdx | i;
+            facets.add(new FixLongitudinalReferenceFacet(refNdx,
+                FIX_REFERENCE_EVENTS_LS,
+                i18n_ref + " - " + df.format(d)));
+            refNdx = idg.next() << 8;
+            refNdx |= i;
+            facets.add(new FixReferenceEventsFacet(refNdx,
+                FIX_REFERENCE_EVENTS_WQ,
+                i18n_ref + " - " + df.format(d)));
+            i++;
+        }
+
+
+        facets.add(new FixLongitudinalDeviationFacet(idg.next(),
+            FIX_DEVIATION_LS,
+            i18n_dev));
+
+        String i18n_ana = Resources.getMsg(context.getMeta(),
+                I18N_ANALYSISPERIODS,
+                I18N_ANALYSISPERIODS);
+        facets.add(new FixAnalysisPeriodsFacet(idg.next(),
+            FIX_ANALYSIS_PERIODS_DWT,
+            i18n_ana));
+        facets.add(new FixAnalysisPeriodsFacet(idg.next(),
+            FIX_ANALYSIS_PERIODS_LS,
+            i18n_ana));
+        facets.add(new FixAnalysisPeriodsFacet(idg.next(),
+            FIX_ANALYSIS_PERIODS_WQ,
+            i18n_ana));
+
+        String i18n_refp = Resources.getMsg(context.getMeta(),
+                I18N_REFERENCEPERIOD,
+                I18N_REFERENCEPERIOD);
+        facets.add(new DataFacet(idg.next(),
+            FIX_REFERENCE_PERIOD_DWT,
+            i18n_refp,
+            ComputeType.ADVANCE, null, null));
+
+        facets.add(new FixWQCurveFacet(idg.next(), "W/Q"));
+
+        Boolean preprocessing = access.getPreprocessing();
+
+        if (preprocessing != null && preprocessing) {
+            facets.add(new FixOutlierFacet(
+                idg.next(),
+                FIX_OUTLIER,
+                Resources.getMsg(
+                    context.getMeta(), I18N_OUTLIER, I18N_OUTLIER)));
+        }
+
+        facets.add(new FixDerivateFacet(
+            idg.next(),
+            FIX_DERIVATE_CURVE,
+            Resources.getMsg(
+                context.getMeta(),
+                I18N_DERIVATIVE,
+                I18N_DERIVATIVE)));
+
+        facets.add(new FixDeviationFacet(
+            idg.next(),
+            FIX_DEVIATION_DWT,
+            Resources.getMsg(context.getMeta(),
+                I18N_DEVIATION,
+                I18N_DEVIATION)));
+        return res;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/fixation/FixRealizingCompute.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,166 @@
+package org.dive4elements.river.artifacts.states.fixation;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.access.FixRealizingAccess;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.CrossSectionWaterLineFacet;
+import org.dive4elements.river.artifacts.model.DataFacet;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.ReportFacet;
+import org.dive4elements.river.artifacts.model.WQCKms;
+import org.dive4elements.river.artifacts.model.WQKms;
+import org.dive4elements.river.artifacts.model.WQKmsFactory;
+import org.dive4elements.river.artifacts.model.fixings.FixOutlierFacet;
+import org.dive4elements.river.artifacts.model.fixings.FixRealizingCalculation;
+import org.dive4elements.river.artifacts.model.fixings.FixRealizingResult;
+import org.dive4elements.river.artifacts.model.fixings.FixEventFacet;
+import org.dive4elements.river.artifacts.model.fixings.FixReferenceEventsFacet;
+import org.dive4elements.river.artifacts.model.fixings.FixWQCurveFacet;
+import org.dive4elements.river.artifacts.model.fixings.FixWaterlevelFacet;
+import org.dive4elements.river.artifacts.resources.Resources;
+import org.dive4elements.river.artifacts.states.DefaultState;
+
+/**
+ * State to compute the fixation realizing (vollmer) results.
+ *
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class FixRealizingCompute extends DefaultState implements FacetTypes {
+
+    /** The logger used in this class. */
+    private static Logger logger = Logger.getLogger(FixRealizingCompute.class);
+
+    public static final String I18N_WQ_CURVE = "fix.vollmer.wq.curve";
+
+    public static final String I18N_WQ_OUTLIER = "fix.vollmer.wq.outliers";
+
+    public static final String I18N_WQ_EVENTS = "fix.vollmer.wq.events";
+
+
+    /**
+     * The default constructor that initializes an empty State object.
+     */
+    public FixRealizingCompute() {
+    }
+
+
+    @Override
+    public Object computeAdvance(
+        FLYSArtifact artifact,
+        String       hash,
+        CallContext  context,
+        List<Facet>  facets,
+        Object       old
+    ) {
+        logger.debug("FixRealizingCompute.computeAdvance");
+
+        CalculationResult res;
+
+        FixRealizingAccess access =
+            new FixRealizingAccess(artifact, context);
+
+        if (old instanceof CalculationResult) {
+            res = (CalculationResult) old;
+        }
+        else {
+            FixRealizingCalculation calc = new FixRealizingCalculation(access);
+            res = calc.calculate();
+        }
+
+        if (facets == null) {
+            return res;
+        }
+
+        if (res.getReport().hasProblems()) {
+            facets.add(new ReportFacet());
+        }
+
+        String   id   = getID();
+        CallMeta meta = context.getMeta();
+
+        FixRealizingResult fixRes = (FixRealizingResult) res.getData();
+        WQKms [] wqkms = fixRes != null ? fixRes.getWQKms() : new WQKms[0];
+
+        for (int i = 0; i < wqkms.length; i++) {
+            String nameW = null;
+            String nameQ = null;
+
+            if (access.isQ()) {
+                nameQ = wqkms[i].getName();
+                nameW = "W(" + nameQ + ")";
+            }
+            else {
+                nameW = wqkms[i].getName();
+                nameQ = "Q(" + nameW + ")";
+            }
+
+            Facet w = new FixWaterlevelFacet(
+                i, LONGITUDINAL_W, nameW);
+
+            Facet q = new FixWaterlevelFacet(
+                i, LONGITUDINAL_Q, nameQ);
+            Facet csFacet = new CrossSectionWaterLineFacet(i, nameW);
+
+            facets.add(w);
+            facets.add(q);
+            facets.add(csFacet);
+
+            if (wqkms[i] instanceof WQCKms) {
+                String nameC = nameW.replace(
+                    "benutzerdefiniert",
+                    "benutzerdefiniert [korrigiert]");
+
+                Facet c = new FixWaterlevelFacet(
+                    i, DISCHARGE_LONGITUDINAL_C, nameC);
+
+                facets.add(c);
+            }
+        }
+
+        // Add one facet per event.
+        int i = 0;
+        for (int event: access.getEvents()) {
+            facets.add(new FixEventFacet(i, FIX_EVENTS, WQKmsFactory.getWQKmsName(event)));
+            i++;
+        }
+
+        if (wqkms.length > 0) {
+            facets.add(
+                new DataFacet(CSV, "CSV data", ComputeType.ADVANCE, hash, id));
+
+            facets.add(
+                new DataFacet(WST, "WST data", ComputeType.ADVANCE, hash, id));
+
+            facets.add(
+                new DataFacet(PDF, "PDF data", ComputeType.ADVANCE, hash, id));
+        }
+
+        facets.add(
+            new DataFacet(AT, "AT data", ComputeType.ADVANCE, hash, id));
+
+        facets.add(new FixWQCurveFacet(
+            0,
+            Resources.getMsg(meta, I18N_WQ_CURVE, I18N_WQ_CURVE) + " ("
+                + access.getFunction() + ")"));
+
+        facets.add(new FixReferenceEventsFacet(
+            0,
+            FIX_REFERENCE_EVENTS_WQ,
+            Resources.getMsg(meta, I18N_WQ_EVENTS, I18N_WQ_EVENTS)));
+
+        facets.add(new FixOutlierFacet(
+            0,
+            FIX_OUTLIER,
+            Resources.getMsg(meta, I18N_WQ_OUTLIER, I18N_WQ_OUTLIER)));
+
+        return res;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/fixation/FixationPeriod.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,28 @@
+package org.dive4elements.river.artifacts.states.fixation;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.river.artifacts.states.DefaultState;
+
+/**
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class FixationPeriod extends DefaultState {
+
+    /** The logger used in this class. */
+    private static Logger logger = Logger.getLogger(FixationPeriod.class);
+
+
+    /**
+     * The default constructor that initializes an empty State object.
+     */
+    public FixationPeriod() {
+    }
+
+    @Override
+    protected String getUIProvider() {
+        return "fix.period_panel";
+    }
+
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/fixation/FixationSelect.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,68 @@
+package org.dive4elements.river.artifacts.states.fixation;
+
+import org.w3c.dom.Element;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+
+import org.dive4elements.river.artifacts.resources.Resources;
+import org.dive4elements.river.artifacts.states.DefaultState;
+
+
+/**
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class FixationSelect extends DefaultState {
+
+    /** Constant value for the default fixation calculation. */
+    public static final String CALCULATION_DEFAULT = "calculation.analysis";
+
+    /** Constant value for the vollmer fixation analysis. */
+    public static final String CALCULATION_VOLLMER = "calculation.vollmer";
+
+    /** An Array that holds all available calculation modes.*/
+    public static final String[] CALCULATIONS = {
+        CALCULATION_DEFAULT,
+        CALCULATION_VOLLMER
+    };
+
+    /** Error message that is thrown if no mode has been chosen. */
+    public static final String ERROR_NO_CALCULATION_MODE =
+        "error_feed_no_calculation_mode";
+
+    /** Error message that is thrown if an invalid calculation mode has been
+     * chosen. */
+    public static  final String ERROR_INVALID_CALCULATION_MODE =
+        "error_feed_invalid_calculation_mode";
+
+
+    /**
+     * The default constructor that initializes an empty State object.
+     */
+    public FixationSelect() {
+    }
+
+
+    @Override
+    protected Element[] createItems(
+        XMLUtils.ElementCreator ec,
+        Artifact                artifact,
+        String                  name,
+        CallContext             context)
+    {
+        CallMeta meta = context.getMeta();
+        Element[] calculations = new Element[CALCULATIONS.length];
+
+        for (int i = 0; i < CALCULATIONS.length; i++) {
+            String calc = CALCULATIONS[i];
+            calculations[i] = createItem(
+                ec,
+                new String[] {Resources.getMsg(meta, calc, calc), calc});
+        }
+        return calculations;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/fixation/FunctionSelect.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,52 @@
+package org.dive4elements.river.artifacts.states.fixation;
+
+import java.util.Collection;
+
+import org.w3c.dom.Element;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+import org.dive4elements.river.artifacts.math.fitting.Function;
+import org.dive4elements.river.artifacts.math.fitting.FunctionFactory;
+import org.dive4elements.river.artifacts.states.DefaultState;
+
+/**
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class FunctionSelect extends DefaultState {
+
+    /**
+     * The default constructor that initializes an empty State object.
+     */
+    public FunctionSelect() {
+    }
+
+    @Override
+    public String getUIProvider() {
+        return "fix.functionselect";
+    }
+
+    @Override
+    protected Element[] createItems(
+        XMLUtils.ElementCreator ec,
+        Artifact                artifact,
+        String                  name,
+        CallContext             context)
+    {
+        FunctionFactory ff = FunctionFactory.getInstance();
+        Collection<Function> fc = ff.getFunctions();
+        Element[] functions = new Element[fc.size()];
+
+        int j = 0;
+        for (Function f: fc) {
+            String n = f.getName();
+            String d = f.getDescription();
+            functions[j] = createItem(ec, new String[] {d, n});
+            j++;
+        }
+
+        return functions;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/fixation/GaugeRange.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,28 @@
+package org.dive4elements.river.artifacts.states.fixation;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.river.artifacts.states.DefaultState;
+
+/**
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class GaugeRange extends DefaultState {
+
+    /** The logger used in this class. */
+    private static Logger logger = Logger.getLogger(GaugeRange.class);
+
+
+    /**
+     * The default constructor that initializes an empty State object.
+     */
+    public GaugeRange() {
+    }
+
+
+    @Override
+    protected String getUIProvider() {
+        return "fix.gaugeselect_panel";
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/fixation/LocationSelect.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,55 @@
+package org.dive4elements.river.artifacts.states.fixation;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.Artifact;
+
+import org.dive4elements.river.utils.FLYSUtils;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.states.RangeState;
+
+
+/**
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class LocationSelect extends RangeState {
+
+    /** The logger used in this class. */
+    private static Logger logger = Logger.getLogger(LocationSelect.class);
+
+    private static final String LOWER = "ld_from";
+
+    private static final String UPPER = "ld_to";
+
+    private static final String STEP = "ld_step";
+
+
+    /**
+     * The default constructor that initializes an empty State object.
+     */
+    public LocationSelect() {
+    }
+
+    @Override
+    protected String getUIProvider() {
+        return "fix.location_panel";
+    }
+
+    @Override
+    protected double[] getMinMax(Artifact artifact) {
+        FLYSArtifact flysArtifact = (FLYSArtifact) artifact;
+        return FLYSUtils.getRiverMinMax(flysArtifact);
+    }
+
+
+    /** Misuse to set location mode. */
+    @Override
+    public boolean validate(Artifact artifact)
+    throws IllegalArgumentException
+    {
+        ((FLYSArtifact) artifact).addStringData("ld_mode", "distance");
+        return super.validate(artifact);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/fixation/PreprocessingSelect.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,52 @@
+package org.dive4elements.river.artifacts.states.fixation;
+
+import org.w3c.dom.Element;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+
+import org.dive4elements.river.artifacts.resources.Resources;
+import org.dive4elements.river.artifacts.states.DefaultState;
+
+
+/**
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class PreprocessingSelect extends DefaultState {
+
+    /**
+     * The default constructor that initializes an empty State object.
+     */
+    public PreprocessingSelect() {
+    }
+
+    @Override
+    protected Element[] createItems(
+        XMLUtils.ElementCreator ec,
+        Artifact                artifact,
+        String                  name,
+        CallContext             context)
+    {
+        CallMeta meta = context.getMeta();
+        Element[] elements = new Element[1];
+        elements[0] = createItem(
+                ec,
+                new String[] {
+                    Resources.getMsg(meta,
+                        "state.fix.preprocess",
+                        "state.fix.preprocess"),
+                    "preprocess"});
+
+        return elements;
+    }
+
+
+    @Override
+    protected String getUIProvider() {
+        return "fix.preprocessing_panel";
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/fixation/QSelect.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,29 @@
+package org.dive4elements.river.artifacts.states.fixation;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.river.artifacts.states.DefaultState;
+
+/**
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class QSelect extends DefaultState {
+
+    /** The logger used in this class. */
+    private static Logger logger = Logger.getLogger(QSelect.class);
+
+
+    /**
+     * The default constructor that initializes an empty State object.
+     */
+    public QSelect() {
+    }
+
+
+    /** Show Q Select Panel in clients. */
+    @Override
+    protected String getUIProvider() {
+        return "fix.qselect_panel";
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/fixation/ReferencePeriod.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,28 @@
+package org.dive4elements.river.artifacts.states.fixation;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.river.artifacts.states.DefaultState;
+
+/**
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class ReferencePeriod extends DefaultState {
+
+    /** The logger used in this class. */
+    private static Logger logger = Logger.getLogger(ReferencePeriod.class);
+
+
+    /**
+     * The default constructor that initializes an empty State object.
+     */
+    public ReferencePeriod() {
+    }
+
+    @Override
+    protected String getUIProvider() {
+        return "fix.period_ref_panel";
+    }
+
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/minfo/BedQualityPeriodsSelect.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,24 @@
+package org.dive4elements.river.artifacts.states.minfo;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.river.artifacts.states.DefaultState;
+
+public class BedQualityPeriodsSelect extends DefaultState {
+
+    /** The logger used in this class. */
+    private static Logger logger = Logger.getLogger(BedQualityPeriodsSelect.class);
+
+
+    /**
+     * The default constructor that initializes an empty State object.
+     */
+    public BedQualityPeriodsSelect() {
+    }
+
+    @Override
+    protected String getUIProvider() {
+        return "bedquality_periods_select";
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/minfo/BedQualityState.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,227 @@
+package org.dive4elements.river.artifacts.states.minfo;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.access.BedQualityAccess;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.DataFacet;
+import org.dive4elements.river.artifacts.model.DateRange;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.minfo.BedDensityFacet;
+import org.dive4elements.river.artifacts.model.minfo.BedDiameterFacet;
+import org.dive4elements.river.artifacts.model.minfo.BedDiameterResult;
+import org.dive4elements.river.artifacts.model.minfo.BedParametersResult;
+import org.dive4elements.river.artifacts.model.minfo.BedPorosityFacet;
+import org.dive4elements.river.artifacts.model.minfo.BedQualityCalculation;
+import org.dive4elements.river.artifacts.model.minfo.BedQualityDiameterResult;
+import org.dive4elements.river.artifacts.model.minfo.BedQualityResult;
+import org.dive4elements.river.artifacts.model.minfo.BedloadDiameterFacet;
+import org.dive4elements.river.artifacts.model.minfo.BedloadDiameterResult;
+import org.dive4elements.river.artifacts.resources.Resources;
+import org.dive4elements.river.artifacts.states.DefaultState;
+
+
+public class BedQualityState extends DefaultState implements FacetTypes {
+
+    private static final long serialVersionUID = 1L;
+
+    private static final Logger logger = Logger
+        .getLogger(BedQualityState.class);
+
+    public static final String I18N_TOPLAYER = "bedquality.toplayer";
+    public static final String I18N_SUBLAYER = "bedquality.sublayer";
+
+    public static final String I18N_FACET_BED_POROSITY_TOPLAYER = "facet.bedquality.bed.porosity.toplayer";
+    public static final String I18N_FACET_BED_POROSITY_SUBLAYER = "facet.bedquality.bed.porosity.sublayer";
+    public static final String I18N_FACET_BED_DENSITY_TOPLAYER = "facet.bedquality.bed.density.toplayer";
+    public static final String I18N_FACET_BED_DENSITY_SUBLAYER = "facet.bedquality.bed.density.sublayer";
+    public static final String I18N_FACET_BED_DIAMETER_TOPLAYER = "facet.bedquality.bed.diameter.toplayer";
+    public static final String I18N_FACET_BED_DIAMETER_SUBLAYER = "facet.bedquality.bed.diameter.sublayer";
+    public static final String I18N_FACET_BEDLOAD_DIAMETER = "facet.bedquality.bedload.diameter";
+
+    @Override
+    public Object computeAdvance(FLYSArtifact artifact, String hash,
+        CallContext context, List<Facet> facets, Object old) {
+        logger.debug("BedQualityState.computeAdvance");
+
+        List<Facet> newFacets = new ArrayList<Facet>();
+
+        BedQualityAccess access = new BedQualityAccess(artifact, context);
+
+        CalculationResult res = old instanceof CalculationResult ? (CalculationResult) old
+            : new BedQualityCalculation().calculate(access);
+
+        if (facets == null || res == null) {
+            return res;
+        }
+
+        BedQualityResult[] results = (BedQualityResult[]) res.getData();
+
+        if (results == null || results.length == 0) {
+            logger.warn("Calculation computed no results!");
+            return res;
+        }
+
+        generateFacets(context, newFacets, results, getID(), hash);
+        logger.debug("Created " + newFacets.size() + " new Facets.");
+
+        facets.addAll(newFacets);
+
+        return res;
+    }
+
+    protected void generateFacets(CallContext context, List<Facet> newFacets,
+        BedQualityResult[] results, String stateId, String hash) {
+        logger.debug("BedQualityState.generateFacets");
+
+        CallMeta meta = context.getMeta();
+
+        newFacets.add(new DataFacet(CSV, "CSV data", ComputeType.ADVANCE, hash, id));
+        for (int idx = 0; idx < results.length; idx++) {
+            BedQualityResult result = results[idx];
+            DateRange range = result.getDateRange();
+            BedDiameterResult[] bedDiameter = result.getBedResults();
+            for (int j = 0; j < bedDiameter.length; j++) {
+                newFacets.add(new BedDiameterFacet((idx << 8) + j,
+                    BED_QUALITY_BED_DIAMETER_TOPLAYER,
+                    createDiameterTopLayerDescription(
+                        meta,
+                        bedDiameter[j],
+                        range),
+                    ComputeType.ADVANCE, stateId, hash));
+
+                newFacets.add(new BedDiameterFacet((idx << 8) +j,
+                    BED_QUALITY_BED_DIAMETER_SUBLAYER,
+                    createDiameterSubLayerDescription(
+                        meta,
+                        bedDiameter[j],
+                        range),
+                    ComputeType.ADVANCE, stateId, hash));
+            }
+            BedloadDiameterResult[] bedloadDiameter = result.getBedloadResults();
+            for (int j = 0;  j < bedloadDiameter.length; j++) {
+                newFacets.add(new BedloadDiameterFacet(
+                    (idx << 8) + j,
+                    BED_QUALITY_BEDLOAD_DIAMETER,
+                    createDiameterDescription(
+                        meta, bedloadDiameter[j]),
+                    ComputeType.ADVANCE,
+                    stateId,
+                    hash));
+
+            }
+            BedParametersResult[] bedParameters = result.getParameters();
+            for (int j = 0; j < bedParameters.length; j++) {
+                newFacets.add(new BedPorosityFacet((idx << 8) + j,
+                    BED_QUALITY_POROSITY_TOPLAYER,
+                    createPorosityTopLayerDescription(
+                        meta,
+                        bedParameters[j],
+                        range),
+                    ComputeType.ADVANCE, stateId, hash));
+
+                newFacets.add(new BedPorosityFacet((idx << 8) + j,
+                    BED_QUALITY_POROSITY_SUBLAYER,
+                    createPorositySubLayerDescription(
+                        meta,
+                        bedParameters[j],
+                        range),
+                    ComputeType.ADVANCE, stateId, hash));
+
+                newFacets.add(new BedDensityFacet((idx << 8) + j,
+                    BED_QUALITY_SEDIMENT_DENSITY_TOPLAYER,
+                    createDensityTopLayerDescription(
+                        meta,
+                        bedParameters[j],
+                        range),
+                    ComputeType.ADVANCE, stateId, hash));
+
+                newFacets.add(new BedDensityFacet((idx << 8) + j,
+                    BED_QUALITY_SEDIMENT_DENSITY_SUBLAYER,
+                    createDensitySubLayerDescription(
+                        meta,
+                        bedParameters[j],
+                        range),
+                    ComputeType.ADVANCE, stateId, hash));
+            }
+        }
+    }
+
+    protected String createPorosityTopLayerDescription(CallMeta meta,
+        BedParametersResult result, DateRange range) {
+        Date from = range != null ? range.getFrom() : new Date();
+        Date to = range != null ? range.getTo() : new Date();
+
+        String toplayer = Resources.getMsg(meta, I18N_TOPLAYER, I18N_TOPLAYER);
+        return Resources.getMsg(meta, I18N_FACET_BED_POROSITY_TOPLAYER,
+            I18N_FACET_BED_POROSITY_TOPLAYER, new Object[] { from, to, toplayer });
+    }
+
+    protected String createPorositySubLayerDescription(CallMeta meta,
+        BedParametersResult result, DateRange range) {
+        Date from = range != null ? range.getFrom() : new Date();
+        Date to = range != null ? range.getTo() : new Date();
+
+        String sublayer = Resources.getMsg(meta, I18N_SUBLAYER, I18N_SUBLAYER);
+        return Resources.getMsg(meta, I18N_FACET_BED_POROSITY_SUBLAYER,
+            I18N_FACET_BED_POROSITY_SUBLAYER, new Object[] { from, to, sublayer });
+    }
+
+    protected String createDensityTopLayerDescription(CallMeta meta,
+        BedParametersResult result, DateRange range) {
+        Date from = range != null ? range.getFrom() : new Date();
+        Date to = range != null ? range.getTo() : new Date();
+
+        String toplayer = Resources.getMsg(meta, I18N_TOPLAYER, I18N_TOPLAYER);
+        return Resources.getMsg(meta, I18N_FACET_BED_DENSITY_TOPLAYER,
+            I18N_FACET_BED_DENSITY_TOPLAYER, new Object[] { from, to, toplayer });
+    }
+
+    protected String createDensitySubLayerDescription(CallMeta meta,
+        BedParametersResult result, DateRange range) {
+        Date from = range != null ? range.getFrom() : new Date();
+        Date to = range != null ? range.getTo() : new Date();
+
+        String sublayer = Resources.getMsg(meta, I18N_SUBLAYER, I18N_SUBLAYER);
+        return Resources.getMsg(meta, I18N_FACET_BED_DENSITY_SUBLAYER,
+            I18N_FACET_BED_DENSITY_SUBLAYER, new Object[] { from, to, sublayer });
+    }
+
+    protected String createDiameterTopLayerDescription(CallMeta meta,
+        BedDiameterResult result, DateRange range) {
+        Date from = range != null ? range.getFrom() : new Date();
+        Date to = range != null ? range.getTo() : new Date();
+
+        String toplayer = Resources.getMsg(meta, I18N_TOPLAYER, I18N_TOPLAYER);
+
+        return Resources.getMsg(meta, I18N_FACET_BED_DIAMETER_TOPLAYER,
+            I18N_FACET_BED_DIAMETER_TOPLAYER, new Object[] { result.getType(),
+                from, to, toplayer });
+    }
+
+    protected String createDiameterSubLayerDescription(CallMeta meta,
+        BedDiameterResult result, DateRange range) {
+        Date from = range != null ? range.getFrom() : new Date();
+        Date to = range != null ? range.getTo() : new Date();
+
+        String sublayer = Resources.getMsg(meta, I18N_SUBLAYER, I18N_SUBLAYER);
+        return Resources.getMsg(meta, I18N_FACET_BED_DIAMETER_SUBLAYER,
+            I18N_FACET_BED_DIAMETER_SUBLAYER, new Object[] { result.getType(),
+                from, to, sublayer });
+    }
+
+    protected String createDiameterDescription(CallMeta meta,
+        BedQualityDiameterResult result) {
+        return Resources.getMsg(meta, I18N_FACET_BEDLOAD_DIAMETER,
+            I18N_FACET_BEDLOAD_DIAMETER, new Object[] { result.getType() });
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/minfo/CharDiameter.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,88 @@
+package org.dive4elements.river.artifacts.states.minfo;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.artifacts.common.model.KVP;
+import org.dive4elements.river.artifacts.resources.Resources;
+import org.dive4elements.river.artifacts.states.MultiStringArrayState;
+
+public class CharDiameter extends MultiStringArrayState {
+
+    private static final Logger logger = Logger.getLogger(CharDiameter.class);
+
+    public static final String UI_PROVIDER = "parameter-matrix";
+
+    private static final String CHAR_DIAMETER_MIN  = "calc.bed.dmin";
+    private static final String CHAR_DIAMETER_MAX  = "calc.bed.dmax";
+    private static final String CHAR_DIAMETER_90  = "calc.bed.d90";
+    private static final String CHAR_DIAMETER_84  = "calc.bed.d84";
+    private static final String CHAR_DIAMETER_80  = "calc.bed.d80";
+    private static final String CHAR_DIAMETER_75  = "calc.bed.d75";
+    private static final String CHAR_DIAMETER_70  = "calc.bed.d70";
+    private static final String CHAR_DIAMETER_60  = "calc.bed.d60";
+    private static final String CHAR_DIAMETER_50  = "calc.bed.d50";
+    private static final String CHAR_DIAMETER_40  = "calc.bed.d40";
+    private static final String CHAR_DIAMETER_30  = "calc.bed.d30";
+    private static final String CHAR_DIAMETER_25  = "calc.bed.d25";
+    private static final String CHAR_DIAMETER_20  = "calc.bed.d20";
+    private static final String CHAR_DIAMETER_16  = "calc.bed.d16";
+    private static final String CHAR_DIAMETER_10  = "calc.bed.d10";
+
+    public static final String[] CHAR_DIAMETER = {
+        CHAR_DIAMETER_10,
+        CHAR_DIAMETER_16,
+        CHAR_DIAMETER_20,
+        CHAR_DIAMETER_25,
+        CHAR_DIAMETER_30,
+        CHAR_DIAMETER_40,
+        CHAR_DIAMETER_50,
+        CHAR_DIAMETER_60,
+        CHAR_DIAMETER_70,
+        CHAR_DIAMETER_75,
+        CHAR_DIAMETER_80,
+        CHAR_DIAMETER_84,
+        CHAR_DIAMETER_90,
+        CHAR_DIAMETER_MAX,
+        CHAR_DIAMETER_MIN
+    };
+
+    @Override
+    public String getUIProvider() {
+        return UI_PROVIDER;
+    }
+
+    @Override
+    protected KVP<String, String>[] getOptions(
+        Artifact artifact,
+        String parameterName,
+        CallContext context
+    )
+    throws IllegalArgumentException
+    {
+        CallMeta meta   = context.getMeta();
+
+        List<KVP<String, String>> rows = new ArrayList<KVP<String, String>>();
+        String key = parameterName;
+        for (int i = 0; i < CHAR_DIAMETER.length; ++i) {
+            String calc = CHAR_DIAMETER[i];
+            rows.add(new KVP (calc,
+                              Resources.getMsg(meta, calc, calc)));
+        }
+
+        return rows.toArray(new KVP[rows.size()]);
+    }
+
+    @Override
+    protected String getLabelFor(CallContext cc, String parameterName,
+            String value) throws IllegalArgumentException {
+
+        return Resources.getMsg(cc.getMeta(), value, value);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/minfo/DifferenceSelect.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,135 @@
+package org.dive4elements.river.artifacts.states.minfo;
+
+import java.util.List;
+
+import org.w3c.dom.Element;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.access.BedHeightAccess;
+import org.dive4elements.river.artifacts.states.DefaultState;
+import org.dive4elements.river.utils.StringUtil;
+
+/**
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class DifferenceSelect extends DefaultState {
+
+    /**
+     * The default constructor that initializes an empty State object.
+     */
+    public DifferenceSelect() {
+    }
+    /**
+     *  Specify to display nothing (this is kind of a "final" state).
+     */
+    @Override
+    protected String getUIProvider() {
+        return "bedheights_twin_panel";
+    }
+
+    /**
+     * Overridden to do nothing.
+     */
+    @Override
+    public Object computeAdvance(
+        FLYSArtifact artifact,
+        String       hash,
+        CallContext  context,
+        List<Facet>  facets,
+        Object       old
+    ) {
+        //Get data and do stuff, do not calculate
+        return "";
+    }
+
+    @Override
+    protected Element[] createItems(
+        XMLUtils.ElementCreator ec,
+        Artifact                artifact,
+        String                  name,
+        CallContext             context)
+    {
+        Element[] elements = new Element[1];
+        BedHeightAccess bha = new BedHeightAccess((FLYSArtifact)artifact, context);
+        String time = bha.getYearEpoch();
+        elements[0] = createItem(
+            ec,
+            new String[] {"year-epoch", time});
+
+        return elements;
+    }
+
+    /**
+     * Creates the data element used for the static part of DESCRIBE document.
+     */
+    @Override
+    protected Element createStaticData(
+        FLYSArtifact   flys,
+        ElementCreator creator,
+        CallContext    cc,
+        String         name,
+        String         value,
+        String         type
+    ) {
+        Element dataElement = creator.create("data");
+        creator.addAttr(dataElement, "name", name, true);
+        creator.addAttr(dataElement, "type", type, true);
+
+        Element itemElement = creator.create("item");
+        creator.addAttr(itemElement, "value", value, true);
+
+        String[] labels = getLabels(cc, value);
+
+        creator.addAttr(
+            itemElement,
+            "label",
+            labels[0],
+            true);
+        dataElement.appendChild(itemElement);
+
+        return dataElement;
+    }
+
+
+    /**
+     * Get name to display for selected watelerlevels (for example "Q=123")
+     * from the CalculationResult.
+     */
+    public static String[] getLabels(CallContext cc, String value) {
+        String[] recommendations = value.split("#");
+        String displayString = "";
+
+        // Walk over all selected recommendations and create label
+        for (int i = 0; i < recommendations.length; i+=2) {
+            String[] minuendParts = StringUtil
+                .unbracket(recommendations[i+0])
+                .split(";");
+            if(minuendParts.length >= 4) {
+                displayString += "(" + minuendParts[3];
+            }
+            else {
+                displayString += "([error]";
+            }
+
+            displayString += " - ";
+
+            String[] subtrahendParts = StringUtil
+                .unbracket(recommendations[i+1])
+                .split(";");
+            if(subtrahendParts.length >= 4) {
+                displayString += subtrahendParts[3] + ") ";
+            }
+            else {
+                displayString += "[error])";
+            }
+        }
+
+        return new String[] { displayString };
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/minfo/DifferencesState.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,403 @@
+package org.dive4elements.river.artifacts.states.minfo;
+
+import java.text.DateFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifactdatabase.state.FacetActivity;
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.access.BedDifferencesAccess;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.minfo.BedDiffCalculation;
+import org.dive4elements.river.artifacts.model.minfo.BedDiffEpochFacet;
+import org.dive4elements.river.artifacts.model.minfo.BedDiffEpochFilterFacet;
+import org.dive4elements.river.artifacts.model.minfo.BedDiffEpochResult;
+import org.dive4elements.river.artifacts.model.minfo.BedDiffYearFacet;
+import org.dive4elements.river.artifacts.model.minfo.BedDiffYearFilterFacet;
+import org.dive4elements.river.artifacts.model.minfo.BedDiffYearResult;
+import org.dive4elements.river.artifacts.model.minfo.BedDifferencesResult;
+import org.dive4elements.river.artifacts.resources.Resources;
+import org.dive4elements.river.artifacts.states.DefaultState;
+import org.dive4elements.river.utils.Formatter;
+
+/**
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class DifferencesState
+extends DefaultState
+implements FacetTypes
+{
+    /** The logger used in this class. */
+    private static Logger logger = Logger.getLogger(DifferencesState.class);
+    public static final String I18N_DIFF_YEAR = "beddifference.year";
+    public static final String I18N_DIFF_EPOCH = "beddifference.epoch";
+
+    public static final String I18N_FACET_BED_DIFF_YEAR = "facet.bedheight.diff.year";
+    public static final String I18N_FACET_BED_DIFF_YEAR_RAW = "facet.bedheight.diff.year.raw";
+    public static final String I18N_FACET_BED_DIFF_ABSOLUTE = "facet.bedheight.diff.absolute";
+    public static final String I18N_FACET_BED_DIFF_ABSOLUTE_RAW = "facet.bedheight.diff.absolute.raw";
+    public static final String I18N_FACET_BED_DIFF_MORPH = "facet.bedheight.diff.morph";
+    public static final String I18N_FACET_BED_DIFF_EPOCH = "facet.bedheight.diff.epoch";
+    public static final String I18N_FACET_BED_DIFF_EPOCH_RAW = "facet.bedheight.diff.epoch.raw";
+    public static final String I18N_FACET_BED_DIFF_HEIGHT1 = "facet.bedheight.diff.height1";
+    public static final String I18N_FACET_BED_DIFF_HEIGHT1_RAW = "facet.bedheight.diff.height1.raw";
+    public static final String I18N_FACET_BED_DIFF_HEIGHT2 = "facet.bedheight.diff.height2";
+    public static final String I18N_FACET_BED_DIFF_HEIGHT2_RAW = "facet.bedheight.diff.height2.raw";
+
+    public DifferencesState() {
+    }
+
+    @Override
+    public Object computeAdvance(FLYSArtifact artifact, String hash,
+        CallContext context, List<Facet> facets, Object old) {
+        logger.debug("BedQualityState.computeAdvance");
+
+        List<Facet> newFacets = new ArrayList<Facet>();
+
+        BedDifferencesAccess access =
+            new BedDifferencesAccess(artifact, context);
+
+        CalculationResult res = old instanceof CalculationResult ? (CalculationResult) old
+            : new BedDiffCalculation().calculate(access);
+
+        if (facets == null || res == null) {
+            return res;
+        }
+
+        BedDifferencesResult[] results = (BedDifferencesResult[]) res.getData();
+
+        if (results == null || results.length == 0) {
+            logger.warn("Calculation computed no results!");
+            return res;
+        }
+
+        generateFacets(context, newFacets, results, getID(), hash);
+        logger.debug("Created " + newFacets.size() + " new Facets.");
+
+        facets.addAll(newFacets);
+
+        return res;
+    }
+
+    protected void generateFacets(CallContext context, List<Facet> newFacets,
+        BedDifferencesResult[] results, String stateId, String hash) {
+        logger.debug("BedQualityState.generateFacets");
+
+        CallMeta meta = context.getMeta();
+
+        for (int idx = 0; idx < results.length; idx++) {
+            if (results[idx] instanceof BedDiffYearResult) {
+                newFacets.add(new BedDiffYearFacet(
+                    idx,
+                    BED_DIFFERENCE_YEAR,
+                    createBedDiffYearDescription(
+                        meta,
+                        (BedDiffYearResult)results[idx],
+                        true),
+                    ComputeType.ADVANCE,
+                    stateId,
+                    hash));
+                newFacets.add(new BedDiffYearFacet(
+                    idx,
+                    BED_DIFFERENCE_MORPH_WIDTH,
+                    createBedDiffMorphDescription(
+                        meta,
+                        (BedDiffYearResult)results[idx]),
+                    ComputeType.ADVANCE,
+                    stateId,
+                    hash));
+                newFacets.add(new BedDiffYearFacet(
+                    idx,
+                    BED_DIFFERENCE_YEAR_HEIGHT1,
+                    createBedDiffHeightDescription(
+                        meta,
+                        (BedDiffYearResult)results[idx],
+                        0,
+                        true),
+                    ComputeType.ADVANCE,
+                    stateId,
+                    hash));
+                 newFacets.add(new BedDiffYearFacet(
+                    idx,
+                    BED_DIFFERENCE_YEAR_HEIGHT2,
+                    createBedDiffHeightDescription(
+                        meta,
+                        (BedDiffYearResult)results[idx],
+                        1,
+                        true),
+                    ComputeType.ADVANCE,
+                    stateId,
+                    hash));
+                newFacets.add(new BedDiffYearFacet(
+                    idx,
+                    BED_DIFFERENCE_HEIGHT_YEAR,
+                    createBedDiffAbsoluteDescription(
+                        meta,
+                        (BedDiffYearResult)results[idx],
+                        true),
+                    ComputeType.ADVANCE,
+                    stateId,
+                    hash));
+                newFacets.add(new BedDiffYearFilterFacet(
+                    idx,
+                    BED_DIFFERENCE_YEAR_FILTERED,
+                    createBedDiffYearDescription(
+                        meta,
+                        (BedDiffYearResult)results[idx],
+                        false),
+                    ComputeType.ADVANCE,
+                    stateId,
+                    hash));
+                newFacets.add(new BedDiffYearFilterFacet(
+                    idx,
+                    BED_DIFFERENCE_YEAR_HEIGHT1_FILTERED,
+                    createBedDiffHeightDescription(
+                        meta,
+                        (BedDiffYearResult)results[idx],
+                        0,
+                        false),
+                    ComputeType.ADVANCE,
+                    stateId,
+                    hash));
+                 newFacets.add(new BedDiffYearFilterFacet(
+                    idx,
+                    BED_DIFFERENCE_YEAR_HEIGHT2_FILTERED,
+                    createBedDiffHeightDescription(
+                        meta,
+                        (BedDiffYearResult)results[idx],
+                        1,
+                        false),
+                    ComputeType.ADVANCE,
+                    stateId,
+                    hash));
+                newFacets.add(new BedDiffYearFilterFacet(
+                    idx,
+                    BED_DIFFERENCE_HEIGHT_YEAR_FILTERED,
+                    createBedDiffAbsoluteDescription(
+                        meta,
+                        (BedDiffYearResult)results[idx],
+                        false),
+                    ComputeType.ADVANCE,
+                    stateId,
+                    hash));
+            }
+            if (results[idx] instanceof BedDiffEpochResult) {
+                newFacets.add(new BedDiffEpochFacet(
+                    idx,
+                    BED_DIFFERENCE_EPOCH,
+                    createBedDiffEpochDescription(
+                        meta,
+                        (BedDiffEpochResult)results[idx],
+                        true),
+                    ComputeType.ADVANCE,
+                    stateId,
+                    hash));
+                newFacets.add(new BedDiffEpochFacet(
+                    idx,
+                    BED_DIFFERENCE_EPOCH_HEIGHT1,
+                    createBedDiffHeightEpochDescription(
+                        meta,
+                        (BedDiffEpochResult)results[idx],
+                        0,
+                        true),
+                    ComputeType.ADVANCE,
+                    stateId,
+                    hash));
+                newFacets.add(new BedDiffEpochFacet(
+                    idx,
+                    BED_DIFFERENCE_EPOCH_HEIGHT2,
+                    createBedDiffHeightEpochDescription(
+                        meta,
+                        (BedDiffEpochResult)results[idx],
+                        1,
+                        true),
+                    ComputeType.ADVANCE,
+                    stateId,
+                    hash));
+                newFacets.add(new BedDiffEpochFilterFacet(
+                    idx,
+                    BED_DIFFERENCE_EPOCH_FILTERED,
+                    createBedDiffEpochDescription(
+                        meta,
+                        (BedDiffEpochResult)results[idx],
+                        false),
+                    ComputeType.ADVANCE,
+                    stateId,
+                    hash));
+                newFacets.add(new BedDiffEpochFilterFacet(
+                    idx,
+                    BED_DIFFERENCE_EPOCH_HEIGHT1_FILTERED,
+                    createBedDiffHeightEpochDescription(
+                        meta,
+                        (BedDiffEpochResult)results[idx],
+                        0,
+                        false),
+                    ComputeType.ADVANCE,
+                    stateId,
+                    hash));
+                newFacets.add(new BedDiffEpochFilterFacet(
+                    idx,
+                    BED_DIFFERENCE_EPOCH_HEIGHT2_FILTERED,
+                    createBedDiffHeightEpochDescription(
+                        meta,
+                        (BedDiffEpochResult)results[idx],
+                        1,
+                        false),
+                    ComputeType.ADVANCE,
+                    stateId,
+                    hash));
+            }
+        }
+    }
+
+    private String createBedDiffHeightDescription(
+        CallMeta meta,
+        BedDiffYearResult result,
+        int ndx,
+        boolean raw
+    ) {
+        String range = result.getStart() + " - " + result.getEnd();
+
+        String i18nHeight1 = I18N_FACET_BED_DIFF_HEIGHT1;
+        String i18nHeight2 = I18N_FACET_BED_DIFF_HEIGHT2;
+        if (raw) {
+            i18nHeight1 = I18N_FACET_BED_DIFF_HEIGHT1_RAW;
+            i18nHeight2 = I18N_FACET_BED_DIFF_HEIGHT2_RAW;
+        }
+        if (ndx == 0) {
+            return Resources.getMsg(meta, i18nHeight1,
+                i18nHeight2, new Object[] { range });
+        }
+        else {
+            return Resources.getMsg(meta, i18nHeight1,
+                i18nHeight2, new Object[] {range});
+        }
+    }
+
+    private String createBedDiffHeightEpochDescription(
+        CallMeta meta,
+        BedDiffEpochResult result,
+        int ndx,
+        boolean raw
+    ) {
+        DateFormat df = Formatter.getDateFormatter(meta, "yyyy");
+        String range =
+            df.format(result.getStart())
+            + " - " +
+            df.format(result.getEnd());
+
+        String i18nHeight1 = I18N_FACET_BED_DIFF_HEIGHT1;
+        String i18nHeight2 = I18N_FACET_BED_DIFF_HEIGHT2;
+        if (raw) {
+            i18nHeight1 = I18N_FACET_BED_DIFF_HEIGHT1_RAW;
+            i18nHeight2 = I18N_FACET_BED_DIFF_HEIGHT2_RAW;
+        }
+        if (ndx == 0) {
+            return Resources.getMsg(meta, i18nHeight1,
+                i18nHeight1, new Object[] { range });
+        }
+        else {
+            return Resources.getMsg(meta, i18nHeight2,
+                i18nHeight2, new Object[] {range});
+        }
+    }
+
+    protected String createBedDiffYearDescription(
+        CallMeta meta,
+        BedDiffYearResult result,
+        boolean raw
+    ) {
+        String range = result.getStart() + " - " + result.getEnd();
+
+        String i18n = I18N_FACET_BED_DIFF_YEAR;
+        if (raw) {
+            i18n = I18N_FACET_BED_DIFF_YEAR_RAW;
+        }
+        return Resources.getMsg(meta, i18n, i18n, new Object[] { range });
+    }
+
+    protected String createBedDiffMorphDescription(
+        CallMeta meta,
+        BedDiffYearResult result) {
+        String range = result.getStart() + " - " + result.getEnd();
+
+        return Resources.getMsg(meta, I18N_FACET_BED_DIFF_MORPH,
+            I18N_FACET_BED_DIFF_MORPH, new Object[] { range });
+    }
+
+    protected String createBedDiffAbsoluteDescription(
+        CallMeta meta,
+        BedDiffYearResult result,
+        boolean raw
+    ) {
+        String range = result.getStart() + " - " + result.getEnd();
+
+        String i18n = I18N_FACET_BED_DIFF_ABSOLUTE;
+        if (raw) {
+            i18n = I18N_FACET_BED_DIFF_ABSOLUTE_RAW;
+        }
+        return Resources.getMsg(meta, i18n, i18n, new Object[] { range });
+    }
+
+    protected String createBedDiffEpochDescription(
+        CallMeta meta,
+        BedDiffEpochResult result,
+        boolean raw) {
+        DateFormat df = Formatter.getDateFormatter(meta, "yyyy");
+        String range =
+            df.format(result.getStart())
+            + " - " +
+            df.format(result.getEnd());
+
+        String i18n = I18N_FACET_BED_DIFF_EPOCH;
+        if (raw) {
+            i18n = I18N_FACET_BED_DIFF_EPOCH_RAW;
+        }
+        return Resources.getMsg(meta, i18n, i18n, new Object[] { range });
+    }
+
+    static {
+        // Active/deactivate facets.
+        FacetActivity.Registry.getInstance().register(
+            "minfo",
+            new FacetActivity() {
+                @Override
+                public Boolean isInitialActive(
+                    Artifact artifact,
+                    Facet    facet,
+                    String   output
+                ) {
+                    String name = facet.getName();
+                    if (name.equals(BED_DIFFERENCE_EPOCH) ||
+                        name.equals(BED_DIFFERENCE_EPOCH_HEIGHT1) ||
+                        name.equals(BED_DIFFERENCE_EPOCH_HEIGHT2) ||
+                        name.equals(BED_DIFFERENCE_HEIGHT_YEAR) ||
+                        name.equals(BED_DIFFERENCE_YEAR) ||
+                        name.equals(BED_DIFFERENCE_YEAR_HEIGHT1) ||
+                        name.equals(BED_DIFFERENCE_YEAR_HEIGHT2)) {
+                        return Boolean.FALSE;
+                    }
+                    else if (name.equals(BED_DIFFERENCE_MORPH_WIDTH) ||
+                        name.equals(BED_DIFFERENCE_EPOCH_FILTERED) ||
+                        name.equals(BED_DIFFERENCE_EPOCH_HEIGHT1_FILTERED) ||
+                        name.equals(BED_DIFFERENCE_EPOCH_HEIGHT2_FILTERED) ||
+                        name.equals(BED_DIFFERENCE_HEIGHT_YEAR_FILTERED) ||
+                        name.equals(BED_DIFFERENCE_YEAR_FILTERED) ||
+                        name.equals(BED_DIFFERENCE_YEAR_HEIGHT1_FILTERED) ||
+                        name.equals(BED_DIFFERENCE_YEAR_HEIGHT2_FILTERED)){
+                        return Boolean.TRUE;
+                    }
+                    else {
+                        return null;
+                    }
+                }
+            });
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/minfo/SedimentLoadCalculate.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,432 @@
+package org.dive4elements.river.artifacts.states.minfo;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.access.SedimentLoadAccess;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.DataFacet;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.ReportFacet;
+import org.dive4elements.river.artifacts.model.minfo.SedimentLoadCalculation;
+import org.dive4elements.river.artifacts.model.minfo.SedimentLoadFacet;
+import org.dive4elements.river.artifacts.model.minfo.SedimentLoadResult;
+import org.dive4elements.river.artifacts.resources.Resources;
+import org.dive4elements.river.artifacts.states.DefaultState;
+
+
+public class SedimentLoadCalculate
+extends DefaultState
+implements FacetTypes
+{
+
+    private static final long serialVersionUID = 1L;
+
+    private static final Logger logger = Logger
+        .getLogger(SedimentLoadCalculate.class);
+
+    public static final String I18N_FACET_SEDIMENTLOAD_COARSE = "facet.sedimentload.coarse";
+    public static final String I18N_FACET_SEDIMENTLOAD_SAND = "facet.sedimentload.sand";
+    public static final String I18N_FACET_SEDIMENTLOAD_FINE_MIDDLE = "facet.sedimentload.fine_middle";
+    public static final String I18N_FACET_SEDIMENTLOAD_SUSPSAND = "facet.sedimentload.susp_sand";
+    public static final String I18N_FACET_SEDIMENTLOAD_SUSPSANDBED = "facet.sediemntload.susp_sand_bed";
+    public static final String I18N_FACET_SEDIMENTLOAD_SUSPSEDIMENT = "facet.sedimentload.susp_sediment";
+    public static final String I18N_FACET_SEDIMENTLOAD_TOTAL_LOAD = "facet.sedimentload.total_load";
+    public static final String I18N_FACET_SEDIMENTLOAD_TOTAL = "facet.sedimentload.total";
+
+    @Override
+    public Object computeAdvance(FLYSArtifact artifact, String hash,
+        CallContext context, List<Facet> facets, Object old) {
+        logger.debug("SedimentLoadCalculate.computeAdvance");
+
+        List<Facet> newFacets = new ArrayList<Facet>();
+
+        SedimentLoadAccess access = new SedimentLoadAccess(artifact, context);
+
+        CalculationResult res = old instanceof CalculationResult ? (CalculationResult) old
+            : new SedimentLoadCalculation().calculate(access);
+
+        if (facets == null || res == null) {
+            return res;
+        }
+
+        SedimentLoadResult[] results = (SedimentLoadResult[]) res.getData();
+
+        if (results == null || results.length == 0) {
+            logger.warn("Calculation computed no results!");
+            return res;
+        }
+
+        String type = access.getYearEpoch();
+        if (type.equals("year")) {
+            generateYearFacets(context, newFacets, results, getID(), hash);
+        }
+        else if (type.equals("epoch")) {
+            generateEpochFacets(context, newFacets, results, getID(), hash);
+        }
+        else if (type.equals("off_epoch")) {
+            generateOffEpochFacets(context, newFacets, results, getID(), hash);
+        }
+        logger.debug("Created " + newFacets.size() + " new Facets.");
+        if (res.getReport().hasProblems()) {
+            newFacets.add(new ReportFacet(ComputeType.ADVANCE, hash, id));
+        }
+        facets.addAll(newFacets);
+
+        return res;
+    }
+
+    protected void generateYearFacets(CallContext context, List<Facet> newFacets,
+        SedimentLoadResult[] results, String stateId, String hash) {
+        logger.debug("SedimentLoadCalculate.generateFacets");
+
+        CallMeta meta = context.getMeta();
+
+//      newFacets.add(new DataFacet(CSV, "CSV data", ComputeType.ADVANCE, hash, id));
+        for (int idx = 0; idx < results.length; idx++) {
+            SedimentLoadResult res = results[idx];
+            if (res.hasCoarseData()) {
+                newFacets.add(new SedimentLoadFacet(
+                    idx,
+                    SEDIMENT_LOAD_COARSE,
+                    Resources.getMsg(
+                        meta,
+                        I18N_FACET_SEDIMENTLOAD_COARSE,
+                        I18N_FACET_SEDIMENTLOAD_COARSE) +
+                        " - " + res.getStartYear(),
+                    ComputeType.ADVANCE,
+                    stateId,
+                    hash));
+            }
+            if (res.hasSandData()) {
+                newFacets.add(new SedimentLoadFacet(
+                    idx,
+                    SEDIMENT_LOAD_SAND,
+                    Resources.getMsg(
+                        meta,
+                        I18N_FACET_SEDIMENTLOAD_SAND,
+                        I18N_FACET_SEDIMENTLOAD_SAND) +
+                        " - " + res.getStartYear(),
+                    ComputeType.ADVANCE,
+                    stateId,
+                    hash));
+            }
+            if (res.hasFineMiddleData()) {
+                newFacets.add(new SedimentLoadFacet(
+                    idx,
+                    SEDIMENT_LOAD_FINEMIDDLE,
+                    Resources.getMsg(
+                        meta,
+                        I18N_FACET_SEDIMENTLOAD_FINE_MIDDLE,
+                        I18N_FACET_SEDIMENTLOAD_FINE_MIDDLE) +
+                        " - " + res.getStartYear(),
+                    ComputeType.ADVANCE,
+                    stateId,
+                    hash));
+            }
+            if (res.hasSuspSandData()) {
+                newFacets.add(new SedimentLoadFacet(
+                    idx,
+                    SEDIMENT_LOAD_SUSP_SAND,
+                    Resources.getMsg(
+                        meta,
+                        I18N_FACET_SEDIMENTLOAD_SUSPSAND,
+                        I18N_FACET_SEDIMENTLOAD_SUSPSAND)
+                        + " - " + res.getStartYear(),
+                    ComputeType.ADVANCE,
+                    stateId,
+                    hash));
+            }
+            if (res.hasSuspSandBedData()) {
+                newFacets.add(new SedimentLoadFacet(
+                    idx,
+                    SEDIMENT_LOAD_SUSP_SAND_BED,
+                    Resources.getMsg(
+                        meta,
+                        I18N_FACET_SEDIMENTLOAD_SUSPSANDBED,
+                        I18N_FACET_SEDIMENTLOAD_SUSPSANDBED) +
+                        " - " + res.getStartYear(),
+                    ComputeType.ADVANCE,
+                    stateId,
+                    hash));
+            }
+            if (res.hasSuspSedimentData()) {
+                newFacets.add(new SedimentLoadFacet(
+                    idx,
+                    SEDIMENT_LOAD_SUSP_SEDIMENT,
+                    Resources.getMsg(
+                        meta,
+                        I18N_FACET_SEDIMENTLOAD_SUSPSEDIMENT,
+                        I18N_FACET_SEDIMENTLOAD_SUSPSEDIMENT) +
+                        " - " + res.getStartYear(),
+                    ComputeType.ADVANCE,
+                    stateId,
+                    hash));
+
+            }
+            if (res.hasTotalData()) {
+                newFacets.add(new SedimentLoadFacet(
+                    idx,
+                    SEDIMENT_LOAD_TOTAL,
+                    Resources.getMsg(
+                        meta,
+                        I18N_FACET_SEDIMENTLOAD_TOTAL,
+                        I18N_FACET_SEDIMENTLOAD_TOTAL) +
+                        " - " + res.getStartYear(),
+                    ComputeType.ADVANCE,
+                    stateId,
+                    hash));
+            }
+        }
+    }
+
+    protected void generateEpochFacets(
+        CallContext context,
+        List<Facet> newFacets,
+        SedimentLoadResult[] results,
+        String stateId,
+        String hash
+    ) {
+        logger.debug("SedimentLoadCalculate.generateEpochFacets");
+
+        CallMeta meta = context.getMeta();
+
+//      newFacets.add(new DataFacet(CSV, "CSV data", ComputeType.ADVANCE, hash, id));
+        for (int idx = 0; idx < results.length; idx++) {
+            SedimentLoadResult res = results[idx];
+            if (res.hasCoarseData()) {
+                newFacets.add(new SedimentLoadFacet(
+                    idx,
+                    SEDIMENT_LOAD_COARSE,
+                    Resources.getMsg(
+                        meta,
+                        I18N_FACET_SEDIMENTLOAD_COARSE,
+                        I18N_FACET_SEDIMENTLOAD_COARSE) +
+                        " - " + res.getStartYear() + "-" + res.getEndYear(),
+                    ComputeType.ADVANCE,
+                    stateId,
+                    hash));
+            }
+            if (res.hasSandData()) {
+                newFacets.add(new SedimentLoadFacet(
+                    idx,
+                    SEDIMENT_LOAD_SAND,
+                    Resources.getMsg(
+                        meta,
+                        I18N_FACET_SEDIMENTLOAD_SAND,
+                        I18N_FACET_SEDIMENTLOAD_SAND) +
+                        " - " + res.getStartYear() + "-" + res.getEndYear(),
+                    ComputeType.ADVANCE,
+                    stateId,
+                    hash));
+            }
+            if (res.hasFineMiddleData()) {
+                newFacets.add(new SedimentLoadFacet(
+                    idx,
+                    SEDIMENT_LOAD_FINEMIDDLE,
+                    Resources.getMsg(
+                        meta,
+                        I18N_FACET_SEDIMENTLOAD_FINE_MIDDLE,
+                        I18N_FACET_SEDIMENTLOAD_FINE_MIDDLE) +
+                        " - " + res.getStartYear(),
+                    ComputeType.ADVANCE,
+                    stateId,
+                    hash));
+            }
+            if (res.hasSuspSandData()) {
+                newFacets.add(new SedimentLoadFacet(
+                    idx,
+                    SEDIMENT_LOAD_SUSP_SAND,
+                    Resources.getMsg(
+                        meta,
+                        I18N_FACET_SEDIMENTLOAD_SUSPSAND,
+                        I18N_FACET_SEDIMENTLOAD_SUSPSAND)
+                        + " - " + res.getStartYear() + "-" + res.getEndYear(),
+                    ComputeType.ADVANCE,
+                    stateId,
+                    hash));
+            }
+            if (res.hasSuspSandBedData()) {
+                newFacets.add(new SedimentLoadFacet(
+                    idx,
+                    SEDIMENT_LOAD_SUSP_SAND_BED,
+                    Resources.getMsg(
+                        meta,
+                        I18N_FACET_SEDIMENTLOAD_SUSPSANDBED,
+                        I18N_FACET_SEDIMENTLOAD_SUSPSANDBED) +
+                        " - " + res.getStartYear() + "-" + res.getEndYear(),
+                    ComputeType.ADVANCE,
+                    stateId,
+                    hash));
+            }
+            if (res.hasSuspSedimentData()) {
+                newFacets.add(new SedimentLoadFacet(
+                    idx,
+                    SEDIMENT_LOAD_SUSP_SEDIMENT,
+                    Resources.getMsg(
+                        meta,
+                        I18N_FACET_SEDIMENTLOAD_SUSPSEDIMENT,
+                        I18N_FACET_SEDIMENTLOAD_SUSPSEDIMENT) +
+                        " - " + res.getStartYear() + "-" + res.getEndYear(),
+                    ComputeType.ADVANCE,
+                    stateId,
+                    hash));
+
+            }
+            if (res.hasTotalLoadData()) {
+                newFacets.add(new SedimentLoadFacet(
+                    idx,
+                    SEDIMENT_LOAD_TOTAL_LOAD,
+                    Resources.getMsg(
+                        meta,
+                        I18N_FACET_SEDIMENTLOAD_TOTAL_LOAD,
+                        I18N_FACET_SEDIMENTLOAD_TOTAL_LOAD) +
+                        " - " + res.getStartYear() + "-" + res.getEndYear(),
+                    ComputeType.ADVANCE,
+                    stateId,
+                    hash));
+            }
+            if (res.hasTotalData()) {
+                newFacets.add(new SedimentLoadFacet(
+                    idx,
+                    SEDIMENT_LOAD_TOTAL,
+                    Resources.getMsg(
+                        meta,
+                        I18N_FACET_SEDIMENTLOAD_TOTAL,
+                        I18N_FACET_SEDIMENTLOAD_TOTAL) +
+                        " - " + res.getStartYear() + "-" + res.getEndYear(),
+                    ComputeType.ADVANCE,
+                    stateId,
+                    hash));
+            }
+        }
+    }
+
+    protected void generateOffEpochFacets(
+        CallContext context,
+        List<Facet> newFacets,
+        SedimentLoadResult[] results,
+        String stateId,
+        String hash
+    ) {
+        logger.debug("SedimentLoadCalculate.generateOffEpochFacets");
+
+        CallMeta meta = context.getMeta();
+
+//      newFacets.add(new DataFacet(CSV, "CSV data", ComputeType.ADVANCE, hash, id));
+        for (int idx = 0; idx < results.length; idx++) {
+            SedimentLoadResult res = results[idx];
+            if (res.hasCoarseData()) {
+                newFacets.add(new SedimentLoadFacet(
+                    idx,
+                    SEDIMENT_LOAD_COARSE,
+                    Resources.getMsg(
+                        meta,
+                        I18N_FACET_SEDIMENTLOAD_COARSE,
+                        I18N_FACET_SEDIMENTLOAD_COARSE) +
+                        " - " + res.getStartYear() + "-" + res.getEndYear(),
+                    ComputeType.ADVANCE,
+                    stateId,
+                    hash));
+            }
+            if (res.hasSandData()) {
+                newFacets.add(new SedimentLoadFacet(
+                    idx,
+                    SEDIMENT_LOAD_SAND,
+                    Resources.getMsg(
+                        meta,
+                        I18N_FACET_SEDIMENTLOAD_SAND,
+                        I18N_FACET_SEDIMENTLOAD_SAND) +
+                        " - " + res.getStartYear() + "-" + res.getEndYear(),
+                    ComputeType.ADVANCE,
+                    stateId,
+                    hash));
+            }
+            if (res.hasFineMiddleData()) {
+                newFacets.add(new SedimentLoadFacet(
+                    idx,
+                    SEDIMENT_LOAD_FINEMIDDLE,
+                    Resources.getMsg(
+                        meta,
+                        I18N_FACET_SEDIMENTLOAD_FINE_MIDDLE,
+                        I18N_FACET_SEDIMENTLOAD_FINE_MIDDLE) +
+                        " - " + res.getStartYear() + "-" + res.getEndYear(),
+                    ComputeType.ADVANCE,
+                    stateId,
+                    hash));
+            }
+            if (res.hasSuspSandData()) {
+                newFacets.add(new SedimentLoadFacet(
+                    idx,
+                    SEDIMENT_LOAD_SUSP_SAND,
+                    Resources.getMsg(
+                        meta,
+                        I18N_FACET_SEDIMENTLOAD_SUSPSAND,
+                        I18N_FACET_SEDIMENTLOAD_SUSPSAND) +
+                        " - " + res.getStartYear() + "-" + res.getEndYear(),
+                    ComputeType.ADVANCE,
+                    stateId,
+                    hash));
+            }
+            if (res.hasSuspSandBedData()) {
+                newFacets.add(new SedimentLoadFacet(
+                    idx,
+                    SEDIMENT_LOAD_SUSP_SAND_BED,
+                    Resources.getMsg(
+                        meta,
+                        I18N_FACET_SEDIMENTLOAD_SUSPSANDBED,
+                        I18N_FACET_SEDIMENTLOAD_SUSPSANDBED) +
+                        " - " + res.getStartYear() + "-" + res.getEndYear(),
+                    ComputeType.ADVANCE,
+                    stateId,
+                    hash));
+            }
+            if (res.hasSuspSedimentData()) {
+                newFacets.add(new SedimentLoadFacet(
+                    idx,
+                    SEDIMENT_LOAD_SUSP_SEDIMENT,
+                    Resources.getMsg(
+                        meta,
+                        I18N_FACET_SEDIMENTLOAD_SUSPSEDIMENT,
+                        I18N_FACET_SEDIMENTLOAD_SUSPSEDIMENT) +
+                        " - " + res.getStartYear() + "-" + res.getEndYear(),
+                    ComputeType.ADVANCE,
+                    stateId,
+                    hash));
+
+            }
+            if (res.hasTotalLoadData()) {
+                newFacets.add(new SedimentLoadFacet(
+                    idx,
+                    SEDIMENT_LOAD_TOTAL_LOAD,
+                    Resources.getMsg(
+                        meta,
+                        I18N_FACET_SEDIMENTLOAD_TOTAL_LOAD,
+                        I18N_FACET_SEDIMENTLOAD_TOTAL_LOAD) +
+                        " - " + res.getStartYear() + "-" + res.getEndYear(),
+                    ComputeType.ADVANCE,
+                    stateId,
+                    hash));
+            }
+            if (res.hasTotalData()) {
+                newFacets.add(new SedimentLoadFacet(
+                    idx,
+                    SEDIMENT_LOAD_TOTAL,
+                    Resources.getMsg(
+                        meta,
+                        I18N_FACET_SEDIMENTLOAD_TOTAL,
+                        I18N_FACET_SEDIMENTLOAD_TOTAL) +
+                        " - " + res.getStartYear() + "-" + res.getEndYear(),
+                    ComputeType.ADVANCE,
+                    stateId,
+                    hash));
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/minfo/SedimentLoadDistanceSelect.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,13 @@
+package org.dive4elements.river.artifacts.states.minfo;
+
+import org.dive4elements.river.artifacts.states.DistanceOnlySelect;
+
+
+public class SedimentLoadDistanceSelect
+extends DistanceOnlySelect
+{
+    @Override
+    protected String getUIProvider() {
+        return "minfo.sedimentload_distance_select";
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/minfo/SedimentLoadEpochSelect.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,25 @@
+package org.dive4elements.river.artifacts.states.minfo;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.river.artifacts.states.DefaultState;
+
+
+public class SedimentLoadEpochSelect
+extends DefaultState
+{
+    /** The logger used in this class. */
+    private static Logger logger = Logger.getLogger(SedimentLoadEpochSelect.class);
+
+
+    /**
+     * The default constructor that initializes an empty State object.
+     */
+    public SedimentLoadEpochSelect() {
+    }
+
+    @Override
+    protected String getUIProvider() {
+        return "minfo.sedimentload_epoch_select";
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/minfo/SedimentLoadOffEpochSelect.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,25 @@
+package org.dive4elements.river.artifacts.states.minfo;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.river.artifacts.states.DefaultState;
+
+
+public class SedimentLoadOffEpochSelect
+extends DefaultState
+{
+   /** The logger used in this class. */
+    private static Logger logger = Logger.getLogger(SedimentLoadOffEpochSelect.class);
+
+
+    /**
+     * The default constructor that initializes an empty State object.
+     */
+    public SedimentLoadOffEpochSelect() {
+    }
+
+    @Override
+    protected String getUIProvider() {
+        return "minfo.sedimentload_offepoch_select";
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/minfo/SedimentLoadUnit.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,59 @@
+package org.dive4elements.river.artifacts.states.minfo;
+
+import org.apache.log4j.Logger;
+import org.w3c.dom.Element;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+import org.dive4elements.river.artifacts.resources.Resources;
+import org.dive4elements.river.artifacts.states.DefaultState;
+
+
+public class SedimentLoadUnit
+extends DefaultState
+{
+   /** The logger used in this class. */
+    private static Logger logger = Logger.getLogger(SedimentLoadUnit.class);
+
+
+    /**
+     * The default constructor that initializes an empty State object.
+     */
+    public SedimentLoadUnit() {
+    }
+
+    @Override
+    protected String getUIProvider() {
+        return "minfo.sedimentload_unit_select";
+    }
+
+    @Override
+    protected Element[] createItems(
+        XMLUtils.ElementCreator ec,
+        Artifact                artifact,
+        String                  name,
+        CallContext             context)
+    {
+        CallMeta meta = context.getMeta();
+        Element[] elements = new Element[2];
+        elements[0] = createItem(
+                ec,
+                new String[] {
+                    Resources.getMsg(meta,
+                        "state.minfo.t_per_a",
+                        "state.minfo.t_per_a"),
+                    "t_per_a"});
+
+        elements[1] = createItem(
+            ec,
+            new String[] {
+                Resources.getMsg(meta,
+                    "state.minfo.m3_per_a",
+                    "state.minfo.m3_per_a"),
+                "m3_per_a"});
+
+       return elements;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/minfo/SedimentLoadYearSelect.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,48 @@
+package org.dive4elements.river.artifacts.states.minfo;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.Artifact;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+import org.dive4elements.river.artifacts.access.SedimentLoadAccess;
+import org.dive4elements.river.artifacts.states.DefaultState;
+
+
+/** State in which to fetch years for sedminent load calculation. */
+public class SedimentLoadYearSelect
+extends DefaultState
+{
+    /** The logger used in this class. */
+    private static Logger logger = Logger.getLogger(SedimentLoadYearSelect.class);
+
+
+    /**
+     * The default constructor that initializes an empty State object.
+     */
+    public SedimentLoadYearSelect() {
+    }
+
+
+    /** Year Select Widget. */
+    @Override
+    protected String getUIProvider() {
+        return "minfo.sedimentload_year_select";
+    }
+
+
+    @Override
+    public boolean validate(Artifact artifact)
+    throws IllegalArgumentException
+    {
+        SedimentLoadAccess access = new SedimentLoadAccess((FLYSArtifact) artifact, null);
+
+        // Second year should be later than first.
+        if (access.getPeriod() != null && access.getPeriod()[1] < access.getPeriod()[0])
+           throw new IllegalArgumentException("error_years_wrong");
+
+        return true;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/minfo/YearEpochSelect.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,72 @@
+package org.dive4elements.river.artifacts.states.minfo;
+
+import org.w3c.dom.Element;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.resources.Resources;
+import org.dive4elements.river.artifacts.states.DefaultState;
+
+/**
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class YearEpochSelect extends DefaultState {
+
+    /**
+     * The default constructor that initializes an empty State object.
+     */
+    public YearEpochSelect() {
+    }
+
+    @Override
+    protected String getUIProvider() {
+        return "minfo.bed.year_epoch";
+    }
+
+    @Override
+    protected Element[] createItems(
+        XMLUtils.ElementCreator ec,
+        Artifact                artifact,
+        String                  name,
+        CallContext             context)
+    {
+        CallMeta meta = context.getMeta();
+        Element[] elements;
+        if (((FLYSArtifact)artifact).getCurrentStateId()
+            .equals("state.minfo.sediment.load.year_epoch")
+        ) {
+            elements = new Element[3];
+            elements[2] = createItem(
+                ec,
+                new String[] {
+                    Resources.getMsg(meta,
+                        "state.minfo.off_epoch",
+                        "state.minfo.off_epoch"),
+                    "off_epoch"});
+        }
+        else {
+            elements = new Element[2];
+        }
+        elements[0] = createItem(
+                ec,
+                new String[] {
+                    Resources.getMsg(meta,
+                        "state.minfo.year",
+                        "state.minfo.year"),
+                    "year"});
+
+        elements[1] = createItem(
+            ec,
+            new String[] {
+                Resources.getMsg(meta,
+                    "state.minfo.epoch",
+                    "state.minfo.epoch"),
+                "epoch"});
+
+       return elements;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/sq/OutlierMethod.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,76 @@
+package org.dive4elements.river.artifacts.states.sq;
+
+import org.apache.log4j.Logger;
+import org.w3c.dom.Element;
+
+import org.dive4elements.artifactdatabase.ProtocolUtils;
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+import org.dive4elements.river.artifacts.resources.Resources;
+import org.dive4elements.river.artifacts.states.DefaultState;
+
+
+public class OutlierMethod
+extends DefaultState
+{
+    /** The logger that is used in this class.*/
+    private static Logger logger = Logger.getLogger(OutlierMethod.class);
+
+    public static final String STD_DEV   = "outlier.method.std-dev";
+    public static final String GRUBBS = "outlier.method.grubbs";
+
+    public static final String[] METHODS = {
+        STD_DEV,
+        GRUBBS,
+  };
+
+
+    @Override
+    protected String getUIProvider() {
+        return "";
+    }
+
+    @Override
+    protected Element[] createItems(
+        XMLUtils.ElementCreator cr,
+        Artifact    artifact,
+        String      name,
+        CallContext context)
+    {
+        CallMeta meta = context.getMeta();
+
+        Element[] methods = new Element[METHODS.length];
+
+        int i = 0;
+
+        for (String method: METHODS) {
+            methods[i++] = createItem(
+                cr, new String[] {
+                    Resources.getMsg(meta, method, method),
+                    method
+                });
+        }
+
+        return methods;
+    }
+
+
+    @Override
+    protected Element createItem(XMLUtils.ElementCreator cr, Object obj) {
+        Element item  = ProtocolUtils.createArtNode(cr, "item", null, null);
+        Element label = ProtocolUtils.createArtNode(cr, "label", null, null);
+        Element value = ProtocolUtils.createArtNode(cr, "value", null, null);
+
+        String[] arr = (String[]) obj;
+
+        label.setTextContent(arr[0]);
+        value.setTextContent(arr[1]);
+
+        item.appendChild(label);
+        item.appendChild(value);
+
+        return item;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/sq/SQPeriodSelect.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,17 @@
+package org.dive4elements.river.artifacts.states.sq;
+
+import org.dive4elements.river.artifacts.states.PeriodsSelect;
+
+public class SQPeriodSelect extends PeriodsSelect {
+
+    public static final String UI_PROVIDER = "sq.period.select";
+
+    private static final long serialVersionUID = 1L;
+
+
+    @Override
+    public String getUIProvider() {
+        return UI_PROVIDER;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/sq/SQStaticState.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,80 @@
+package org.dive4elements.river.artifacts.states.sq;
+
+import java.text.DateFormat;
+import java.util.List;
+
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.access.StaticSQRelationAccess;
+import org.dive4elements.river.artifacts.model.sq.StaticSQContainer;
+import org.dive4elements.river.artifacts.model.sq.StaticSQFactory;
+import org.dive4elements.river.artifacts.model.sq.StaticSQRelation;
+import org.dive4elements.river.artifacts.states.StaticState;
+
+
+public class SQStaticState
+extends StaticState
+{
+    private static final Logger log =
+        Logger.getLogger(SQStaticState.class);
+
+    public SQStaticState() {
+        super();
+    }
+
+    public SQStaticState(String name) {
+        super(name);
+    }
+
+    @Override
+    public Object staticCompute(
+        List<Facet> facets,
+        FLYSArtifact artifact
+    ) {
+        StaticSQRelationAccess access = new StaticSQRelationAccess(artifact);
+
+        String river = access.getRiver();
+        String measurementStation = access.getMeasurementStation();
+
+        int ms = -1;
+        try {
+            ms = Integer.parseInt(measurementStation);
+        }
+        catch (NumberFormatException nfe) {
+            log.error("Unparseable measurement station: " + measurementStation);
+            return null;
+        }
+
+        StaticSQContainer sqRelations =
+            StaticSQFactory.getSQRelations(river, ms);
+
+        for (StaticSQRelation.Parameter p: StaticSQRelation.Parameter.values()) {
+
+            List<StaticSQRelation> relations =
+                sqRelations.getRelationsByParameter(p);
+
+            if (!relations.isEmpty()) {
+                int count = 0;
+
+                for (StaticSQRelation relation : relations) {
+                    String name = "sq_" + p.toString().toLowerCase() + "_curve";
+                    DateFormat df =
+                        DateFormat.getDateInstance(DateFormat.SHORT);
+                    String desc = p.toString() + ": " +
+                        df.format(relation.getStartTime()) + " - " +
+                        df.format(relation.getStopTime());
+                    facets.add(new StaticSQRelationFacet(
+                        count,
+                        name,
+                        desc,
+                        relation));
+                    count++;
+                }
+            }
+        }
+        return null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/states/sq/StaticSQRelationFacet.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,49 @@
+package org.dive4elements.river.artifacts.states.sq;
+
+import org.dive4elements.artifactdatabase.state.DefaultFacet;
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.math.fitting.Function;
+import org.dive4elements.river.artifacts.math.fitting.FunctionFactory;
+import org.dive4elements.river.artifacts.model.sq.SQFunction;
+import org.dive4elements.river.artifacts.model.sq.StaticSQRelation;
+
+
+public class StaticSQRelationFacet
+extends DefaultFacet
+implements Facet
+{
+    public static final String FUNCTION = "sq-pow";
+
+    private StaticSQRelation relation;
+
+
+    public StaticSQRelationFacet(
+        int ndx,
+        String name,
+        String description,
+        StaticSQRelation relation) {
+        super(ndx, name, description);
+        this.relation = relation;
+    }
+
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        double qmax = relation.getQmax();
+        double[] coeffs = new double[] {relation.getA(), relation.getB()};
+        Function func = FunctionFactory.getInstance().getFunction(FUNCTION);
+        org.dive4elements.river.artifacts.math.Function function =
+            func.instantiate(coeffs);
+        SQFunction sqf = new SQFunction(function, 0, qmax);
+        return sqf;
+    }
+
+    @Override
+    public Facet deepCopy() {
+        StaticSQRelationFacet copy =
+            new StaticSQRelationFacet(index, name, description, relation);
+        copy.set(this);
+        return copy;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/transitions/DefaultTransition.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,104 @@
+package org.dive4elements.river.artifacts.transitions;
+
+import org.w3c.dom.Node;
+
+import org.dive4elements.artifacts.Artifact;
+
+import org.dive4elements.artifactdatabase.state.State;
+import org.dive4elements.artifactdatabase.transition.Transition;
+
+
+/**
+ * The default implementation of a <code>Transition</code>.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DefaultTransition implements Transition {
+
+    /** The ID of the current state */
+    protected String from;
+
+    /** The ID of the target state */
+    protected String to;
+
+
+    /**
+     * The default constructor.
+     */
+    public DefaultTransition() {
+    }
+
+
+    /**
+     * The default constructor.
+     *
+     * @param from The current state.
+     * @param to The target state.
+     */
+    public DefaultTransition(String from, String to) {
+        this.from = from;
+        this.to   = to;
+    }
+
+
+    public void init(Node config) {
+        // nothing to do in the default transition
+    }
+
+
+    /**
+     * Returns the current state ID.
+     *
+     * @return the current state ID.
+     */
+    public String getFrom() {
+        return from;
+    }
+
+
+    /**
+     * Returns the target state ID.
+     *
+     * @return the target state ID.
+     */
+    public String getTo() {
+        return to;
+    }
+
+
+    /**
+     * Set the current state ID.
+     *
+     * @param from current state ID.
+     */
+    public void setFrom(String from) {
+        this.from = from;
+    }
+
+
+    /**
+     * Set the target state ID.
+     *
+     * @param to the target state ID.
+     */
+    public void setTo(String to) {
+        this.to = to;
+    }
+
+
+    /**
+     * Determines if its valid to step from state <i>a</i> of an artifact
+     * <i>artifact</i> to state <i>b</i>. This method always returns true - no
+     * validation takes place.
+     *
+     * @param artifact The owner artifact of state a and b.
+     * @param a The current state.
+     * @param b The target state.
+     *
+     * @return true, if it is valid to step from a to b, otherwise false.
+     */
+    public boolean isValid(Artifact artifact, State a, State b) {
+        return true;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/transitions/TransitionFactory.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,71 @@
+package org.dive4elements.river.artifacts.transitions;
+
+import javax.xml.xpath.XPathConstants;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Node;
+
+import org.dive4elements.artifactdatabase.transition.Transition;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class TransitionFactory {
+
+    /** The logger used in this class */
+    private static Logger logger = Logger.getLogger(TransitionFactory.class);
+
+    /** The XPath to the classname of the transition */
+    public static final String XPATH_TRANSITION = "@transition";
+
+    /** The XPath to the current state ID */
+    public static final String XPATH_CURRENT_STATE = "from/@state";
+
+    /** The XPath to the target state ID */
+    public static final String XPATH_TARGET_STATE = "to/@state";
+
+
+    /**
+     * Creates a new Transition based on the configured class provided by
+     * <code>transitionConf</code>.
+     *
+     * @param transitionConf The configuration of the transition.
+     *
+     * @return a Transition.
+     */
+    public static Transition createTransition(Node transitionConf) {
+        String clazz = (String) XMLUtils.xpath(
+            transitionConf, XPATH_TRANSITION, XPathConstants.STRING);
+
+        Transition transition = null;
+
+        try {
+            transition = (Transition) Class.forName(clazz).newInstance();
+
+            String from = (String) XMLUtils.xpath(
+                transitionConf, XPATH_CURRENT_STATE, XPathConstants.STRING);
+            String to = (String) XMLUtils.xpath(
+                transitionConf, XPATH_TARGET_STATE, XPathConstants.STRING);
+
+            transition.init(transitionConf);
+            transition.setFrom(from);
+            transition.setTo(to);
+        }
+        catch (InstantiationException ie) {
+            logger.error(ie, ie);
+        }
+        catch (IllegalAccessException iae) {
+            logger.error(iae, iae);
+        }
+        catch (ClassNotFoundException cnfe) {
+            logger.error(cnfe, cnfe);
+        }
+
+        return transition;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/transitions/ValueCompareTransition.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,119 @@
+package org.dive4elements.river.artifacts.transitions;
+
+import javax.xml.xpath.XPathConstants;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Node;
+
+import org.dive4elements.artifacts.Artifact;
+
+import org.dive4elements.artifactdatabase.data.StateData;
+import org.dive4elements.artifactdatabase.state.State;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+
+/**
+ * This transition compares data objects with a <i>equal</i> or <i>notequal</i>
+ * operator.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ValueCompareTransition extends DefaultTransition {
+
+    /** The logger that is used in this transition.*/
+    private static Logger log = Logger.getLogger(ValueCompareTransition.class);
+
+
+    /** XPath that points to the condition's operator.*/
+    public static final String XPATH_OPERATOR = "condition/@operator";
+
+    /** XPath that points to the condition's value.*/
+    public static final String XPATH_VALUE    = "condition/@value";
+
+    /** XPath that points  to the condition's dataname.*/
+    public static final String XPATH_DATANAME = "condition/@data";
+
+    /** The value of the 'equal' operator.*/
+    public static final String OPERATOR_EQUAL = "equal";
+
+    /** The value of the 'not equal' operator.*/
+    public static final String OPERATOR_NOTEQUAL = "notequal";
+
+
+    /** The operator.*/
+    protected String operator;
+
+    /** The value used for comparison.*/
+    protected String value;
+
+    /** The name of the data used for the comparison.*/
+    protected String dataname;
+
+
+
+    public ValueCompareTransition() {
+    }
+
+
+    public ValueCompareTransition(String from, String to) {
+        super(from, to);
+    }
+
+
+    /** Setup member values from the document. */
+    @Override
+    public void init(Node config) {
+        log.debug("ValueCompareTransition.init");
+
+        String tmp = (String) XMLUtils.xpath(
+            config,
+            XPATH_OPERATOR,
+            XPathConstants.STRING);
+        operator = tmp.trim().toLowerCase();
+
+        value = (String) XMLUtils.xpath(
+            config,
+            XPATH_VALUE,
+            XPathConstants.STRING);
+
+        dataname = (String) XMLUtils.xpath(
+            config,
+            XPATH_DATANAME,
+            XPathConstants.STRING);
+    }
+
+
+    @Override
+    public boolean isValid(Artifact artifact, State a, State b) {
+        log.debug("ValueCompareTransition.isValid");
+
+        FLYSArtifact flysArtifact = (FLYSArtifact) artifact;
+
+        StateData dataObj = flysArtifact.getData(dataname);
+        String    dataVal = dataObj != null ? (String) dataObj.getValue() : "";
+
+        if (log.isDebugEnabled()) {
+            log.debug("Compare settings:");
+            log.debug("-- dataname: " + dataname);
+            log.debug("-- operator: " + operator);
+            log.debug("-- compare value: " + value);
+            log.debug("-- state value: " + dataVal);
+        }
+
+        if (operator.equals(OPERATOR_EQUAL)) {
+            return value.equals(dataVal);
+        }
+        else if (operator.equals(OPERATOR_NOTEQUAL)) {
+            return !(value.equals(dataVal));
+        }
+
+        log.error("Wrong operator set! No comparison takes place.");
+
+        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/flys-artifacts/src/main/java/org/dive4elements/river/collections/AttributeParser.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,167 @@
+package org.dive4elements.river.collections;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.xpath.XPathConstants;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import org.dive4elements.artifacts.ArtifactNamespaceContext;
+
+import org.dive4elements.artifactdatabase.state.DefaultOutput;
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifactdatabase.state.Output;
+import org.dive4elements.artifactdatabase.state.Settings;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+
+import org.dive4elements.river.artifacts.model.ManagedDomFacet;
+import org.dive4elements.river.exports.ChartSettings;
+
+/**
+ * Access parts of the Attribute parts of a FLYSCollections description
+ * document.
+ */
+public class AttributeParser {
+
+    /** Constant XPath that points to the outputmodes of an artifact. */
+    public static final String XPATH_ARTIFACT_OUTPUTMODES =
+        "/art:attribute/art:outputs/art:output";
+
+
+    private static Logger logger = Logger.getLogger(AttributeParser.class);
+
+
+    protected Document attributeDocument;
+
+    protected CollectionAttribute attribute;
+
+
+    public AttributeParser(Document attributeDocument) {
+        this.attributeDocument = attributeDocument;
+    }
+
+
+    public void parse() {
+        logger.debug("AttributeParser.parse");
+
+        attribute = new CollectionAttribute();
+
+        NodeList outs = (NodeList) XMLUtils.xpath(
+            attributeDocument,
+            XPATH_ARTIFACT_OUTPUTMODES,
+            XPathConstants.NODESET,
+            ArtifactNamespaceContext.INSTANCE);
+
+        int num = outs != null ? outs.getLength() : 0;
+
+        logger.debug("Attribute has " + num + " outputs.");
+
+        for (int i = 0; i < num; i++) {
+            Node out = outs.item(i);
+
+            parseOutput(out);
+        }
+    }
+
+
+    public CollectionAttribute getCollectionAttribute() {
+        if (attribute == null) {
+            parse();
+        }
+
+        return attribute;
+    }
+
+
+    public Document getAttributeDocument() {
+        return attributeDocument;
+    }
+
+
+    public Map<String, Output> getOuts() {
+        return attribute.getOutputs();
+    }
+
+
+    /**
+     * Access all facets.
+     * @return list of all facets.
+     */
+    public List<Facet> getFacets() {
+        return attribute.getFacets();
+    }
+
+
+    protected void parseOutput(Node out) {
+        String name = ((Element)out).getAttribute("name");
+
+        if (name.length() == 0) {
+            logger.warn("No Output name specified. Cancel parsing!");
+            return;
+        }
+
+        Output o = attribute.getOutput(name);
+
+        if (o == null) {
+            logger.debug("Create new output: " + name);
+
+            o = new DefaultOutput(name, null, null);
+            attribute.addOutput(name, o);
+        }
+
+        parseSettings(out, name);
+        parseItems(out, name);
+    }
+
+    private static final Node getChild(Element element, String name) {
+        NodeList children = element.getChildNodes();
+        for (int i = 0, N = children.getLength(); i < N; ++i) {
+            Node child = children.item(i);
+            if (child.getNodeType() == Node.ELEMENT_NODE
+            && child.getLocalName().equals(name)) {
+                return child;
+            }
+        }
+        return null;
+    }
+
+
+    protected void parseSettings(Node out, String outname) {
+        Node settingsNode = getChild((Element)out, "settings");
+
+        if (settingsNode == null) {
+            logger.debug("No Settings found for Output '" + outname + "'");
+            return;
+        }
+
+        Settings settings = ChartSettings.parse(settingsNode);
+        attribute.setSettings(outname, settings);
+    }
+
+
+    protected void parseItems(Node out, String outname) {
+        String uri = ArtifactNamespaceContext.NAMESPACE_URI;
+        Element element = (Element)out;
+
+        NodeList themes = element.getElementsByTagNameNS(uri, "facet");
+
+        int num = themes.getLength();
+
+        logger.debug("Output has " + num + " themes.");
+
+        for (int i = 0; i < num; i++) {
+            Element theme = (Element) themes.item(i);
+            if (theme.getParentNode() == out) {
+                attribute.addFacet(outname, new ManagedDomFacet(theme));
+            }
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/collections/AttributeWriter.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,313 @@
+package org.dive4elements.river.collections;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.ArtifactDatabase;
+import org.dive4elements.artifacts.ArtifactDatabaseException;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifactdatabase.state.FacetActivity;
+import org.dive4elements.artifactdatabase.state.Output;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.model.ManagedFacet;
+
+/**
+ * Create attribute- element of describe document of an ArtifactCollection.
+ * The attribute-element contains the merged output of all outputmodes and
+ * facets that are part of the collection.
+ */
+public class AttributeWriter {
+
+    /** ArtifactDatabase used to fetch Artifacts. */
+    protected ArtifactDatabase db = null;
+
+    protected Map<String, Output> oldAttr;
+
+    protected Map<String, Output> newAttr;
+
+    /** List of already seen facets. */
+    protected List<Facet>         oldFacets;
+
+    /** List of "new" facets. */
+    protected List<Facet>         newFacets;
+
+    /**
+     * "Compatibility matrix", mapws list of facet names to output names.
+     * Any facet that is not found in the list for a specific output will
+     * not be added to the resulting document.
+     */
+    protected Map<String, List<String>> compatibilities;
+
+
+    /** The result of the <i>write()</i> operation.*/
+    protected CollectionAttribute attribute;
+
+
+    private static Logger logger = Logger.getLogger(AttributeWriter.class);
+
+
+    /**
+     * Create a AttributeWriter.
+     * Attributes not present in newAttr will not be included in the document.
+     * @param db      Database to fetch artifacts.
+     * @param oldAttr "Old" (possibly user-changed) outputs.
+     * @param newAttr "New" (eventually re-read in its original, unchanged
+     *                form) outputs.
+     * @param matrix Compatibility matrix, mapping output names to list of
+     *               facet names that can be included in this out.
+     */
+    public AttributeWriter(
+        ArtifactDatabase    db,
+        CollectionAttribute attribute,
+        Map<String, Output> oldAttr,
+        List<Facet>         oldFacets,
+        Map<String, Output> newAttr,
+        List<Facet>         newFacets,
+        Map<String, List<String>> matrix)
+    {
+        this.db        = db;
+        this.attribute = attribute;
+        this.oldAttr   = oldAttr;
+        this.newAttr   = newAttr;
+        this.oldFacets = oldFacets;
+        this.newFacets = newFacets;
+        this.compatibilities = matrix;
+    }
+
+
+    /**
+     * Create document by merging outputs given in
+     * constructor.
+     *
+     * The "new" set rules about existance of attributes, so anything not
+     * present in it will not be included in the resulting document.
+     * The "old" set rules about the content of attributes (as user changes
+     * are recorded here and not in the new set).
+     *
+     * @return document with merged outputs as described.
+     */
+    protected CollectionAttribute write() {
+
+        boolean debug = logger.isDebugEnabled();
+
+        for (Map.Entry<String, Output> entry: newAttr.entrySet()) {
+            String outName = entry.getKey();
+            Output a       = entry.getValue();
+
+            if (!attribute.hasOutput(outName)) {
+                attribute.addOutput(outName, a);
+            }
+
+            attribute.clearFacets(outName);
+
+            if (debug) {
+                logger.debug("Merge Output: " + outName);
+                logger.debug("   old Facets: " + oldFacets.size());
+                logger.debug("   new Facets: " + newFacets.size());
+            }
+
+            writeOutput(a.getName(), newFacets, oldFacets);
+        }
+
+        // THIS CALL IS ABSOLUTELY NECESSARY!
+        attribute.cleanEmptyOutputs();
+
+        return attribute;
+    }
+
+
+    /**
+     * @param outputName the "new" outputs name
+     * @param newOutFacets Facets of the new outputs
+     * @param oldOutFacets Facets of the old outputs (can be null)
+     */
+    protected void writeOutput(
+        String      outputName,
+        List<Facet> newOutFacets,
+        List<Facet> oldOutFacets
+    ) {
+        List<String> compatFacets = this.compatibilities.get(outputName);
+
+        if (logger.isDebugEnabled() && compatFacets != null) {
+            logger.debug("Compabitle Facets:");
+            for (String compatible: compatFacets) {
+                logger.debug(   "- " + compatible);
+            }
+        }
+
+        try {
+            writeFacets(outputName, newOutFacets, oldOutFacets, compatFacets);
+        }
+        catch (ArtifactDatabaseException ade) {
+            logger.error(ade, ade);
+        }
+    }
+
+
+    /**
+     * @param newFacets the new facets
+     * @param oldFacets the old facets
+     * @param compatibleFacets List of facets to accept
+     * @return true if any facets are written to the out.
+     */
+    protected boolean writeFacets(
+        String        outputName,
+        List<Facet>   newFacets,
+        List<Facet>   oldFacets,
+        List<String>  compatibleFacets)
+    throws ArtifactDatabaseException
+    {
+        if (compatibleFacets == null) {
+            logger.warn("No compatible facets, not generating out " + outputName + ".");
+            return false;
+        }
+
+        int num = newFacets.size();
+
+        // Add all new Facets either in their old state or (if really
+        // new) as they are.
+        List<ManagedFacet> currentFacets      = new ArrayList<ManagedFacet>();
+        List<ManagedFacet> genuinelyNewFacets = new ArrayList<ManagedFacet>();
+
+        boolean debug = logger.isDebugEnabled();
+        if (debug) {
+           logger.debug("Compatible facets are " + compatibleFacets);
+        }
+
+        for (int i = 0; i < num; i++) {
+            ManagedFacet facet = (ManagedFacet) newFacets.get(i);
+
+            if (debug) {
+                logger.debug("Try to add Facet: " + facet.getName());
+            }
+
+            if (!compatibleFacets.contains(facet.getName())) {
+                logger.warn("Have incompatible facet, skip: " + facet.getName());
+                continue;
+            }
+            else {
+                logger.debug("Have compatible facet: " + facet.getName());
+            }
+
+            ManagedFacet picked = pickFacet(facet, oldFacets);
+
+            if (facet.equals(picked)) {
+                genuinelyNewFacets.add(picked);
+            }
+            else {
+                currentFacets.add(picked);
+            }
+        }
+
+        FacetActivity.Registry registry = FacetActivity.Registry.getInstance();
+
+        // With each genuinely new Facet, figure out whether it comes to live
+        // in/activate.
+        for (ManagedFacet newMF: genuinelyNewFacets) {
+            FLYSArtifact flys =
+                (FLYSArtifact)db.getRawArtifact(newMF.getArtifact());
+
+            boolean isActive = registry.isInitialActive(
+                flys.getName(), flys, newMF, outputName);
+
+            newMF.setActive(isActive ? 1 : 0);
+        }
+
+        // For each genuinely new Facet check positional conflicts.
+        for (ManagedFacet newMF: genuinelyNewFacets) {
+            boolean conflicts = true;
+            // Loop until all conflicts resolved.
+            while (conflicts) {
+                conflicts = false;
+                for (ManagedFacet oldMF: currentFacets) {
+                    if (newMF.getPosition() == oldMF.getPosition()) {
+                        conflicts = true;
+                        if (debug) {
+                            logger.debug(
+                                "Positional conflict while merging " +
+                                "facets, pushing newest facet 1 up (" +
+                                newMF.getPosition() + ")");
+                        }
+                        newMF.setPosition(newMF.getPosition() + 1);
+                        break;
+                    }
+                }
+            }
+            currentFacets.add(newMF);
+        }
+
+        // Fill/correct "gaps" (e.g. position 1,2,5 are taken, after gap filling
+        // expect positions 1,2,3 [5->3])
+        // Preparations to be able to detect gaps.
+        Map<Integer, ManagedFacet> mfmap =
+            new HashMap<Integer, ManagedFacet>();
+        int max = 0;
+        for (ManagedFacet mf: currentFacets) {
+            int pos = mf.getPosition();
+            mfmap.put(Integer.valueOf(pos), mf);
+            if (pos > max) max = pos;
+        }
+
+        // Finally do gap correction.
+        if (max != currentFacets.size()) {
+            int gap = 0;
+            for (int i = 1; i <= max; i++) {
+                ManagedFacet mf = mfmap.get(Integer.valueOf(i));
+                if (mf == null) {
+                    gap++;
+                    continue;
+                }
+                mf.setPosition(mf.getPosition() - gap);
+            }
+        }
+
+        // Now add all facets.
+        for (ManagedFacet oldMF: currentFacets) {
+            attribute.addFacet(outputName, oldMF);
+        }
+
+        return !currentFacets.isEmpty();
+    }
+
+
+    /**
+     * Returns the facet to be added to Document.
+     * Return the new facet only if the "same" facet was not present before.
+     * Return the "old" facet otherwise (user-defined information sticks
+     * to it).
+     * @param facet     the new facet.
+     * @param oldFacets the old facets, new facet is compared against each of
+     *                  these.
+     * @return facet if genuinely new, matching old facet otherwise.
+     */
+    protected ManagedFacet pickFacet(ManagedFacet facet, List<Facet> oldFacets)
+    {
+        if (oldFacets == null) {
+            logger.debug("No old facets to compare a new to found.");
+            return facet;
+        }
+
+        String hash = facet.getName() + facet.getIndex() + facet.getArtifact();
+
+        // Compare "new" facet with all old facets.
+        // Take oldFacet if that facet was already present (otherwise
+        // information is lost, the new one otherwise.
+        for (Facet oFacet: oldFacets) {
+            ManagedFacet oldFacet = (ManagedFacet) oFacet;
+            String oldHash = oldFacet.getName()
+                           + oldFacet.getIndex()
+                           + oldFacet.getArtifact();
+            if (hash.equals(oldHash)) {
+                return oldFacet;
+            }
+        }
+        return facet;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/collections/CollectionAttribute.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,313 @@
+package org.dive4elements.river.collections;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.ArtifactNamespaceContext;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator;
+
+import org.dive4elements.artifactdatabase.state.DefaultOutput;
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifactdatabase.state.Output;
+import org.dive4elements.artifactdatabase.state.Settings;
+
+
+/** Create attribute part of collection document. */
+public class CollectionAttribute {
+
+    /** Privately owned logger. */
+    private static final Logger logger =
+        Logger.getLogger(CollectionAttribute.class);
+
+    protected ElementCreator ec;
+
+    protected Map<String, Output> outputMap;
+
+    protected Node loadedRecommendations;
+
+
+    public CollectionAttribute() {
+    }
+
+
+    public void addOutput(String key, Output output) {
+        if (outputMap == null) {
+            outputMap = new HashMap<String, Output>();
+        }
+
+        if (key != null && key.length() > 0 && output != null) {
+            outputMap.put(
+                key,
+                new DefaultOutput(
+                    output.getName(),
+                    output.getDescription(),
+                    output.getMimeType(),
+                    new ArrayList<Facet>(),
+                    output.getType()));
+        }
+    }
+
+
+    public void cleanEmptyOutputs() {
+        if (outputMap == null) {
+            return;
+        }
+
+        List<String> removeUs = new ArrayList<String>();
+
+        for (Map.Entry<String, Output> entry: outputMap.entrySet()) {
+            Output o = entry.getValue();
+
+            List<Facet> facets = o.getFacets();
+            if (facets == null || facets.isEmpty()) {
+                removeUs.add(entry.getKey());
+            }
+        }
+
+        for (String key: removeUs) {
+            outputMap.remove(key);
+        }
+    }
+
+
+    public void setSettings(String outputKey, Settings settings) {
+        if (settings == null) {
+            logger.warn("Tried to set empty Settings for '" + outputKey + "'");
+            return;
+        }
+
+        if (outputMap == null) {
+            logger.warn("Tried to add facet 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);
+            return;
+        }
+
+        output.setSettings(settings);
+    }
+
+
+    public void addFacet(String outputKey, Facet facet) {
+        if (facet == null) {
+            logger.warn("Tried to add empty facet.");
+            return;
+        }
+
+        if (outputMap == null) {
+            logger.warn("Tried to add facet 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);
+            return;
+        }
+
+        logger.debug("Add facet for '" + outputKey + "': " + facet.getName());
+        output.addFacet(facet);
+    }
+
+
+    public void setLoadedRecommendations(Node loadedRecommendations) {
+        // TODO Replace this Node with a Java class object.
+        this.loadedRecommendations = loadedRecommendations;
+    }
+
+
+    public void clearFacets(String outputKey) {
+        if (outputKey == null || outputKey.length() == 0) {
+            logger.warn("Tried to clear Facets, but no Output key specified!");
+            return;
+        }
+
+        if (outputMap == null) {
+            logger.warn("Tried to clear Facets, but no Outputs existing!");
+            return;
+        }
+
+        Output output = outputMap.get(outputKey);
+        if (output == null) {
+            logger.warn("Tried to clear Facets for unknown Out: " + outputKey);
+            return;
+        }
+
+        output.setFacets(new ArrayList<Facet>());
+    }
+
+
+    public Document toXML() {
+        Document doc = XMLUtils.newDocument();
+
+        ec = new ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element root = ec.create("attribute");
+
+        appendOutputs(root);
+        appendLoadedRecommendations(root);
+
+        doc.appendChild(root);
+
+        return doc;
+    }
+
+    /** True if output with outputName is found. */
+    public boolean hasOutput(String outputName) {
+        return getOutput(outputName) != null;
+    }
+
+    public Map<String, Output> getOutputs() {
+        return outputMap;
+    }
+
+
+    public Output getOutput(String name) {
+        if (name == null || name.length() == 0) {
+            logger.warn("No Output name specified.");
+            return null;
+        }
+
+        if (outputMap == null || outputMap.isEmpty()) {
+            logger.warn("Tried to retrieve Output, but no Outputs existing.");
+            return null;
+        }
+
+        return outputMap.get(name);
+    }
+
+
+    public List<Facet> getFacets(String output) {
+        if (output == null || output.length() == 0) {
+            logger.warn("No Output name specified.");
+            return new ArrayList<Facet>();
+        }
+
+        if (outputMap == null) {
+            logger.warn("Tried to retrieve facets, but no Outputs existing.");
+            return new ArrayList<Facet>();
+        }
+
+        Output o = outputMap.get(output);
+
+        if (o == null) {
+            logger.warn("No Output '" + output + "' existing.");
+            return new ArrayList<Facet>();
+        }
+
+        return o.getFacets();
+    }
+
+
+    public List<Facet> getFacets() {
+        List<Facet> allFacets = new ArrayList<Facet>();
+
+        if (outputMap == null || outputMap.isEmpty()) {
+            logger.warn("No Outputs existing.");
+            return allFacets;
+        }
+
+        for (String outputName: outputMap.keySet()) {
+            allFacets.addAll(getFacets(outputName));
+        }
+
+        return allFacets;
+    }
+
+
+    protected void appendOutputs(Element root) {
+        if (outputMap == null || outputMap.isEmpty()) {
+            logger.warn("No outputs to append.");
+            return;
+        }
+
+        logger.debug("Append " + outputMap.size() + " Output Elements.");
+
+        Element outputsEl = ec.create("outputs");
+
+        for (Map.Entry<String, Output> entry: outputMap.entrySet()) {
+            appendOutput(outputsEl, entry.getKey(), entry.getValue());
+        }
+
+        root.appendChild(outputsEl);
+    }
+
+
+    protected void appendOutput(Element root, String name, Output output) {
+        if (name == null || name.length() == 0 || output == null) {
+            logger.warn("Tried to appendOutput, but Output is invalid.");
+            return;
+        }
+
+        logger.debug("Append Output Element for '" + name + "'");
+
+        Element outputEl = ec.create("output");
+        ec.addAttr(outputEl, "name", name);
+
+        appendSettings(outputEl, output.getSettings());
+        appendFacets(outputEl, output.getFacets());
+
+        root.appendChild(outputEl);
+    }
+
+
+    protected void appendSettings(Element root, Settings settings) {
+        if (settings == null) {
+            logger.warn("Tried to append Settings, but Settings is empty!");
+            return;
+        }
+
+        settings.toXML(root);
+    }
+
+
+    protected void appendFacets(Element root, List<Facet> facets) {
+        if (facets == null || facets.isEmpty()) {
+            logger.warn("Tried to append 0 Facets.");
+            return;
+        }
+
+        Document owner = root.getOwnerDocument();
+
+        logger.debug("Append " + facets.size() + " facets.");
+
+        for (Facet facet: facets) {
+            Node facetNode = facet.toXML(owner);
+
+            if (facetNode != null) {
+                root.appendChild(facetNode);
+            }
+        }
+    }
+
+
+    protected void appendLoadedRecommendations(Element root) {
+        if (loadedRecommendations == null) {
+            logger.debug("No loaded recommendations existing yet.");
+            return;
+        }
+
+        Document owner = root.getOwnerDocument();
+
+        root.appendChild(owner.importNode(loadedRecommendations, true));
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/collections/CollectionDescriptionHelper.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,203 @@
+package org.dive4elements.river.collections;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import javax.xml.xpath.XPathConstants;
+
+import org.apache.log4j.Logger;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import org.dive4elements.artifacts.ArtifactDatabase;
+import org.dive4elements.artifacts.ArtifactDatabaseException;
+import org.dive4elements.artifacts.ArtifactNamespaceContext;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator;
+
+
+public class CollectionDescriptionHelper {
+
+    private static final Logger logger =
+        Logger.getLogger(CollectionDescriptionHelper.class);
+
+
+    public static final String XPATH_ARTIFACT_STATE_DATA =
+        "/art:result/art:ui/art:static/art:state/art:data";
+
+    /** Constant XPath that points to the outputmodes of an artifact. */
+    public static final String XPATH_ARTIFACT_OUTPUTMODES =
+        "/art:result/art:outputmodes";
+
+
+    protected ElementCreator ec;
+
+    protected CallContext      context;
+    protected ArtifactDatabase database;
+
+    protected String name;
+    protected String uuid;
+    protected Date   creation;
+    protected long   ttl;
+
+    protected List<String>        artifacts;
+    protected CollectionAttribute attribute;
+
+
+    /**
+     * @param name The name of the collection.
+     * @param uuid The uuid of the collection.
+     * @param creation The creation time of the collection.
+     * @param ttl The time to live of the collection.
+     */
+    public CollectionDescriptionHelper(
+        String      name,
+        String      uuid,
+        Date        creation,
+        long        ttl,
+        CallContext callContext
+    ) {
+        this.name      = name;
+        this.uuid      = uuid;
+        this.creation  = creation;
+        this.ttl       = ttl;
+        this.context   = callContext;
+        this.database  = callContext.getDatabase();
+        this.artifacts = new ArrayList<String>();
+    }
+
+
+    public void addArtifact(String uuid) {
+        if (uuid != null && uuid.length() > 0) {
+            artifacts.add(uuid);
+        }
+    }
+
+
+    public void setAttribute(CollectionAttribute attribute) {
+        if (attribute != null) {
+            this.attribute = attribute;
+        }
+    }
+
+
+    public Document toXML() {
+        Document doc = XMLUtils.newDocument();
+
+        ec = new ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element root = ec.create("artifact-collection");
+        doc.appendChild(root);
+
+        String creationTime = creation != null
+            ? Long.toString(creation.getTime())
+            : "";
+
+        ec.addAttr(root, "name", name, true);
+        ec.addAttr(root, "uuid", uuid, true);
+        ec.addAttr(root, "creation", creationTime, true);
+        ec.addAttr(root, "ttl", String.valueOf(ttl), true);
+
+        appendArtifacts(root);
+        appendAttribute(root);
+
+        return doc;
+    }
+
+
+    /**
+     * Appends parts of the DESCRIBE document of each Artifact to <i>root</i>.
+     *
+     * @param root The root node.
+     */
+    protected void appendArtifacts(Element root) {
+        Element artifactsEl = ec.create("artifacts");
+
+        for (String uuid: artifacts) {
+            try {
+                Element e = buildArtifactNode(uuid);
+
+                if (e != null) {
+                    artifactsEl.appendChild(e);
+                }
+            }
+            catch (ArtifactDatabaseException dbe) {
+                logger.warn(dbe, dbe);
+            }
+        }
+
+        root.appendChild(artifactsEl);
+    }
+
+
+    /**
+     * Create the Artifacts Node that contains outputmode and statedata.
+     *
+     * @param uuid uuid of the artifact.
+     */
+    protected Element buildArtifactNode(String uuid)
+    throws    ArtifactDatabaseException
+    {
+        logger.debug("Append artifact '" + uuid + "' to collection description");
+
+        // TODO
+        String hash = "MYHASH";
+
+        Element ci = ec.create("artifact");
+        ec.addAttr(ci, "uuid", uuid, true);
+        ec.addAttr(ci, "hash", hash, true);
+
+        // XXX I am not sure if it works well every time with an empty document
+        // in the describe operation of an artifact.
+        Document description = database.describe(uuid, null, context.getMeta());
+
+        // Add outputmode element(s).
+        Node outputModes = (Node) XMLUtils.xpath(
+            description,
+            XPATH_ARTIFACT_OUTPUTMODES,
+            XPathConstants.NODE,
+            ArtifactNamespaceContext.INSTANCE);
+
+        if (outputModes != null) {
+            Document doc = ci.getOwnerDocument();
+            ci.appendChild(doc.importNode(outputModes, true));
+        }
+
+        // Add state-data element(s).
+        Node dataNode = ci.appendChild(
+            ci.getOwnerDocument().createElement("art:data-items"));
+
+        NodeList dataNodes = (NodeList) XMLUtils.xpath(
+            description,
+            XPATH_ARTIFACT_STATE_DATA,
+            XPathConstants.NODESET,
+            ArtifactNamespaceContext.INSTANCE);
+
+        if (dataNodes != null) {
+            Document doc = ci.getOwnerDocument();
+            for (int i = 0, D = dataNodes.getLength(); i < D; i++) {
+                dataNode.appendChild(doc.importNode(dataNodes.item(i), true));
+            }
+        }
+
+        return ci;
+    }
+
+
+    protected void appendAttribute(Element root) {
+        if (attribute != null) {
+            Document owner = root.getOwnerDocument();
+            Document attr  = attribute.toXML();
+
+            root.appendChild(owner.importNode(attr.getFirstChild(), true));
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/collections/FLYSArtifactCollection.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,625 @@
+package org.dive4elements.river.collections;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.xpath.XPathConstants;
+
+import org.apache.log4j.Logger;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import org.dive4elements.artifactdatabase.Backend;
+import org.dive4elements.artifactdatabase.Backend.PersistentArtifact;
+import org.dive4elements.artifactdatabase.DefaultArtifactCollection;
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifactdatabase.state.Output;
+import org.dive4elements.artifactdatabase.state.Settings;
+import org.dive4elements.artifactdatabase.state.StateEngine;
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.ArtifactDatabase;
+import org.dive4elements.artifacts.ArtifactDatabaseException;
+import org.dive4elements.artifacts.ArtifactNamespaceContext;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.context.FLYSContext;
+import org.dive4elements.river.exports.OutGenerator;
+import org.dive4elements.river.exports.OutputHelper;
+import org.dive4elements.river.utils.FLYSUtils;
+
+/**
+ * Collection of artifacts, can do outs, describe.
+ * Lots of stuff done in AttributeParser and AttributeWriter.
+ * Critical out and facet merging.
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class FLYSArtifactCollection extends DefaultArtifactCollection {
+    /** The logger used in this class. */
+    private static Logger log = Logger.getLogger(FLYSArtifactCollection.class);
+
+    /** Constant XPath that points to the outputmodes of an artifact. */
+    public static final String XPATH_ARTIFACT_OUTPUTMODES =
+        "/art:result/art:outputmodes";
+
+    public static final String XPATH_ARTIFACT_STATE_DATA =
+        "/art:result/art:ui/art:static/art:state/art:data";
+
+    public static final String XPATH_COLLECTION_ITEMS =
+        "/art:result/art:artifact-collection/art:collection-item";
+
+    public static final String XPATH_OUT_NAME = "/art:action/@art:name";
+
+    public static final String XPATH_OUT_TYPE = "/art:action/@art:type";
+
+    /** Xpath to master artifacts uuid. */
+    public static final String XPATH_MASTER_UUID =
+        "/art:artifact-collection/art:artifact/@art:uuid";
+
+    public static final String XPATH_LOADED_RECOMMENDATIONS =
+        "/art:attribute/art:loaded-recommendations";
+
+
+    /**
+     * Create and return description Document for this collection.
+     */
+    @Override
+    public Document describe(CallContext context) {
+        log.debug("FLYSArtifactCollection.describe: " + identifier);
+
+        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);
+
+            oldAttrs = removeAttributes(oldAttrs, context);
+            parser   = new AttributeParser(oldAttrs);
+
+            CollectionAttribute newAttr = mergeAttributes(
+                db, context, parser, aUUIDs);
+
+            if (checkOutputSettings(newAttr, context)) {
+                saveCollectionAttribute(db, context, newAttr);
+            }
+
+            helper.setAttribute(newAttr);
+
+            if (aUUIDs != null) {
+                for (String uuid: aUUIDs) {
+                    helper.addArtifact(uuid);
+                }
+            }
+        }
+        catch (ArtifactDatabaseException ade) {
+            log.error("Error while merging attribute documents.", ade);
+
+            helper.setAttribute(parser.getCollectionAttribute());
+        }
+
+        return helper.toXML();
+    }
+
+
+    /**
+     * Merge the current art:outputs nodes with the the outputs provided by the
+     * artifacts in the Collection.
+     *
+     * @param uuids Artifact uuids.
+     */
+    protected CollectionAttribute mergeAttributes(
+        ArtifactDatabase db,
+        CallContext      context,
+        AttributeParser  oldParser,
+        String[]         uuids
+    ) {
+        CollectionAttribute cAttribute =
+            buildOutAttributes(db, context, oldParser, uuids);
+
+        if (cAttribute == null) {
+            log.warn("mergeAttributes: cAttribute == null");
+            return null;
+        }
+
+        cAttribute.setLoadedRecommendations(
+            getLoadedRecommendations(oldParser.getAttributeDocument()));
+
+        saveCollectionAttribute(db, context, cAttribute);
+
+        return cAttribute;
+    }
+
+
+    protected Document removeAttributes(Document attrs, CallContext context) {
+        Node outs = (Node) XMLUtils.xpath(
+            attrs,
+            "/art:attribute/art:outputs",
+            XPathConstants.NODE,
+            ArtifactNamespaceContext.INSTANCE);
+
+        NodeList nodes = (NodeList) XMLUtils.xpath(
+            attrs,
+            "/art:attribute/art:outputs/art:output",
+            XPathConstants.NODESET,
+            ArtifactNamespaceContext.INSTANCE);
+
+        if (nodes != null) {
+            for (int i = 0; i < nodes.getLength(); i++) {
+                Element e = (Element)nodes.item(i);
+                if(!outputExists(e.getAttribute("name"), context)) {
+                    outs.removeChild(e);
+                }
+            }
+        }
+        return attrs;
+    }
+
+
+    /**
+     * True if current MasterArtifact has given output.
+     * @param name Name of the output of interest.
+     * @param context current context
+     * @return true if current master artifact has given output.
+     */
+    protected boolean outputExists(String name, CallContext context) {
+        FLYSArtifact master = getMasterArtifact(context);
+        List<Output> outList = master.getOutputs(context);
+
+        for (Output o : outList) {
+            if (name.equals(o.getName())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * @param db The ArtifactDatabase which is required to save the attribute
+     * into.
+     * @param attribute The CollectionAttribute that should be stored in the
+     * database.
+     *
+     * @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.");
+
+        Document doc = attribute.toXML();
+
+        try {
+            // Save the merged document into database.
+            db.setCollectionAttribute(identifier(), context.getMeta(), doc);
+
+            log.info("Saving CollectionAttribute was successful.");
+
+            return true;
+        }
+        catch (ArtifactDatabaseException adb) {
+            log.error(adb, adb);
+        }
+
+        return false;
+    }
+
+
+    /**
+     * Merge the recommendations which have already been loaded from the old
+     * attribute document into the new attribute document. This is necessary,
+     * because mergeAttributes() only merges the art:outputs nodes - all
+     * other nodes are skipped.
+     */
+    protected Node getLoadedRecommendations(Document oldAttrs) {
+        Element loadedRecoms = (Element) XMLUtils.xpath(
+            oldAttrs,
+            XPATH_LOADED_RECOMMENDATIONS,
+            XPathConstants.NODE,
+            ArtifactNamespaceContext.INSTANCE);
+
+        return loadedRecoms;
+    }
+
+
+    /**
+     * Evaluates the Output settings. If an Output has no Settings set, the
+     * relevant OutGenerator is used to initialize a default Settings object.
+     *
+     * @param attribute The CollectionAttribute.
+     * @param cc The CallContext.
+     *
+     * @return true, if the CollectionAttribute was modified, otherwise false.
+     */
+    protected boolean checkOutputSettings(
+        CollectionAttribute attribute,
+        CallContext         cc
+    ) {
+        boolean modified = false;
+
+        Map<String, Output> outputMap = attribute != null
+            ? attribute.getOutputs()
+            : null;
+
+        if (outputMap == null || outputMap.isEmpty()) {
+            log.debug("No Output Settings check necessary.");
+            return modified;
+        }
+
+
+        for (Map.Entry<String, Output> entry: outputMap.entrySet()) {
+            String outName = entry.getKey();
+            Output output  = entry.getValue();
+
+            if (outName.equals("sq_overview")) {
+                continue;
+            }
+            Settings settings = output.getSettings();
+
+            if (settings == null) {
+                log.debug("No Settings set for Output '" + outName + "'.");
+                output.setSettings(
+                    createInitialOutputSettings(cc, attribute, outName));
+
+                modified = true;
+            }
+        }
+
+        return modified;
+    }
+
+
+    /**
+     * This method uses the the OutGenerator for the specified Output
+     * <i>out</i> to create an initial Settings object.
+     *
+     * @param cc The CallContext object.
+     * @param attr The CollectionAttribute.
+     * @param out The name of the output.
+     *
+     * @return a default Settings object for the specified Output.
+     */
+    protected Settings createInitialOutputSettings(
+        CallContext         cc,
+        CollectionAttribute attr,
+        String              out
+    ) {
+        OutGenerator outGen = FLYSContext.getOutGenerator(cc, out, null);
+
+        if (outGen == null) {
+            return null;
+        }
+
+        // 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);
+
+        try {
+            Document outAttr = getAttribute(cc, attr, out);
+            OutputHelper helper = new OutputHelper(identifier());
+            helper.doOut(outGen, out, out, outAttr, cc);
+        }
+        catch (ArtifactDatabaseException adbe) {
+            log.error(adbe, adbe);
+        }
+        catch (IOException ioe) {
+            log.error(ioe, ioe);
+        }
+
+        return outGen.getSettings();
+    }
+
+
+    @Override
+    public void out(
+        String       type,
+        Document     format,
+        OutputStream out,
+        CallContext  context)
+    throws IOException
+    {
+        boolean debug = log.isDebugEnabled();
+
+        long reqBegin = System.currentTimeMillis();
+
+        if (debug) {
+            log.debug(XMLUtils.toString(format));
+            log.debug("FLYSArtifactCollection.out");
+        }
+
+        String name = XMLUtils.xpathString(
+            format, XPATH_OUT_NAME, ArtifactNamespaceContext.INSTANCE);
+
+        String subtype = XMLUtils.xpathString(
+            format, XPATH_OUT_TYPE, ArtifactNamespaceContext.INSTANCE);
+
+        if (debug) {
+            log.debug("-> Output name = " + name);
+            log.debug("-> Output type = " + type);
+            log.debug("-> Output subtype = " + subtype);
+        }
+
+        OutGenerator generator = null;
+        if (type != null
+             && type.length() > 0
+             && type.indexOf("chartinfo") > 0)
+        {
+            generator = FLYSContext.getOutGenerator(context, type, subtype);
+        }
+        else {
+            generator = FLYSContext.getOutGenerator(context, name, subtype);
+        }
+
+        if (generator == null) {
+            log.error("There is no generator specified for output: " + name);
+            // TODO Throw an exception.
+
+            return;
+        }
+
+        Document        oldAttrs  = getAttribute();
+        AttributeParser parser    = new AttributeParser(oldAttrs);
+        CollectionAttribute cAttr = parser.getCollectionAttribute();
+
+        Output output = cAttr.getOutput(name);
+        Settings settings = null;
+        if (output != null) {
+            settings = output.getSettings();
+
+            if (debug) {
+                List<Facet> facets = output.getFacets();
+                for(Facet facet: facets) {
+                    log.debug("  -- Facet " + facet.getName());
+                }
+            }
+        }
+
+        generator.init(format, out, context);
+        generator.setSettings(settings);
+        generator.setCollection(this);
+        prepareMasterArtifact(generator, context);
+
+        try {
+            Document attr = getAttribute(context, cAttr, name);
+            OutputHelper helper = new OutputHelper(identifier());
+            if (name.equals("sq_overview")) {
+                helper.doOut(generator, name, subtype, format, context);
+            }
+            helper.doOut(generator, name, subtype, attr, context);
+            generator.generate();
+        }
+        catch (ArtifactDatabaseException adbe) {
+            log.error(adbe, adbe);
+        }
+
+        if (debug) {
+            long duration = System.currentTimeMillis() -reqBegin;
+            log.info("Processing out(" + name + ") took " + duration + " ms.");
+        }
+    }
+
+
+    /**
+     * 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
+    ) {
+        // Get master artifact.
+        FLYSArtifact master = getMasterArtifact(cc);
+        if (master != null) {
+            log.debug("Set master Artifact to uuid: " + master.identifier());
+            generator.setMasterArtifact(master);
+        }
+        else {
+            log.warn("Could not set master artifact.");
+        }
+    }
+
+
+    /**
+     * @return masterartifact or null if exception/not found.
+     */
+    protected FLYSArtifact getMasterArtifact(CallContext context)
+    {
+        try {
+            ArtifactDatabase db = context.getDatabase();
+            CallMeta callMeta   = context.getMeta();
+            Document document   = db.getCollectionsMasterArtifact(
+                identifier(), callMeta);
+
+            String masterUUID   = XMLUtils.xpathString(
+                document, XPATH_MASTER_UUID, ArtifactNamespaceContext.INSTANCE);
+            FLYSArtifact masterArtifact =
+                (FLYSArtifact) getArtifact(masterUUID, context);
+            return masterArtifact;
+        }
+        catch (ArtifactDatabaseException ade) {
+            log.error(ade, ade);
+        }
+        return null;
+    }
+
+
+    /**
+     * Return merged output document.
+     * @param uuids List of artifact uuids.
+     */
+    protected CollectionAttribute buildOutAttributes(
+        ArtifactDatabase db,
+        CallContext      context,
+        AttributeParser  aParser,
+        String[]         uuids)
+    {
+        FLYSContext flysContext = FLYSUtils.getFlysContext(context);
+        StateEngine engine = (StateEngine) flysContext.get(
+            FLYSContext.STATE_ENGINE_KEY);
+
+        if (engine == null) {
+            log.error("buildOutAttributes: engine == null");
+            return null;
+        }
+
+        FLYSArtifact masterArtifact = getMasterArtifact(context);
+
+        if (masterArtifact == null) {
+            log.debug("buildOutAttributes: masterArtifact == null");
+            return null;
+        }
+
+        OutputParser oParser = new OutputParser(db, context);
+
+        if (uuids != null) {
+            for (String uuid: uuids) {
+                try {
+                    oParser.parse(uuid);
+                }
+                catch (ArtifactDatabaseException ade) {
+                    log.warn(ade, ade);
+                }
+            }
+        }
+
+        aParser.parse();
+
+        AttributeWriter aWriter = new AttributeWriter(
+            db,
+            aParser.getCollectionAttribute(),
+            aParser.getOuts(),
+            aParser.getFacets(),
+            oParser.getOuts(),
+            oParser.getFacets(),
+            engine.getCompatibleFacets(masterArtifact.getStateHistoryIds())
+            );
+        return aWriter.write();
+    }
+
+
+    /**
+     * Returns the "attribute" (part of description document) for a specific
+     * output type.
+     *
+     * @param context The CallContext object.
+     * @param cAttr The CollectionAttribute.
+     * @param output The name of the desired output type.
+     *
+     * @return the attribute for the desired output type.
+     */
+    protected Document getAttribute(
+        CallContext         context,
+        CollectionAttribute cAttr,
+        String              output)
+    throws    ArtifactDatabaseException
+    {
+        Document attr = cAttr.toXML();
+
+        Map<String, String> vars = new HashMap<String, String>();
+        vars.put("output", output);
+
+        Node out = (Node) XMLUtils.xpath(
+            attr,
+            "art:attribute/art:outputs/art:output[@name=$output]",
+            XPathConstants.NODE,
+            ArtifactNamespaceContext.INSTANCE,
+            vars);
+
+
+        if (out != null) {
+            Document o = XMLUtils.newDocument();
+
+            o.appendChild(o.importNode(out, true));
+
+            return o;
+        }
+
+        return null;
+    }
+
+
+    /**
+     * This method returns the list of artifact UUIDs that this collections
+     * contains.
+     *
+     * @param context The CallContext that is necessary to get information about
+     * the ArtifactDatabase.
+     *
+     * @return a list of uuids.
+     */
+    protected String[] getArtifactUUIDs(CallContext context)
+    throws    ArtifactDatabaseException
+    {
+        log.debug("FLYSArtifactCollection.getArtifactUUIDs");
+
+        ArtifactDatabase db = context.getDatabase();
+        CallMeta meta       = context.getMeta();
+
+        Document itemList = db.listCollectionArtifacts(identifier(), meta);
+        NodeList items    = (NodeList) XMLUtils.xpath(
+            itemList,
+            XPATH_COLLECTION_ITEMS,
+            XPathConstants.NODESET,
+            ArtifactNamespaceContext.INSTANCE);
+
+        if (items == null || items.getLength() == 0) {
+            log.debug("No artifacts found in this collection.");
+            return null;
+        }
+
+        int num = items.getLength();
+
+        List<String> uuids = new ArrayList<String>(num);
+
+        for (int i = 0; i < num; i++) {
+            String uuid = XMLUtils.xpathString(
+                items.item(i),
+                "@art:uuid",
+                ArtifactNamespaceContext.INSTANCE);
+
+            if (uuid != null && uuid.trim().length() != 0) {
+                uuids.add(uuid);
+            }
+        }
+
+        return uuids.toArray(new String[uuids.size()]);
+    }
+
+
+    /**
+     * Returns a concrete Artifact of this collection specified by its uuid.
+     *
+     * @param uuid The Artifact's uuid.
+     * @param context The CallContext.
+     *
+     * @return an Artifact.
+     */
+    protected Artifact getArtifact(String uuid, CallContext context)
+    throws    ArtifactDatabaseException
+    {
+        log.debug("FLYSArtifactCollection.getArtifact");
+
+        Backend backend               = Backend.getInstance();
+        PersistentArtifact persistent = backend.getArtifact(uuid);
+
+        return persistent != null ? persistent.getArtifact() : 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/flys-artifacts/src/main/java/org/dive4elements/river/collections/OutputParser.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,139 @@
+package org.dive4elements.river.collections;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.ArtifactDatabase;
+import org.dive4elements.artifacts.ArtifactDatabaseException;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.artifactdatabase.state.DefaultOutput;
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifactdatabase.state.Output;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.model.ManagedFacetAdapter;
+
+
+/**
+ * The OutputParsers task is to pull Artifacts from database and put
+ * its outputs and facets into some structures.
+ */
+public class OutputParser {
+
+    /** Constant XPath that points to the outputmodes of an artifact. */
+    public static final String XPATH_ARTIFACT_OUTPUTMODES =
+        "/art:result/art:outputmodes/art:output";
+
+    private static Logger logger = Logger.getLogger(OutputParser.class);
+
+    protected ArtifactDatabase db;
+    protected CallMeta         meta;
+    protected CallContext      context;
+
+    /** Map outputs name to Output. */
+    protected Map<String, Output> outs;
+
+    /** Map facets name to list of Facets. */
+    protected List<Facet> facets;
+
+
+    /**
+     * @param db Database used to fetch artifacts, outputs and facets.
+     */
+    public OutputParser(ArtifactDatabase db, CallContext context) {
+        this.db      = db;
+        this.meta    = context.getMeta();
+        this.context = context;
+        this.outs    = new HashMap<String, Output>();
+        this.facets  = new ArrayList<Facet>();
+    }
+
+
+    /**
+     * Gets raw artifact with given id and sorts outputs in mapping.
+     * Converts Facets to ManagedFacets on the way.
+     * @param uuid uuid of artifact to load from database.
+     */
+    public void parse(String uuid)
+    throws ArtifactDatabaseException
+    {
+        logger.debug("OutputParser.parse: " + uuid);
+
+        FLYSArtifact flys = (FLYSArtifact) db.getRawArtifact(uuid);
+
+        List<Output> outList = flys.getOutputs(context);
+
+        logger.debug("   has " + outList.size() + " Outputs.");
+
+        for (Output out: outList) {
+            String name = out.getName();
+            logger.debug("Process Output '" + name + "'");
+
+            Output o = outs.get(name);
+            int  pos = 1;
+
+            if (o == null) {
+                o = new DefaultOutput(
+                    out.getName(),
+                    out.getDescription(),
+                    out.getMimeType(),
+                    new ArrayList<Facet>(),
+                    out.getType());
+                outs.put(name, o);
+            }
+            else {
+                logger.debug("OutputParser.parse: Use 'old' Output");
+                pos = o.getFacets().size() + 1;
+            }
+
+            List<Facet> mfacets = facet2ManagedFacet(uuid, out.getFacets(), pos);
+            o.addFacets(mfacets);
+            this.facets.addAll(mfacets);
+        }
+    }
+
+
+    /**
+     * Access mapping of Outputname to Output.
+     */
+    public Map<String, Output> getOuts() {
+        return outs;
+    }
+
+
+    /**
+     * Access all facets.
+     */
+    public List<Facet> getFacets() {
+        return this.facets;
+    }
+
+
+    /**
+     * Creates a list of ManagedFacets from list of Facets.
+     * @param pos Position of first facet (for each other the positions
+     *            will be increased).
+     */
+    protected List<Facet> facet2ManagedFacet(
+        String      uuid,
+        List<Facet> old,
+        int         pos)
+    {
+        List<Facet> newFacets = new ArrayList<Facet>(old.size());
+
+        logger.debug("There are " + old.size() + " Facets for this Output.");
+
+        for (Facet f: old) {
+            newFacets.add(new ManagedFacetAdapter(f, uuid, pos++, 1, 1));
+        }
+
+        return newFacets;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/ATExporter.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,145 @@
+package org.dive4elements.river.exports;
+
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.IOException;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
+import org.dive4elements.artifactdatabase.state.Settings;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+import org.dive4elements.river.artifacts.model.WQ;
+import org.dive4elements.river.collections.FLYSArtifactCollection;
+
+import org.dive4elements.river.model.Gauge;
+import org.dive4elements.river.model.River;
+import org.dive4elements.river.model.TimeInterval;
+import org.dive4elements.river.utils.FLYSUtils;
+import org.dive4elements.river.artifacts.access.RangeAccess;
+
+public class ATExporter
+implements   OutGenerator
+{
+    private static Logger logger = Logger.getLogger(ATExporter.class);
+
+    public static final String DEFAULT_ENCODING = "UTF-8";
+
+    protected WQ           data;
+    protected CallContext  context;
+    protected OutputStream out;
+    protected FLYSArtifact master;
+
+    protected FLYSArtifactCollection collection;
+
+
+    public ATExporter() {
+    }
+
+    @Override
+    public void init(Document request, OutputStream out, CallContext context) {
+        this.context = context;
+        this.out     = out;
+    }
+
+
+    @Override
+    public void setMasterArtifact(Artifact master) {
+        this.master = (FLYSArtifact) master;
+    }
+
+    @Override
+    public void setCollection(FLYSArtifactCollection collection) {
+        this.collection = collection;
+    }
+
+    @Override
+    public void doOut(
+        ArtifactAndFacet artifactf,
+        Document attr,
+        boolean  visible
+    ) {
+        data = (WQ)artifactf.getData(context);
+    }
+
+    @Override
+    public void generate() throws IOException {
+
+        if (data == null) {
+            logger.debug("no W/Q data");
+            return;
+        }
+
+        ATWriter at;
+        try {
+            at = new ATWriter(data);
+        }
+        catch (IllegalArgumentException iae) {
+            logger.error("creating ATWriter failed", iae);
+            throw new IOException(iae);
+        }
+
+        River river = FLYSUtils.getRiver(master);
+        RangeAccess rangeAccess = new RangeAccess(master, context);
+        double[] kms = rangeAccess.getLocations();
+
+        Gauge gauge = river.determineGaugeByPosition(kms[0]);
+        if (Math.abs(kms[0] - gauge.getStation().doubleValue()) < 1e-4) {
+            // at gauge.
+            TimeInterval interval =
+                gauge.fetchMasterDischargeTable().getTimeInterval();
+            at.write(
+                new OutputStreamWriter(out, DEFAULT_ENCODING),
+                context.getMeta(),
+                river.getName(),
+                kms[0],
+                gauge.getName(),
+                gauge.getDatum(),
+                interval.getStartTime(),
+                100d);
+        }
+        else {
+            // at km
+            at.write(
+                new OutputStreamWriter(out),
+                context.getMeta(),
+                river.getName(),
+                kms[0],
+                null,
+                null,
+                null,
+                0d);
+        }
+
+    }
+
+
+    /**
+     * Returns an instance of <i>EmptySettings</i> currently!
+     *
+     * @return an instance of <i>EmptySettings</i>.
+     */
+    @Override
+    public Settings getSettings() {
+        return new EmptySettings();
+    }
+
+
+    /**
+     * This method is not implemented!
+     *
+     * @param settings A settings object.
+     */
+    @Override
+    public void setSettings(Settings settings) {
+        // do nothing here
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/ATWriter.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,204 @@
+package org.dive4elements.river.exports;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.io.PrintWriter;
+import java.math.BigDecimal;
+
+import java.text.DateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.river.artifacts.model.WQ;
+import org.dive4elements.river.artifacts.resources.Resources;
+
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+
+import org.apache.commons.math.analysis.interpolation.SplineInterpolator;
+import org.apache.commons.math.analysis.interpolation.LinearInterpolator;
+
+import org.apache.commons.math.analysis.polynomials.PolynomialFunction;
+
+import org.apache.commons.math.FunctionEvaluationException;
+
+import org.apache.log4j.Logger;
+
+/** Write AT files. */
+public class ATWriter
+{
+    private static Logger logger = Logger.getLogger(ATWriter.class);
+
+    public static final int COLUMNS = 10;
+
+    public static final String I18N_AT_HEADER =
+        "export.discharge.curve.at.header";
+
+    public static final String I18N_AT_GAUGE_HEADER =
+        "export.discharge.curve.at.gauge.header";
+
+    public static final String EMPTY = "         ";
+
+    protected double minW;
+    protected double maxW;
+    protected double minQ;
+    protected double maxQ;
+
+    protected UnivariateRealFunction qFunc;
+
+    public ATWriter() {
+    }
+
+    public ATWriter(WQ wq) throws IllegalArgumentException {
+
+        int [] bounds = wq.longestIncreasingWRangeIndices();
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("exporting w between indices " +
+                bounds[0] + " and " + bounds[1] + " (" +
+                wq.getW(bounds[0]) + ", " + wq.getW(bounds[1]));
+        }
+
+        if (bounds[1]-bounds[0] < 1) { // Only first w can be written out.
+            minW = maxW = wq.getW(bounds[0]);
+            minQ = maxQ = wq.getQ(bounds[0]);
+            // constant function
+            qFunc = new PolynomialFunction(new double [] { minQ });
+            return;
+        }
+
+        double [] ws = new double[bounds[1]-bounds[0]];
+        double [] qs = new double[ws.length];
+
+        for (int i = 0; i < ws.length; ++i) {
+            int idx = bounds[0]+i;
+            ws[i] = wq.getW(idx);
+            qs[i] = wq.getQ(idx);
+        }
+
+        qFunc = ws.length < 3
+            ? new LinearInterpolator().interpolate(ws, qs)
+            : new SplineInterpolator().interpolate(ws, qs);
+
+        minW = wq.getW(bounds[0]);
+        maxW = wq.getW(bounds[1]);
+        minQ = wq.getQ(bounds[0]);
+        maxQ = wq.getQ(bounds[1]);
+    }
+
+    public double getQ(double w) {
+
+        try {
+            return qFunc.value(w);
+        }
+        catch (FunctionEvaluationException aode) {
+            // should not happen
+            logger.warn("spline interpolation failed", aode);
+            return w <= minW ? minQ : maxQ;
+        }
+    }
+
+    public static void printQ(PrintWriter out, double q) {
+        String format;
+             if (q <   1d) format = " % 8.3f";
+        else if (q <  10d) format = " % 8.2f";
+        else if (q < 100d) format = " % 8.1f";
+        else {
+            format = " % 8.0f";
+            if (q > 1000d) q = Math.rint(q/10d)*10d;
+        }
+        out.printf(Locale.US, format, q);
+    }
+
+
+    protected static void printGaugeHeader(
+        PrintWriter out,
+        CallMeta    callMeta,
+        String      river,
+        double      km,
+        String      gName,
+        BigDecimal  datum,
+        Date        date
+    ) {
+        DateFormat f = DateFormat.getDateInstance();
+        out.print(Resources.getMsg(
+            callMeta,
+            I18N_AT_GAUGE_HEADER,
+            I18N_AT_GAUGE_HEADER,
+            new Object[] { river, gName, f.format(date), datum } ));
+        out.print("\r\n");
+    }
+
+    protected static void printHeader(
+        PrintWriter out,
+        CallMeta    callMeta,
+        String      river,
+        double      km
+    ) {
+        out.print(Resources.getMsg(
+            callMeta,
+            I18N_AT_HEADER,
+            I18N_AT_HEADER,
+            new Object[] { river, km } ));
+        out.print("\r\n");
+    }
+
+    public void write(
+        Writer writer,
+        CallMeta meta,
+        String river,
+        double km,
+        String gName,
+        BigDecimal datum,
+        Date date,
+        double scale)
+    throws IOException
+    {
+        PrintWriter out = new PrintWriter(writer);
+
+        // A header is required, because the desktop version of FLYS will skip
+        // the first row.
+        if (gName != null) {
+            printGaugeHeader(out, meta, river, km, gName, datum, date);
+        }
+        else {
+            printHeader(out, meta, river, km);
+        }
+
+        double rest = (minW * 100.0) % 10.0;
+
+        double startW = Math.rint((minW - rest*0.01)*10.0)*0.1;
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("startW: " + startW);
+            logger.debug("rest: " + rest);
+        }
+
+        int col = 0;
+        for (double w = startW; w <= maxW; w += 0.01) {
+            if (col == 0) {
+                out.printf(Locale.US, "%8d", (int)Math.round(w * scale));
+            }
+
+            if (w < minW) {
+                out.print(EMPTY);
+            }
+            else {
+                printQ(out, getQ(w));
+            }
+
+            if (++col >= COLUMNS) {
+                out.print("\r\n");
+                col = 0;
+            }
+        }
+
+        if (col > 0) {
+            out.print("\r\n");
+        }
+
+        out.flush();
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/AbstractExporter.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,346 @@
+package org.dive4elements.river.exports;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+
+import java.text.NumberFormat;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+import au.com.bytecode.opencsv.CSVWriter;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
+import org.dive4elements.artifactdatabase.state.Settings;
+
+import org.dive4elements.artifacts.common.ArtifactNamespaceContext;
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+
+import org.dive4elements.river.artifacts.resources.Resources;
+import org.dive4elements.river.collections.FLYSArtifactCollection;
+
+import org.dive4elements.river.utils.Formatter;
+
+
+/**
+ * An abstract exporter that implements some basic methods for exporting data of
+ * artifacts.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public abstract class AbstractExporter implements OutGenerator {
+
+    /** The logger used in this exporter.*/
+    private static Logger logger = Logger.getLogger(AbstractExporter.class);
+
+    /* XXX: Why does AbstractExporter do not implement FacetTypes? */
+    public static String FIX_PARAMETERS = "fix_parameters";
+
+    /** The name of the CSV facet which triggers the CSV creation. */
+    public static final String FACET_CSV = "csv";
+
+    /** The name of the PDF facet which triggers the PDF creation. */
+    public static final String FACET_PDF = "pdf";
+
+    /** The default charset for the CSV export. */
+    public static final String DEFAULT_CSV_CHARSET = "UTF-8";
+
+    /** The default separator for the CSV export. */
+    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 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;
+
+    /** The selected facet. */
+    protected String facet;
+
+    /** The collection.*/
+    protected FLYSArtifactCollection collection;
+
+    /** The master artifact. */
+    protected Artifact master;
+
+    private NumberFormat kmFormat;
+
+    private NumberFormat wFormat;
+
+    private NumberFormat qFormat;
+
+
+    /**
+     * Concrete subclasses need to use this method to write their special data
+     * objects into the CSV document.
+     *
+     * @param writer The CSVWriter.
+     */
+    protected abstract void writeCSVData(CSVWriter writer) throws IOException;
+
+
+    /**
+     * Concrete subclasses need to use this method to write their special data
+     * objects into the PDF document.
+     */
+    protected abstract void writePDF(OutputStream out);
+
+
+    /**
+     * This method enables concrete subclasses to collected its own special
+     * data.
+     *
+     * @param data The artifact that stores the data that has to be
+     * exported.
+     */
+    protected abstract void addData(Object data);
+
+
+    @Override
+    public void init(Document request, OutputStream out, CallContext context) {
+        logger.debug("AbstractExporter.init");
+
+        this.request = request;
+        this.out     = out;
+        this.context = context;
+    }
+
+
+    @Override
+    public void setMasterArtifact(Artifact master) {
+        this.master = master;
+    }
+
+    /** Get the callcontext that this exporter has been initialized
+     * with. */
+    public CallContext getCallContext() {
+        return this.context;
+    }
+
+
+    @Override
+    public void setCollection(FLYSArtifactCollection collection) {
+        this.collection = collection;
+    }
+
+
+    /**
+     * This doOut() just collects the data of multiple artifacts. Therefore, it
+     * makes use of the addData() method which enables concrete subclasses to
+     * store its data on its own. The real output creation takes place in the
+     * concrete generate() methods.
+     *
+     * @param artifactFacet The artifact and facet.
+     * The facet to add - NOTE: the facet needs to fit to the first
+     * facet inserted into this exporter. Otherwise this artifact/facet is
+     * skipped.
+     * @param attr The attr document.
+     */
+    @Override
+    public void doOut(
+        ArtifactAndFacet artifactFacet,
+        Document         attr,
+        boolean          visible
+    ) {
+        String name = artifactFacet.getFacetName();
+
+        logger.debug("AbstractExporter.doOut: " + name);
+
+        if (!isFacetValid(name)) {
+            logger.warn("Facet '" + name + "' not valid. No output created!");
+            return;
+        }
+
+        addData(artifactFacet.getData(context));
+    }
+
+
+    /**
+     * Generates an export based on a specified facet.
+     */
+    @Override
+    public void generate()
+    throws IOException
+    {
+        logger.debug("AbstractExporter.generate");
+
+        if (facet == null) {
+            throw new IOException("invalid (null) facet for exporter");
+        }
+
+        if (facet.equals(FACET_CSV)) {
+            generateCSV();
+        }
+        else if (facet.equals(FACET_PDF)) {
+            generatePDF();
+        }
+        else {
+            throw new IOException(
+                "invalid facet for exporter: '" + facet + "'");
+        }
+    }
+
+
+    /**
+     * Determines if the desired facet is valid for this exporter. If no facet
+     * is currently set, <i>facet</i> is set.
+     *
+     * @param facet The desired facet.
+     *
+     * @return true, if <i>facet</i> is valid, otherwise false.
+     */
+    protected boolean isFacetValid(String facet) {
+        logger.debug("AbstractExporter.isFacetValid : " + facet + " (" + getFacet() + ")" );
+
+        String thisFacet = getFacet();
+
+        if (thisFacet == null || thisFacet.length() == 0) {
+            return false;
+        }
+        else if (facet == null || facet.length() == 0) {
+            return false;
+        }
+        else {
+            return thisFacet.equals(facet);
+        }
+    }
+
+
+    /**
+     * Returns the name of the desired facet.
+     *
+     * @return the name of the desired facet.
+     */
+    protected String getFacet() {
+        if (facet == null) {
+            facet = getFacetFromRequest();
+        }
+
+        return facet;
+    }
+
+
+    /**
+     * Extracts the name of the requested facet from request document.
+     *
+     * @return the name of the requested facet.
+     */
+    protected String getFacetFromRequest() {
+        return XMLUtils.xpathString(
+            request, XPATH_FACET, ArtifactNamespaceContext.INSTANCE);
+    }
+
+
+    protected String msg(String key, String def) {
+        return Resources.getMsg(context.getMeta(), key, def);
+    }
+
+    protected String msg(String key, String def, Object[] args) {
+        return Resources.getMsg(context.getMeta(), key, def, args);
+    }
+
+
+    /**
+     * This method starts CSV creation. It makes use of writeCSVData() which has
+     * to be implemented by concrete subclasses.
+     */
+    protected void generateCSV()
+    throws    IOException
+    {
+        logger.info("AbstractExporter.generateCSV");
+
+        char quote = '"';
+        char escape = '\\';
+
+        CSVWriter writer = new CSVWriter(
+            new OutputStreamWriter(
+                out,
+                DEFAULT_CSV_CHARSET),
+            DEFAULT_CSV_SEPARATOR, quote, escape, "\r\n");
+
+        writeCSVData(writer);
+
+        writer.close();
+    }
+
+
+    /**
+     * This method starts PDF creation.
+     */
+    protected void generatePDF()
+    throws    IOException
+    {
+        logger.info("AbstractExporter.generatePDF");
+        writePDF(this.out);
+    }
+
+
+    /**
+     * Returns an instance of <i>EmptySettings</i> currently!
+     *
+     * @return an instance of <i>EmptySettings</i>.
+     */
+    public Settings getSettings() {
+        return new EmptySettings();
+    }
+
+
+    /**
+     * This method is not implemented. Override it in subclasses if those need a
+     * <i>Settings</i> object.
+     */
+    public void setSettings(Settings settings) {
+        // do nothing
+    }
+
+
+    /**
+     * Returns the number formatter for kilometer values.
+     *
+     * @return the number formatter for kilometer values.
+     */
+    protected NumberFormat getKmFormatter() {
+        if (kmFormat == null) {
+            kmFormat = Formatter.getWaterlevelKM(context);
+        }
+        return kmFormat;
+    }
+
+
+    /**
+     * Returns the number formatter for W values.
+     *
+     * @return the number formatter for W values.
+     */
+    protected NumberFormat getWFormatter() {
+        if (wFormat == null) {
+            wFormat = Formatter.getWaterlevelW(context);
+        }
+        return wFormat;
+    }
+
+
+    /**
+     * Returns the number formatter for Q values.
+     *
+     * @return the number formatter for Q values.
+     */
+    protected NumberFormat getQFormatter() {
+        if (qFormat == null) {
+            qFormat = Formatter.getWaterlevelQ(context);
+        }
+        return qFormat;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/AxisSection.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,106 @@
+package org.dive4elements.river.exports;
+
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import org.dive4elements.artifactdatabase.state.Attribute;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class AxisSection extends TypeSection {
+
+    public static final String IDENTIFIER_ATTR = "id";
+    public static final String LABEL_ATTR      = "label";
+    public static final String FONTSIZE_ATTR   = "font-size";
+    public static final String FIXATION_ATTR   = "fixation";
+    public static final String UPPERRANGE_ATTR = "upper";
+    public static final String LOWERRANGE_ATTR = "lower";
+
+
+    public AxisSection() {
+        super("axis");
+    }
+
+
+    public void setIdentifier(String identifier) {
+        setStringValue(IDENTIFIER_ATTR, identifier);
+    }
+
+
+    public String getIdentifier() {
+        return getStringValue(IDENTIFIER_ATTR);
+    }
+
+
+    public void setLabel(String label) {
+        setStringValue(LABEL_ATTR, label);
+    }
+
+
+    public String getLabel() {
+        return getStringValue(LABEL_ATTR);
+    }
+
+
+    public void setFontSize(int fontSize) {
+        if (fontSize <= 0) {
+            return;
+        }
+
+        setIntegerValue(FONTSIZE_ATTR, fontSize);
+    }
+
+
+    public Integer getFontSize() {
+        return getIntegerValue(FONTSIZE_ATTR);
+    }
+
+
+    public void setFixed(boolean fixed) {
+        setBooleanValue(FIXATION_ATTR, fixed);
+    }
+
+
+    public Boolean isFixed() {
+        return getBooleanValue(FIXATION_ATTR);
+    }
+
+
+    public void setUpperRange(double upperRange) {
+        setDoubleValue(UPPERRANGE_ATTR, upperRange);
+    }
+
+
+    public Double getUpperRange() {
+        return getDoubleValue(UPPERRANGE_ATTR);
+    }
+
+
+    public void setLowerRange(double lowerRange) {
+        setDoubleValue(LOWERRANGE_ATTR, lowerRange);
+    }
+
+
+    public Double getLowerRange() {
+        return getDoubleValue(LOWERRANGE_ATTR);
+    }
+
+
+    @Override
+    public void toXML(Node parent) {
+        Document owner = parent.getOwnerDocument();
+        Element   axis = owner.createElement("axis");
+
+        parent.appendChild(axis);
+
+        for (String key: getKeys()) {
+            Attribute attr = getAttribute(key);
+            attr.toXML(axis);
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/BooleanAttribute.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,35 @@
+package org.dive4elements.river.exports;
+
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class BooleanAttribute extends VisibleAttribute {
+
+
+    public BooleanAttribute(String name, boolean value, boolean visible) {
+        super(name, value, visible);
+    }
+
+
+    /**
+     * Calls VisibleAttribute.toXML() and appends afterwards an attribute
+     * <i>type</i> with value <i>boolean</i>.
+     *
+     * @param parent The parent Node.
+     *
+     * @return the new Node that represents this Attribute.
+     */
+    @Override
+    public Node toXML(Node parent) {
+
+        Element ele = (Element) super.toXML(parent);
+        ele.setAttribute("type", "boolean");
+
+        return ele;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/ChartArea.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,57 @@
+package org.dive4elements.river.exports;
+
+import org.jfree.chart.axis.ValueAxis;
+
+import org.jfree.data.Range;
+
+/** Two Ranges that span a rectangular area. */
+public class ChartArea {
+    protected Range xRange;
+    protected Range yRange;
+
+    public ChartArea(Range rangeX, Range rangeY) {
+        this.xRange = rangeX;
+        this.yRange = rangeY;
+    }
+
+    public ChartArea(ValueAxis axisX, ValueAxis axisY) {
+        this.xRange = axisX.getRange();
+        this.yRange = axisY.getRange();
+    }
+
+    public double ofLeft(double percent) {
+        return xRange.getLowerBound()
+            + xRange.getLength() * percent;
+    }
+
+    public double ofRight(double percent) {
+        return xRange.getUpperBound()
+            - xRange.getLength() * percent;
+    }
+
+    public double ofGround(double percent) {
+        return yRange.getLowerBound()
+            + yRange.getLength() * percent;
+    }
+
+    public double atTop() {
+        return yRange.getUpperBound();
+    }
+
+    public double atGround() {
+        return yRange.getLowerBound();
+    }
+
+    public double atRight() {
+        return xRange.getUpperBound();
+    }
+
+    public double atLeft() {
+        return xRange.getLowerBound();
+    }
+
+    public double above(double percent, double base) {
+        return base + yRange.getLength() * percent;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/ChartExportHelper.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,408 @@
+/*
+ * Copyright (c) 2011 by Intevation GmbH
+ *
+ * This program is free software under the LGPL (>=v2.1)
+ * Read the file LGPL.txt coming with the software for details
+ * or visit http://www.gnu.org/licenses/ if it does not exist.
+ */
+package org.dive4elements.river.exports;
+
+import com.lowagie.text.Document;
+import com.lowagie.text.DocumentException;
+import com.lowagie.text.PageSize;
+import com.lowagie.text.Rectangle;
+
+import com.lowagie.text.pdf.PdfContentByte;
+import com.lowagie.text.pdf.PdfTemplate;
+import com.lowagie.text.pdf.PdfWriter;
+
+import java.awt.Graphics2D;
+import java.awt.Transparency;
+
+import java.awt.geom.Rectangle2D.Double;
+
+import java.awt.geom.Rectangle2D;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import org.jfree.chart.ChartRenderingInfo;
+
+import javax.imageio.ImageIO;
+
+import au.com.bytecode.opencsv.CSVWriter;
+
+import org.apache.batik.svggen.SVGGraphics2D;
+import org.apache.batik.svggen.SVGGraphics2DIOException;
+
+import org.apache.log4j.Logger;
+
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.data.xy.XYDataset;
+
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+
+
+/**
+ * This class is a helper class which supports some methods to export charts
+ * into specific formats.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ChartExportHelper {
+
+    public static final String FORMAT_PNG = "png";
+
+    public static final String FORMAT_PDF = "pdf";
+
+    public static final String FORMAT_SVG = "svg";
+
+    public static final String FORMAT_CSV = "csv";
+
+    /**
+     * Constant field to define A4 as default page size.
+     */
+    public static final String  DEFAULT_PAGE_SIZE = "A4";
+
+    /**
+     * Constant field to define UTF-8 as default encoding.
+     */
+    public static final String  DEFAULT_ENCODING  = "UTF-8";
+
+    /** The default separator for the CSV export. */
+    public static final char DEFAULT_CSV_SEPARATOR = ',';
+
+
+    /**
+     * Logger used for logging with log4j.
+     */
+    private static Logger log = Logger.getLogger(ChartExportHelper.class);
+
+
+    /**
+     * A method to export a <code>JFreeChart</code> as image to an
+     * <code>OutputStream</code> with a given format, width and height.
+     *
+     * @param out OutputStream
+     * @param chart JFreeChart object to be exported.
+     * @param cc context, in which e.g. size is stored.
+     *
+     * @throws IOException if writing image to OutputStream failed.
+     */
+    public static void exportImage(
+        OutputStream out,
+        JFreeChart   chart,
+        CallContext  cc
+    )
+    throws IOException
+    {
+        log.info("export chart as png");
+
+        ChartRenderingInfo info = new ChartRenderingInfo();
+
+        String format = (String) cc.getContextValue("chart.image.format");
+
+        int[] size = getSize(cc);
+
+        ImageIO.write(
+            chart.createBufferedImage(
+                size[0], size[1], Transparency.BITMASK, info
+            ),
+            format,
+            out
+        );
+    }
+
+
+    /**
+     * A method to export a <code>JFreeChart</code> as SVG to an
+     * <code>OutputStream</code>.
+     *
+     * @param out OutputStream
+     * @param chart JFreeChart to be exported
+     * @param context The CallContext object that contains extra chart
+     * parameters.
+     */
+    public static void exportSVG(
+        OutputStream out,
+        JFreeChart   chart,
+        CallContext  context
+    ) {
+        String encoding = (String) context.getContextValue("chart.encoding");
+
+        log.info("export chart as svg");
+
+        if (encoding == null)
+            encoding = DEFAULT_ENCODING;
+
+        org.w3c.dom.Document document = XMLUtils.newDocument();
+        SVGGraphics2D        graphics = new SVGGraphics2D(document);
+
+        int[] size = getSize(context);
+
+        ChartRenderingInfo info = new ChartRenderingInfo();
+
+        chart.draw(graphics, new Rectangle2D.Double(0.0D, 0.0D,size[0],size[1]), info);
+
+        try {
+            graphics.stream(new OutputStreamWriter(out, encoding));
+        }
+        catch (SVGGraphics2DIOException svge) {
+            log.error("Error while writing svg export to output stream.", svge);
+        }
+        catch (UnsupportedEncodingException uee) {
+            log.error("Unsupported encoding: " + encoding, uee);
+        }
+    }
+
+
+    /**
+     * A method to export a <code>JFreeChart</code> as PDF to an
+     * <code>OutputStream</code>.
+     *
+     * @param out OutputStream
+     * @param chart JFreeChart
+     */
+    public static void exportPDF(
+        OutputStream out,
+        JFreeChart   chart,
+        CallContext  cc
+    ) {
+        log.info("export chart as pdf.");
+
+        String pageFormat = (String) cc.getContextValue("chart.page.format");
+
+        if (pageFormat == null)
+            pageFormat = DEFAULT_PAGE_SIZE;
+
+        // Max size of the chart.
+        Rectangle page = PageSize.getRectangle(pageFormat);
+        float pageWidth  = page.getWidth();
+        float pageHeight = page.getHeight();
+
+        // The chart width.
+        int[] size = getSize(cc);
+
+        boolean landscape = size[0] > size[1];
+
+        float width  = 0;
+        float height = 0;
+        if (landscape) {
+            width  = pageHeight;
+            height = pageWidth;
+        }
+        else {
+            width  = pageWidth;
+            height = pageHeight;
+        }
+
+        float marginLeft = (Float) cc.getContextValue(
+            "chart.marginLeft");
+
+        float marginRight = (Float) cc.getContextValue(
+            "chart.marginRight");
+
+        float marginTop = (Float) cc.getContextValue(
+            "chart.marginTop");
+
+        float marginBottom = (Float) cc.getContextValue(
+            "chart.marginBottom");
+
+        float spaceX = width  - marginLeft - marginRight;
+        if (size[0] > spaceX) {
+            log.warn("Width of the chart is too big for pdf -> resize it now.");
+            double ratio = ((double)spaceX) / size[0];
+            size[0]  *= ratio;
+            size[1] *= ratio;
+            log.debug("Resized chart to " + size[0] + "x" + size[1]);
+        }
+
+        float spaceY = height - marginTop  - marginBottom;
+        if (size[1] > spaceY) {
+            log.warn("Height of the chart is too big for pdf -> resize it now.");
+            double ratio = ((double)spaceY) / size[1];
+            size[0]  *= ratio;
+            size[1] *= ratio;
+            log.debug("Resized chart to " + size[0] + "x" + size[1]);
+        }
+
+        Document document = null;
+        if (landscape) {
+            document = new Document(page.rotate());
+            log.debug("Create landscape pdf.");
+        }
+        else
+            document = new Document(page);
+
+        try {
+            PdfWriter writer = PdfWriter.getInstance(document, out);
+
+            document.addSubject(chart.getTitle().getText());
+            document.addCreationDate();
+            document.open();
+
+            PdfContentByte content  = writer.getDirectContent();
+
+            PdfTemplate template = content.createTemplate(width, height);
+            Graphics2D  graphics = template.createGraphics(width, height);
+
+            double[] origin = getCenteredAnchor(
+                marginLeft, marginRight, marginBottom, marginTop,
+                width, height,
+                size[0], size[1]);
+
+            Rectangle2D area = new Rectangle2D.Double(
+                origin[0], origin[1], size[0], size[1]);
+
+            ChartRenderingInfo info = new ChartRenderingInfo();
+
+            chart.draw(graphics, area, info);
+            graphics.dispose();
+            content.addTemplate(template, 0f, 0f);
+        }
+        catch (DocumentException de) {
+            log.error("Error while exporting chart to pdf.", de);
+        }
+        finally {
+            document.close();
+        }
+    }
+
+
+    /**
+     * A method to export a CSV file to an
+     * <code>OutputStream</code>.
+     *
+     * @param out OutputStream
+     * @param chart JFreeChart containing the data.
+     * @param context The CallContext object that contains extra parameters.
+     */
+    public static void exportCSV(
+        OutputStream out,
+        JFreeChart chart,
+        CallContext context)
+    {
+        log.debug("export chart as CSV");
+        CSVWriter writer = null;
+        try {
+            writer = new CSVWriter(
+                new OutputStreamWriter(
+                    out,
+                    DEFAULT_ENCODING),
+                DEFAULT_CSV_SEPARATOR);
+        }
+        catch(UnsupportedEncodingException uee) {
+            log.warn("Wrong encoding for CSV export.");
+            return;
+        }
+        XYPlot plot = chart.getXYPlot();
+        int count = plot.getDatasetCount();
+        for (int i = 0; i < count; i++) {
+            XYDataset data = plot.getDataset(i);
+            int scount = data.getSeriesCount();
+            for (int j = 0; j < scount; j++) {
+                Comparable seriesKey = data.getSeriesKey(j);
+                log.debug("series key: " + seriesKey.toString());
+                writeCSVHeader(writer, seriesKey.toString());
+                writeCSVData(writer, data);
+            }
+        }
+        try {
+            writer.close();
+        }
+        catch(IOException ioe) {
+            log.error("Writing CSV export failed!");
+        }
+    }
+
+
+    protected static void writeCSVHeader(CSVWriter writer, String key) {
+        writer.writeNext(new String[] {"#"});
+        writer.writeNext(new String[] {"# " + key});
+        writer.writeNext(new String[] {"#"});
+        writer.writeNext(new String[] {"X", "Y"});
+    }
+
+
+    protected static void writeCSVData(CSVWriter writer, XYDataset data) {
+        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));
+                writer.writeNext(new String[] {
+                    data.getX(i, j).toString(),
+                    data.getY(i, j).toString()});
+            }
+        }
+    }
+
+
+    public static int[] getSize(CallContext cc) {
+        int[] size = new int[2];
+
+        size[0] = (Integer) cc.getContextValue("chart.width");
+        size[1] = (Integer) cc.getContextValue("chart.height");
+
+        return size;
+    }
+
+
+    /**
+     * This method returns the anchor of the chart so that the chart is centered
+     * according to the given parameters.
+     *
+     * @param mLeft Left margin
+     * @param mRight Right margin
+     * @param mBottom Bottom margin
+     * @param mTop Top margin
+     * @param width The complete width of the drawing area.
+     * @param height The complete height of the drawing area.
+     * @param chartWidth The width of the chart.
+     * @param chartHeight The height of the chart.
+     *
+     * @return an array that contains the anchor for a chart with the given
+     * parameters. The first value is the x point, the second value is the y
+     * point.
+     */
+    public static double[] getCenteredAnchor(
+        double mLeft,      double mRight,      double mBottom, double mTop,
+        double width,      double height,
+        double chartWidth, double chartHeight
+    ) {
+        if (log.isDebugEnabled()) {
+            log.debug("Calculate centered origin...");
+            log.debug("-> PDF width    : " + width);
+            log.debug("-> PDF height   : " + height);
+            log.debug("-> Chart width  : " + chartWidth);
+            log.debug("-> Chart height : " + chartHeight);
+            log.debug("-> margin left  : " + mLeft);
+            log.debug("-> margin right : " + mRight);
+            log.debug("-> margin bottom: " + mBottom);
+            log.debug("-> margin top   : " + mTop);
+        }
+
+        double[] origin = new double[2];
+
+        double centerX = width  / 2;
+        double centerY = height / 2;
+
+        origin[0] = centerX - chartWidth / 2;
+        origin[1] = centerY - chartHeight / 2;
+
+        origin[0] = origin[0] >= mLeft ? origin[0] : mLeft;
+        origin[1] = origin[1] >= mTop ? origin[1] : mTop;
+
+        if (log.isDebugEnabled()) {
+            log.debug("==> centered left origin: " + origin[0]);
+            log.debug("==> centered top  origin: " + origin[1]);
+        }
+
+        return origin;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/ChartGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,1915 @@
+package org.dive4elements.river.exports;
+
+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.access.RangeAccess;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.resources.Resources;
+import org.dive4elements.river.collections.FLYSArtifactCollection;
+import org.dive4elements.river.jfree.Bounds;
+import org.dive4elements.river.jfree.CollisionFreeXYTextAnnotation;
+import org.dive4elements.river.jfree.DoubleBounds;
+import org.dive4elements.river.jfree.EnhancedLineAndShapeRenderer;
+import org.dive4elements.river.jfree.FLYSAnnotation;
+import org.dive4elements.river.jfree.StableXYDifferenceRenderer;
+import org.dive4elements.river.jfree.StickyAxisAnnotation;
+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.LineStyle;
+import org.dive4elements.river.themes.TextStyle;
+import org.dive4elements.river.themes.ThemeAccess;
+import org.dive4elements.river.utils.FLYSUtils;
+import org.dive4elements.river.utils.ThemeUtil;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Paint;
+import java.awt.Stroke;
+import java.awt.TexturePaint;
+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.jfree.chart.JFreeChart;
+import org.jfree.chart.LegendItem;
+import org.jfree.chart.LegendItemCollection;
+import org.jfree.chart.annotations.XYLineAnnotation;
+import org.jfree.chart.annotations.XYTextAnnotation;
+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.jfree.ui.TextAnchor;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import org.dive4elements.river.utils.Formatter;
+
+/**
+ * The base class for chart creation. It should provide some basic things that
+ * equal in all chart types.
+ *
+ * Annotations are added as FLYSAnnotations and come in mutliple basic forms:
+ * TextAnnotations are labels somewhere in data space, StickyAnnotations are
+ * labels of a slice or line in one data dimension (i.e. visualized as label
+ * on a single axis).
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public abstract class ChartGenerator implements OutGenerator {
+
+    private static Logger logger = Logger.getLogger(ChartGenerator.class);
+
+    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";
+
+    protected static float ANNOTATIONS_AXIS_OFFSET = 0.02f;
+
+    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 FLYSArtifactCollection 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<FLYSAnnotation> annotations = new ArrayList<FLYSAnnotation>();
+
+    /**
+     * A mini interface that allows to walk over the YAXIS enums defined in
+     * subclasses.
+     */
+    public interface YAxisWalker {
+
+        int length();
+
+        String getId(int idx);
+    } // end of YAxisWalker interface
+
+
+
+    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
+
+
+
+    /**
+     * Default constructor that initializes internal data structures.
+     */
+    public ChartGenerator() {
+        datasets = new TreeMap<Integer, AxisDataset>();
+    }
+
+
+    /**
+     * Adds annotations to list. The given annotation will be visible.
+     */
+    public void addAnnotations(FLYSAnnotation annotation) {
+        annotations.add(annotation);
+    }
+
+    /**
+     * Add a text and a line annotation.
+     * @param area convenience to determine positions in plot.
+     * @param theme (optional) theme document
+     */
+    protected void addStickyAnnotation(
+        StickyAxisAnnotation annotation,
+        XYPlot plot,
+        ChartArea area,
+        LineStyle lineStyle,
+        TextStyle textStyle,
+        Document theme
+    ) {
+        // 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).
+            // FIXME: Remove dependency to XYChartGenerator here
+            AxisDataset dataset = getAxisDataset(
+                new Integer(annotation.getAxisSymbol()));
+            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 (ThemeUtil.parseShowVerticalLine(theme)) {
+                        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 (ThemeUtil.parseShowHorizontalLine(theme)) {
+                        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 (ThemeUtil.parseShowHorizontalLine(theme)) {
+                        XYLineAnnotation hitLineAnnotation =
+                            createStickyLineAnnotation(
+                                StickyAxisAnnotation.SimpleAxis.Y_AXIS,
+                                annotation.getPos(), annotation.getHitPoint(),
+                                area, lineStyle);
+                        plot.getRenderer(rendererIndex).addAnnotation(hitLineAnnotation,
+                            org.jfree.ui.Layer.BACKGROUND);
+                    }
+                    if (ThemeUtil.parseShowVerticalLine(theme)) {
+                        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.
+     */
+    protected 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.
+     */
+    protected 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.
+     */
+    protected 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.
+     */
+    protected 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);
+        }
+    }
+
+    /**
+     * Add the annotations (Sticky, Text and hyk zones) stored
+     * in the annotations field.
+     * @param plot Plot to add annotations to.
+     */
+    protected void addAnnotationsToRenderer(XYPlot plot) {
+        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 (FLYSAnnotation fa: annotations) {
+
+            // Access text styling, if any.
+            Document 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();
+                if (fa.getLabel() != null) {
+                    LegendItemCollection lic = new LegendItemCollection();
+                    LegendItemCollection old = plot.getFixedLegendItems();
+                    lic.add(createLegendItem(theme, fa.getLabel()));
+                    // (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);
+            }
+
+            // 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);
+            }
+        }
+    }
+
+
+    /**
+     * 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,
+        Document         attr,
+        boolean          visible);
+
+
+    protected abstract YAxisWalker getYAxisWalker();
+
+
+    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();
+
+
+    /**
+     * 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 called to retrieve the default label for an Y axis at
+     * position <i>pos</i>.
+     *
+     * @param pos The position of an Y axis.
+     *
+     * @return the default Y axis label at position <i>pos</i>.
+     */
+    protected abstract String getDefaultYAxisLabel(int pos);
+
+
+    /**
+     * 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));
+        }
+    }
+
+
+    /**
+     * Register annotations like MainValues for later plotting
+     *
+     * @param annotations list of annotations (data of facet).
+     * @param aandf   Artifact and the facet.
+     * @param theme   Theme document for given annotations.
+     * @param visible The visibility of the annotations.
+     */
+    protected void doAnnotations(
+        FLYSAnnotation annotations,
+        ArtifactAndFacet aandf,
+        Document theme,
+        boolean visible
+    ){
+        logger.debug("doAnnotations");
+
+        // Add all annotations to our annotation pool.
+        annotations.setTheme(theme);
+        if (aandf != null) {
+            annotations.setLabel(aandf.getFacetDescription());
+        }
+        else {
+            logger.error(
+                "Art/Facet for Annotations is null. " +
+                "This should never happen!");
+        }
+
+        if (visible) {
+            addAnnotations(annotations);
+        }
+    }
+
+
+    /**
+     * Generate chart.
+     */
+    @Override
+    public void generate()
+    throws IOException
+    {
+        logger.debug("ChartGenerator.generate");
+
+        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(Document request, OutputStream out, CallContext context) {
+        logger.debug("ChartGenerator.init");
+
+        this.request = request;
+        this.out     = out;
+        this.context = context;
+    }
+
+
+    /** Sets the master artifact. */
+    @Override
+    public void setMasterArtifact(Artifact master) {
+        this.master = master;
+    }
+
+
+    /** Sets the collection. */
+    @Override
+    public void setCollection(FLYSArtifactCollection 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;
+    }
+
+
+    /**
+     * Creates a list of Section for the chart's Y axes. This method makes use
+     * of <i>getYAxisWalker</i> to be able to access all Y axes defined in
+     * subclasses.
+     *
+     * @return a list of Y axis sections.
+     */
+    protected List<AxisSection> buildYAxisSections() {
+        List<AxisSection> axisSections = new ArrayList<AxisSection>();
+
+        YAxisWalker walker = getYAxisWalker();
+        for (int i = 0, n = walker.length(); i < n; i++) {
+            AxisSection ySection = new AxisSection();
+            ySection.setIdentifier(walker.getId(i));
+            ySection.setLabel(getYAxisLabel(i));
+            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 <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;
+    }
+
+
+    /**
+     * 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 pos) {
+        ChartSettings chartSettings = getChartSettings();
+        if (chartSettings == null) {
+            return DEFAULT_FONT_SIZE;
+        }
+
+        YAxisWalker walker = getYAxisWalker();
+
+        AxisSection   as = chartSettings.getAxisSection(walker.getId(pos));
+        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 };
+    }
+
+
+    /**
+     * 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(int pos) {
+        ChartSettings chartSettings = getChartSettings();
+        if (chartSettings == null) {
+            return getDefaultYAxisLabel(pos);
+        }
+
+        YAxisWalker walker = getYAxisWalker();
+        AxisSection     as = chartSettings.getAxisSection(walker.getId(pos));
+        if (as != null) {
+            String label = as.getLabel();
+
+            if (label != null) {
+                return label;
+            }
+        }
+
+        return getDefaultYAxisLabel(pos);
+    }
+
+
+    /**
+     * 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.
+     */
+    protected 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.
+     */
+    protected String msg(String key) {
+        return Resources.getMsg(context.getMeta(), key, key);
+    }
+
+    protected String msg(String key, String def, Object[] args) {
+        return Resources.getMsg(context.getMeta(), key, def, args);
+    }
+
+
+    protected String getRiverName() {
+        FLYSArtifact flys = (FLYSArtifact) master;
+
+        River river = FLYSUtils.getRiver(flys);
+        return (river != null) ? river.getName() : "";
+    }
+
+
+    protected double[] getRange() {
+        FLYSArtifact flys = (FLYSArtifact) master;
+
+        RangeAccess rangeAccess = new RangeAccess(flys, null);
+        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) {
+        YAxisWalker walker = getYAxisWalker();
+
+        return new IdentifiableNumberAxis(walker.getId(idx), label);
+    }
+
+
+    /**
+     * Create Y (range) axis for given index.
+     * Shall be overriden by subclasses.
+     */
+    protected NumberAxis createYAxis(int index) {
+        YAxisWalker walker = getYAxisWalker();
+
+        Font labelFont = new Font(
+            DEFAULT_FONT_NAME,
+            Font.BOLD,
+            getYAxisFontSize(index));
+
+        IdentifiableNumberAxis axis = new IdentifiableNumberAxis(
+            walker.getId(index),
+            getYAxisLabel(index));
+
+        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(Document theme, String name) {
+        // OPTIMIZE Pass font, parsed Theme items.
+        ThemeAccess themeAccess = new ThemeAccess(theme);
+
+        Color      color       = themeAccess.parseLineColorField();
+        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;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/ChartHelper.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,200 @@
+package org.dive4elements.river.exports;
+
+import org.jfree.data.Range;
+import org.jfree.data.xy.XYDataset;
+import org.jfree.data.xy.XYSeriesCollection;
+import org.jfree.data.time.RegularTimePeriod;
+import org.jfree.data.time.TimeSeriesCollection;
+import org.jfree.data.time.TimeSeries;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.river.jfree.Bounds;
+import org.dive4elements.river.jfree.DoubleBounds;
+import org.dive4elements.river.jfree.TimeBounds;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ChartHelper {
+
+    private static final Logger logger = Logger.getLogger(ChartHelper.class);
+
+
+    /**
+     * This method returns the ranges of the XYDataset <i>dataset</i> as array
+     * with [xrange, yrange].
+     *
+     * @param dataset The dataset which should be evaluated.
+     *
+     * @return an array with x and y ranges.
+     */
+    public static Bounds[] getBounds(XYSeriesCollection dataset) {
+        int seriesCount = dataset != null ? dataset.getSeriesCount() : 0;
+
+        if (seriesCount == 0) {
+            logger.warn("Dataset is empty or has no Series set.");
+            return null;
+        }
+
+        boolean foundValue = false;
+
+        double minX = Double.MAX_VALUE;
+        double maxX = -Double.MAX_VALUE;
+        double minY = Double.MAX_VALUE;
+        double maxY = -Double.MAX_VALUE;
+
+        for (int i = 0, m = seriesCount; i < m; i++) {
+            for (int j = 0, n = dataset.getItemCount(i); j < n; j++) {
+                double x = dataset.getXValue(i, j);
+                double y = dataset.getYValue(i, j);
+
+                if (Double.isNaN(x) || Double.isNaN(y)) {
+                    logger.warn("Item " + j + " in Series " + i + " is broken");
+                    continue;
+                }
+
+                foundValue = true;
+
+                if (x < minX) {
+                    minX = x;
+                }
+
+                if (x > maxX) {
+                    maxX = x;
+                }
+
+                if (y < minY) {
+                    minY = y;
+                }
+
+                if (y > maxY) {
+                    maxY = y;
+                }
+            }
+        }
+
+        return foundValue
+            ? new Bounds[] {
+                    new DoubleBounds(minX, maxX),
+                    new DoubleBounds(minY, maxY) }
+            : null;
+    }
+
+
+    public static Bounds[] getBounds(XYDataset dataset) {
+        if (dataset instanceof XYSeriesCollection) {
+            return getBounds((XYSeriesCollection) dataset);
+        }
+        else if(dataset instanceof TimeSeriesCollection) {
+            return getBounds((TimeSeriesCollection) dataset);
+        }
+        else {
+            logger.warn("Unknown XYDataset instance: " + dataset.getClass());
+            return null;
+        }
+    }
+
+
+    public static Bounds[] getBounds(TimeSeriesCollection collection) {
+        int seriesCount = collection != null ? collection.getSeriesCount() : 0;
+
+        if (seriesCount == 0) {
+            logger.warn("TimeSeriesCollection is empty or has no Series set.");
+            return null;
+        }
+
+        boolean foundValue = false;
+
+        long lowerX = Long.MAX_VALUE;
+        long upperX = -Long.MAX_VALUE;
+
+        double lowerY = Double.MAX_VALUE;
+        double upperY = -Double.MAX_VALUE;
+
+        for (int i = 0, m = seriesCount; i < m; i++) {
+            TimeSeries series = collection.getSeries(i);
+
+            for (int j = 0, n = collection.getItemCount(i); j < n; j++) {
+                RegularTimePeriod rtp = series.getTimePeriod(j);
+
+                if (rtp == null) {
+                    continue;
+                }
+
+                foundValue = true;
+
+                long start = rtp.getFirstMillisecond();
+                long end   = rtp.getLastMillisecond();
+
+                if (start < lowerX) {
+                    lowerX = start;
+                }
+
+                if (end > upperX) {
+                    upperX = end;
+                }
+
+                double y = series.getValue(j).doubleValue();
+
+                lowerY = Math.min(lowerY, y);
+                upperY = Math.max(upperY, y);
+            }
+        }
+
+        if (foundValue) {
+            return new Bounds[] {
+                new TimeBounds(lowerX, upperX),
+                new DoubleBounds(lowerY, upperY)
+            };
+        }
+
+        return null;
+    }
+
+
+    /**
+     * Expand bounds by percent.
+     *
+     * @param bounds The bounds to expand.
+     * @param percent The percentage to expand.
+     *
+     * @return a new, expanded bounds.
+     */
+    public static Bounds expandBounds(Bounds bounds, double percent) {
+        if (bounds == null) {
+            return null;
+        }
+
+        double value  = (Double) bounds.getLower();
+        double expand = Math.abs(value / 100 * percent);
+
+        return expand != 0
+            ? new DoubleBounds(value-expand, value+expand)
+            : new DoubleBounds(-0.01 * percent, 0.01 * percent);
+    }
+
+
+    /**
+     * Expand range by percent.
+     *
+     * @param range The range to expand.
+     * @param percent The percentage to expand.
+     *
+     * @return a new, expanded range.
+     */
+    public static Range expandRange(Range range, double percent) {
+        if (range == null) {
+            return null;
+        }
+
+        double value  = range.getLowerBound();
+        double expand = Math.abs(value / 100 * percent);
+
+        return expand != 0
+            ? new Range(value-expand, value+expand)
+            : new Range(-0.01 * percent, 0.01 * percent);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/ChartInfoGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,189 @@
+package org.dive4elements.river.exports;
+
+import org.dive4elements.river.collections.FLYSArtifactCollection;
+import org.dive4elements.river.java2d.NOPGraphics2D;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import java.awt.Transparency;
+import java.awt.Graphics2D;
+
+import java.awt.geom.Rectangle2D;
+
+import java.awt.image.BufferedImage;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+import org.jfree.chart.ChartRenderingInfo;
+import org.jfree.chart.JFreeChart;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
+import org.dive4elements.artifactdatabase.state.Settings;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+
+
+/**
+ * An OutGenerator that generates meta information for charts. A concrete
+ * ChartInfoGenerator need to instantiate a concrete ChartGenerator and dispatch
+ * the methods to that instance. The only thing this ChartInfoGenerator needs
+ * to, is to overwrite the generate() method which doesn't write the chart image
+ * to the OutputStream but a Document that contains some meta information of the
+ * created chart.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public abstract class ChartInfoGenerator implements OutGenerator {
+
+    public static final boolean USE_NOP_GRAPHICS =
+        Boolean.getBoolean("info.rendering.nop.graphics");
+
+    /** The logger used in this generator.*/
+    private static Logger logger =
+        Logger.getLogger(ChartInfoGenerator.class);
+
+
+    /** The OutGenerator that creates the charts.*/
+    protected ChartGenerator generator;
+
+    protected OutputStream out;
+
+
+
+    public ChartInfoGenerator(ChartGenerator generator) {
+        this.generator = generator;
+    }
+
+
+    /**
+     * Dispatches the operation to the instantiated generator.
+     *
+     * @param request
+     * @param out
+     * @param context
+     */
+    public void init(Document request, OutputStream out, CallContext context) {
+        this.out = out;
+
+        generator.init(request, out, context);
+    }
+
+
+    /**
+     * Dispatches the operation to the instantiated generator.
+     *
+     * @param master The master artifact
+     */
+    public void setMasterArtifact(Artifact master) {
+        generator.setMasterArtifact(master);
+    }
+
+
+    /**
+     * Dispatches the operation to the instantiated generator.
+     *
+     * @param collection The collection.
+     */
+    public void setCollection(FLYSArtifactCollection collection) {
+        generator.setCollection(collection);
+    }
+
+
+    /**
+     * Dispatches the operation to the instantiated generator.
+     */
+    public void doOut(
+        ArtifactAndFacet artifactFacet,
+        Document         attr,
+        boolean          visible
+    ) {
+        generator.doOut(artifactFacet, attr, visible);
+    }
+
+
+    /**
+     * This method generates the chart using a concrete ChartGenerator but
+     * doesn't write the chart itself to the OutputStream but a Document that
+     * contains meta information of the created chart.
+     */
+    @Override
+    public void generate()
+    throws IOException
+    {
+        logger.debug("ChartInfoGenerator.generate");
+
+        JFreeChart chart = generator.generateChart();
+
+        int[] size = generator.getSize();
+        if (size == null) {
+            size = generator.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");
+        }
+
+
+        InfoGeneratorHelper helper = new InfoGeneratorHelper(generator);
+        Document doc = helper.createInfoDocument(chart, info);
+
+        XMLUtils.toStream(doc, out);
+    }
+
+
+    /**
+     * A proxy method which calls <i>generator</i>.getSettings() and returns its
+     * return value.
+     *
+     * @return a Settings object provided by <i>generator</i>.
+     */
+    @Override
+    public Settings getSettings() {
+        return generator.getSettings();
+    }
+
+
+    /**
+     * A proxy method which calls <i>generator</i>.setSettings().
+     *
+     * @param settings A settings object for the <i>generator</i>.
+     */
+    @Override
+    public void setSettings(Settings settings) {
+        generator.setSettings(settings);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/ChartSection.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,92 @@
+package org.dive4elements.river.exports;
+
+import org.apache.log4j.Logger;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ChartSection extends TypeSection {
+
+    private static Logger logger = Logger.getLogger(ChartSection.class);
+
+    public static final String TITLE_ATTR         = "title";
+    public static final String SUBTITLE_ATTR      = "subtitle";
+    public static final String DISPLAYGRID_ATTR   = "display-grid";
+    public static final String DISPLAYLOGO_ATTR   = "display-logo";
+    public static final String LOGOPLACEMENTH_ATTR = "logo-placeh";
+    public static final String LOGOPLACEMENTV_ATTR = "logo-placev";
+
+
+    public ChartSection() {
+        super("chart");
+    }
+
+
+    public void setTitle(String title) {
+        setStringValue(TITLE_ATTR, title);
+    }
+
+
+    public String getTitle() {
+        return getStringValue(TITLE_ATTR);
+    }
+
+
+    public void setSubtitle(String subtitle) {
+        setStringValue(SUBTITLE_ATTR, subtitle);
+    }
+
+
+    public String getSubtitle() {
+        return getStringValue(SUBTITLE_ATTR);
+    }
+
+
+    /** Get Property-value for display-logo property. */
+    public String getDisplayLogo() {
+        return getStringValue(DISPLAYLOGO_ATTR);
+    }
+
+
+    /** Set Property-value for display-logo property. */
+    public void setDisplayLogo(String logo) {
+        logger.debug("Setting Display logo string.");
+        setChoiceStringValue(DISPLAYLOGO_ATTR, logo, "logo");
+    }
+
+
+    /** Get Property-value for horizontal logo-placement property. */
+    public String getLogoHPlacement() {
+        return getStringValue(LOGOPLACEMENTH_ATTR);
+    }
+
+
+    /** Set Property-value for horizontal logo-placement property. */
+    public void setLogoHPlacement(String place) {
+        setChoiceStringValue(LOGOPLACEMENTH_ATTR, place, "placeh");
+    }
+
+
+    /** Get Property-value for vertical logo-placement property. */
+    public String getLogoVPlacement() {
+        return getStringValue(LOGOPLACEMENTV_ATTR);
+    }
+
+
+    /** Set Property-value for vertical logo-placement property. */
+    public void setLogoVPlacement(String place) {
+        setChoiceStringValue(LOGOPLACEMENTV_ATTR, place, "placev");
+    }
+
+
+    public void setDisplayGrid(boolean displayGrid) {
+        setBooleanValue(DISPLAYGRID_ATTR, displayGrid);
+    }
+
+
+    public Boolean getDisplayGrid() {
+        return getBooleanValue(DISPLAYGRID_ATTR);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/ChartSettings.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,299 @@
+package org.dive4elements.river.exports;
+
+import javax.xml.xpath.XPathConstants;
+
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+
+import org.dive4elements.artifactdatabase.state.DefaultSection;
+import org.dive4elements.artifactdatabase.state.DefaultSettings;
+import org.dive4elements.artifactdatabase.state.Section;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ChartSettings extends DefaultSettings {
+
+    private static final Logger logger = Logger.getLogger(ChartSettings.class);
+
+    protected ChartSection  chartSection;
+    protected LegendSection legendSection;
+    protected ExportSection exportSection;
+    protected Section       axesSection;
+
+
+    public ChartSettings() {
+        super();
+
+        axesSection = new DefaultSection("axes");
+        addSection(axesSection);
+    }
+
+
+    /**
+     * Sets the chart section. Old chart sections are removed.
+     *
+     * @param chartSection A new Section that stores chart specific attributes.
+     */
+    public void setChartSection(ChartSection chartSection) {
+        ChartSection oldChartSection = getChartSection();
+
+        if (oldChartSection != null) {
+            removeSection(oldChartSection);
+        }
+
+        this.chartSection = chartSection;
+        addSection(chartSection);
+    }
+
+
+    /**
+     * Returns the Section that stores chart specific attributes.
+     *
+     * @return the Section that stores chart specific attributes.
+     */
+    public ChartSection getChartSection() {
+        return chartSection;
+    }
+
+
+    /**
+     * Sets the legend section. Old legend sections are removed.
+     *
+     * @param legendSection A new Section that stores legend specific
+     * attributes.
+     */
+    public void setLegendSection(LegendSection legendSection) {
+        LegendSection oldLegendSection = getLegendSection();
+
+        if (oldLegendSection != null) {
+            removeSection(oldLegendSection);
+        }
+
+        this.legendSection = legendSection;
+        addSection(legendSection);
+    }
+
+
+    /**
+     * Returns the Section that stores legend specific attributes.
+     *
+     * @return the Section that stores legend specific attributes.
+     */
+    public LegendSection getLegendSection() {
+        return legendSection;
+    }
+
+
+    /**
+     * Sets the export section. Old export sections are removed.
+     *
+     * @param exportSection A new Section that stores export specific
+     * attributes.
+     */
+    public void setExportSection(ExportSection exportSection) {
+        ExportSection oldExportSection = getExportSection();
+
+        if (oldExportSection != null) {
+            removeSection(oldExportSection);
+        }
+
+        this.exportSection = exportSection;
+        addSection(exportSection);
+    }
+
+
+    /**
+     * Returns the Section that stores export specific attributes.
+     *
+     * @return the Section that stores export specific attributes.
+     */
+    public ExportSection getExportSection() {
+        return exportSection;
+    }
+
+
+    /**
+     * Adds a Section for a new axis of the chart.
+     *
+     * @param axisSection The Section specific for a chart axis.
+     */
+    public void addAxisSection(AxisSection axisSection) {
+        if (axisSection != null) {
+            axesSection.addSubsection(axisSection);
+        }
+    }
+
+
+    /**
+     * This method returns an AxisSection specified by <i>axisId</i> or null if
+     * no AxisSection is existing with identifier <i>axisId</i>.
+     *
+     * @param axisId The identifier of the wanted AxisSection.
+     *
+     * @return the AxisSection specified by <i>axisId</i> or null.
+     */
+    public AxisSection getAxisSection(String axisId) {
+        for (int i = 0, n = axesSection.getSubsectionCount(); i < n; i++) {
+            AxisSection as = (AxisSection) axesSection.getSubsection(i);
+            String      id = as.getIdentifier();
+
+            if (id != null && id.equals(axisId)) {
+                return as;
+            }
+        }
+
+        return null;
+    }
+
+
+    /**
+     * Parses the settings from <i>settings</i>. The result is a new
+     * ChartSettings instance.
+     *
+     * @param settings A <i>settings</i> node.
+     *
+     * @return a new <i>ChartSettings</i> instance.
+     */
+    public static ChartSettings parse(Node settings) {
+        if (settings == null) {
+            logger.warn("Tried to parse ChartSettings from empty Node!");
+            return null;
+        }
+
+        ChartSettings chartSettings = new ChartSettings();
+
+        parseAxes(chartSettings, settings);
+        parseChart(chartSettings, settings);
+        parseLegend(chartSettings, settings);
+        parseExport(chartSettings, settings);
+
+        return chartSettings;
+    }
+
+
+    protected static void parseAxes(ChartSettings target, Node settings) {
+        NodeList axesList = (NodeList) XMLUtils.xpath(
+            settings, "axes/axis", XPathConstants.NODESET, null);
+
+        int num = axesList != null ? axesList.getLength() : 0;
+
+        if (num <= 0) {
+            logger.debug("No axis sections found.");
+            return;
+        }
+
+        for (int i = 0; i < num; i++) {
+            parseAxis(target, axesList.item(i));
+        }
+    }
+
+
+    protected static void parseAxis(ChartSettings target, Node axis) {
+        AxisSection section = new AxisSection();
+
+        String id       = XMLUtils.xpathString(axis, "id", null);
+        String label    = XMLUtils.xpathString(axis, "label", null);
+        String fSize    = XMLUtils.xpathString(axis, "font-size", null);
+        String fixation = XMLUtils.xpathString(axis, "fixation", null);
+        String low      = XMLUtils.xpathString(axis, "lower", null);
+        String up       = XMLUtils.xpathString(axis, "upper", null);
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("Fount axis id:        '" + id + "'");
+            logger.debug("Fount axis label:     '" + label + "'");
+            logger.debug("Fount axis font size: '" + fSize + "'");
+            logger.debug("Fount axis fixation:  '" + fixation + "'");
+            logger.debug("Fount axis lower:     '" + low + "'");
+            logger.debug("Fount axis upper:     '" + up + "'");
+        }
+
+        section.setIdentifier(id);
+        section.setLabel(label);
+        section.setFontSize(Integer.parseInt(fSize.length() > 0 ? fSize : "-1"));
+        section.setFixed(Boolean.valueOf(fixation));
+        section.setLowerRange(Double.parseDouble(low.length() > 0 ? low : "0"));
+        section.setUpperRange(Double.parseDouble(up.length() > 0 ? up : "0"));
+
+        target.addAxisSection(section);
+    }
+
+
+    /**
+     * From document chart create ChartSection and populate it with attributes.
+     * Give this object to target as chartsection.
+     */
+    protected static void parseChart(ChartSettings target, Node chart) {
+        ChartSection chartSection = new ChartSection();
+
+        String title = XMLUtils.xpathString(chart, "chart/title", null);
+        String sub   = XMLUtils.xpathString(chart, "chart/subtitle", null);
+        String grid  = XMLUtils.xpathString(chart, "chart/display-grid", null);
+        String logo  = XMLUtils.xpathString(chart, "chart/display-logo", null);
+        String placeh = XMLUtils.xpathString(chart, "chart/logo-placeh", null);
+        String placev = XMLUtils.xpathString(chart, "chart/logo-placev", null);
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("Found chart title:    '" + title + "'");
+            logger.debug("Found chart subtitle: '" + sub + "'");
+            logger.debug("Found chart grid:     '" + grid + "'");
+            logger.debug("Found chart logo:     '" + logo + "'");
+            logger.debug("Found chart logo placeh: '" + placeh + "'");
+            logger.debug("Found chart logo placev: '" + placev + "'");
+        }
+
+        chartSection.setTitle(title);
+        chartSection.setSubtitle(sub);
+        chartSection.setDisplayGrid(Boolean.valueOf(grid));
+        chartSection.setDisplayLogo(logo);
+        chartSection.setLogoHPlacement(placeh);
+        chartSection.setLogoVPlacement(placev);
+
+        target.setChartSection(chartSection);
+    }
+
+
+    protected static void parseLegend(ChartSettings target, Node legend) {
+        LegendSection section = new LegendSection();
+
+        String vis   = XMLUtils.xpathString(legend, "legend/visibility", null);
+        String fSize = XMLUtils.xpathString(legend, "legend/font-size", null);
+        String lthre = XMLUtils.xpathString(legend, "legend/aggregation-threshold", null);
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("Found legend visibility: '" + vis + "'");
+            logger.debug("Found legend font size : '" + fSize + "'");
+            logger.debug("Found legend aggregation threshold : '" + lthre + "'");
+        }
+
+        section.setVisibility(Boolean.valueOf(vis));
+        section.setFontSize(Integer.valueOf(fSize.length() > 0 ? fSize : "-1"));
+        section.setAggregationThreshold(Integer.valueOf(lthre.length() >0 ? lthre : "-1"));
+
+        target.setLegendSection(section);
+    }
+
+
+    protected static void parseExport(ChartSettings target, Node export) {
+        ExportSection section = new ExportSection();
+
+        String width  = XMLUtils.xpathString(export, "export/width", null);
+        String height = XMLUtils.xpathString(export, "export/height", null);
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("Found export width : '" + width + "'");
+            logger.debug("Found export height: '" + height + "'");
+        }
+
+        section.setWidth(Integer.valueOf(width.length() > 0 ? width : "-1"));
+        section.setHeight(Integer.valueOf(height.length() > 0 ? height : "-1"));
+
+        target.setExportSection(section);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/ChoiceStringAttribute.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,42 @@
+package org.dive4elements.river.exports;
+
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ChoiceStringAttribute extends StringAttribute {
+
+    /** Indicator which type of choice is dealt with. */
+    protected String choiceType;
+
+
+    public ChoiceStringAttribute(String name,
+                           String value,
+                           boolean visible,
+                           String choiceType) {
+        super(name, value, visible);
+        this.choiceType = choiceType;
+    }
+
+
+    /**
+     * Calls VisibleAttribute.toXML() and appends afterwards an attribute
+     * <i>type</i> with value <i>string</i>.
+     *
+     * @param parent The parent Node.
+     *
+     * @return the new Node that represents this Attribute.
+     */
+    @Override
+    public Node toXML(Node parent) {
+        Element ele = (Element) super.toXML(parent);
+        ele.setAttribute("type", "string");
+        ele.setAttribute("choice", choiceType);
+
+        return ele;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/ComputedDischargeCurveExporter.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,229 @@
+package org.dive4elements.river.exports;
+
+import java.io.OutputStream;
+import java.text.NumberFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Date;
+import java.text.DateFormat;
+import java.util.Locale;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+import au.com.bytecode.opencsv.CSVWriter;
+
+import net.sf.jasperreports.engine.JasperExportManager;
+import net.sf.jasperreports.engine.JasperFillManager;
+import net.sf.jasperreports.engine.JasperPrint;
+import net.sf.jasperreports.engine.JRException;
+
+import org.dive4elements.artifacts.common.utils.Config;
+
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+import org.dive4elements.river.artifacts.access.RangeAccess;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.WQKms;
+import org.dive4elements.river.artifacts.model.WKmsJRDataSource;
+import org.dive4elements.river.artifacts.resources.Resources;
+
+import org.dive4elements.river.utils.FLYSUtils;
+import org.dive4elements.river.utils.Formatter;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ComputedDischargeCurveExporter extends AbstractExporter {
+
+    /** The logger used in this exporter.*/
+    private static Logger logger =
+        Logger.getLogger(ComputedDischargeCurveExporter.class);
+
+    public static final String CSV_W_HEADER =
+        "export.computed.discharge.curve.csv.header.w";
+
+    public static final String CSV_Q_HEADER =
+        "export.computed.discharge.curve.csv.header.q";
+
+    public static final String DEFAULT_CSV_W_HEADER  = "W [NN + m]";
+    public static final String DEFAULT_CSV_Q_HEADER  = "Q [m\u00b3/s]";
+
+    public static final String PDF_HEADER_MODE = "export.computed.discharge.pdf.mode";
+    public static final String JASPER_FILE = "export.computed.discharge.pdf.file";
+
+    protected List<WQKms> data;
+
+
+    public void init(Document request, OutputStream out, CallContext context) {
+        logger.debug("ComputedDischargeCurveExporter.init");
+
+        super.init(request, out, context);
+
+        this.data = new ArrayList<WQKms>();
+    }
+
+
+    @Override
+    protected void addData(Object d) {
+        if (d instanceof CalculationResult) {
+            d = ((CalculationResult)d).getData();
+        }
+        if (d instanceof WQKms[]){
+            data.addAll(Arrays.asList((WQKms [])d));
+            return;
+        }
+        else if (d instanceof WQKms) {
+            data.add((WQKms)d);
+            return;
+        }
+        logger.warn("Can't add data for csv export. Unkown data type " +
+                d.getClass().getName());
+    }
+
+
+    protected void writeCSVData(CSVWriter writer) {
+        logger.info("ComputedDischargeCurveExporter.writeData");
+
+        writeCSVHeader(writer);
+
+        NumberFormat wf  = getWFormatter();
+        NumberFormat qf  = getQFormatter();
+
+        double[] res = new double[3];
+
+        for (WQKms wqkms: data) {
+            int size = wqkms.size();
+
+            for (int i = 0; i < size; i++) {
+                res = wqkms.get(i, res);
+
+                writer.writeNext(new String[] {
+                    wf.format(res[0]),
+                    qf.format(res[1])
+                });
+            }
+        }
+    }
+
+
+    protected void writeCSVHeader(CSVWriter writer) {
+        logger.debug("ComputedDischargeCurveExporter.writeCSVHeader");
+
+        String unit = FLYSUtils.getRiver((FLYSArtifact) master).getWstUnit().getName();
+
+        writer.writeNext(new String[] {
+            msg(CSV_W_HEADER, DEFAULT_CSV_W_HEADER, new Object[] { unit }),
+            msg(CSV_Q_HEADER, DEFAULT_CSV_Q_HEADER)
+        });
+    }
+
+
+    /**
+     * Returns the number formatter for W values.
+     *
+     * @return the number formatter for W values.
+     */
+    protected NumberFormat getWFormatter() {
+        return Formatter.getComputedDischargeW(context);
+    }
+
+
+    /**
+     * Returns the number formatter for Q values.
+     *
+     * @return the number formatter for Q values.
+     */
+    protected NumberFormat getQFormatter() {
+        return Formatter.getComputedDischargeQ(context);
+    }
+
+
+    @Override
+    protected void writePDF(OutputStream out) {
+        WKmsJRDataSource source = createJRData();
+
+        String jasperFile = Resources.getMsg(
+                                context.getMeta(),
+                                JASPER_FILE,
+                                "/jasper/computed-discharge_en.jasper");
+        String confPath = Config.getConfigDirectory().toString();
+
+        Map parameters = new HashMap();
+        parameters.put("ReportTitle", "Exported Data");
+        try {
+            JasperPrint print = JasperFillManager.fillReport(
+                confPath + jasperFile,
+                parameters,
+                source);
+            JasperExportManager.exportReportToPdfStream(print, out);
+        }
+        catch(JRException je) {
+            logger.warn("Error generating PDF Report!");
+            je.printStackTrace();
+        }
+    }
+
+
+    protected WKmsJRDataSource createJRData() {
+        WKmsJRDataSource source = new WKmsJRDataSource();
+
+        addMetaData(source);
+        addWQData(source);
+
+        return source;
+    }
+
+
+    protected void addMetaData(WKmsJRDataSource source) {
+        CallMeta meta = context.getMeta();
+
+        FLYSArtifact flys = (FLYSArtifact) master;
+
+        source.addMetaData ("river", FLYSUtils.getRivername(flys));
+
+        Locale locale = Resources.getLocale(meta);
+        DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, locale);
+
+        source.addMetaData("date", df.format(new Date()));
+
+        RangeAccess rangeAccess = new RangeAccess(flys, null);
+        double[] kms = rangeAccess.getKmRange();
+        source.addMetaData("range", String.valueOf(kms[0]));
+
+        source.addMetaData("calculation", Resources.getMsg(
+                                            locale,
+                                            PDF_HEADER_MODE,
+                                            "Computed Discharge"));
+    }
+
+    protected void addWQData(WKmsJRDataSource source) {
+        NumberFormat wf  = getWFormatter();
+        NumberFormat qf  = getQFormatter();
+
+        double[] res = new double[3];
+
+        for (WQKms wqkms: data) {
+            int size = wqkms.size();
+
+            for (int i = 0; i < size; i++) {
+                res = wqkms.get(i, res);
+
+                source.addData(new String[] {
+                    "",   // Empty, the WKmsJRDtasource stores km here.
+                    wf.format(res[0]),
+                    qf.format(res[1])
+                });
+            }
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/ComputedDischargeCurveGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,269 @@
+package org.dive4elements.river.exports;
+
+import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.StaticWKmsArtifact;
+import org.dive4elements.river.artifacts.WINFOArtifact;
+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.jfree.FLYSAnnotation;
+import org.dive4elements.river.jfree.StickyAxisAnnotation;
+import org.dive4elements.river.jfree.StyledXYSeries;
+
+import org.dive4elements.river.utils.FLYSUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.jfree.data.xy.XYSeries;
+import org.w3c.dom.Document;
+
+
+/**
+ * An OutGenerator that generates discharge curves.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ComputedDischargeCurveGenerator
+extends      DischargeCurveGenerator
+implements   FacetTypes
+{
+    /** The logger used in this generator. */
+    private static Logger logger =
+        Logger.getLogger(ComputedDischargeCurveGenerator.class);
+
+    public static final String I18N_CHART_TITLE =
+        "chart.computed.discharge.curve.title";
+
+    public static final String I18N_CHART_SUBTITLE =
+        "chart.computed.discharge.curve.subtitle";
+
+    public static final String I18N_YAXIS_LABEL =
+        "chart.computed.discharge.curve.yaxis.label";
+
+    public static final String I18N_CHART_TITLE_DEFAULT = "Abflusskurve";
+    public static final String I18N_YAXIS_LABEL_DEFAULT = "W [NN + m]";
+    public static final String I18N_MAINVALUES_Q_LABEL = "Q (Haupt- und Extremwerte)";
+    public static final String I18N_MAINVALUES_W_LABEL = "W (Haupt- und Extremwerte)";
+
+
+    /** Trivial Constructor. */
+    public ComputedDischargeCurveGenerator () {
+        super();
+    }
+
+
+    @Override
+    protected String getDefaultChartTitle() {
+        return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT);
+    }
+
+
+    @Override
+    protected String getDefaultChartSubtitle() {
+        double[] dist = getRange();
+
+        Object[] args = new Object[] {
+            getRiverName(),
+            dist[0]
+        };
+
+        return msg(I18N_CHART_SUBTITLE, "", args);
+    }
+
+
+    @Override
+    protected String getDefaultYAxisLabel(int pos) {
+        FLYSArtifact flys = (FLYSArtifact) master;
+
+        String unit = FLYSUtils.getRiver(flys).getWstUnit().getName();
+
+        return msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL_DEFAULT, new Object[] { unit });
+    }
+
+
+    /**
+     * Process data, build up plot.
+     */
+    @Override
+    public void doOut(
+        ArtifactAndFacet artifactFacet,
+        Document         attr,
+        boolean          visible
+    ) {
+        String name = artifactFacet.getFacetName();
+
+        logger.debug("ComputedDischargeCurveGenerator.doOut: " + name);
+
+        if (name == null) {
+            logger.warn("Broken facet in computed discharge out generation.");
+            return;
+        }
+
+        //XXX DEAD CODE // Facet facet = artifactFacet.getFacet();
+
+        if (name.equals(COMPUTED_DISCHARGE_Q)) {
+            doQOut((WQKms) artifactFacet.getData(context), artifactFacet, attr, visible);
+        }
+        else if (name.equals(STATIC_WQ)) {
+            doWQOut(artifactFacet.getData(context), artifactFacet, attr, visible);
+        }
+        else if (name.equals(STATIC_WQ_ANNOTATIONS)) {
+            doWQAnnotations(
+                artifactFacet.getData(context),
+                artifactFacet,
+                attr,
+                visible);
+        }
+        else if (name.equals(COMPUTED_DISCHARGE_MAINVALUES_Q)
+                || name.equals(MAINVALUES_Q)
+                || name.equals(COMPUTED_DISCHARGE_MAINVALUES_W)
+                || name.equals(MAINVALUES_W)
+        ) {
+            doAnnotations((FLYSAnnotation)
+                artifactFacet.getData(context), artifactFacet, attr, visible);
+        }
+        else if (name.equals(STATIC_WKMS_INTERPOL) || name.equals(HEIGHTMARKS_POINTS)) {
+            doWAnnotations(
+                artifactFacet.getData(context),
+                artifactFacet,
+                attr,
+                visible);
+        }
+        else if (name.equals(STATIC_WKMS)) {
+            doWAnnotations(
+                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(DISCHARGE_CURVE)) {
+            doDischargeOut(
+                (WINFOArtifact) artifactFacet.getArtifact(),
+                artifactFacet.getData(context),
+                artifactFacet.getFacetDescription(),
+                attr,
+                visible);
+        }
+        else {
+            logger.warn("Unknown facet type for computed discharge: " + name);
+            return;
+        }
+    }
+
+
+    /**
+     * Add WQ Data to plot.
+     * @param wqkms data as double[][]
+     */
+    protected void doWQOut(
+        Object           wqkms,
+        ArtifactAndFacet aaf,
+        Document         theme,
+        boolean          visible
+    ) {
+        logger.debug("ComputedDischargeCurveGenerator: doWQOut");
+        double [][] data = (double [][]) wqkms;
+
+        XYSeries series = new StyledXYSeries(aaf.getFacetDescription(), theme);
+        StyledSeriesBuilder.addPoints(series, data, true);
+
+        addAxisSeries(series, YAXIS.W.idx, visible);
+    }
+
+
+    /**
+     * Add Q-Series to plot.
+     * @param wqkms actual data
+     * @param theme theme to use.
+     */
+    protected void doQOut(
+        WQKms            wqkms,
+        ArtifactAndFacet aaf,
+        Document         theme,
+        boolean          visible
+    ) {
+        logger.debug("ComputedDischargeCurveGenerator: doWQOut");
+        XYSeries series = new StyledXYSeries(aaf.getFacetDescription(), theme);
+        StyledSeriesBuilder.addPointsQW(series, wqkms);
+
+        addAxisSeries(series, YAXIS.W.idx, visible);
+    }
+
+
+    /**
+     * Add WQ-Annotations to plot.
+     * @param wqkms actual data
+     * @param theme theme to use.
+     */
+    protected void doWQAnnotations(
+        Object   wqkms,
+        ArtifactAndFacet aandf,
+        Document theme,
+        boolean  visible
+    ) {
+        List<StickyAxisAnnotation> xy = new ArrayList<StickyAxisAnnotation>();
+        double [][] data = (double [][]) wqkms;
+        for (int i = 0; i< data[0].length; i++) {
+            // TODO we need linear interpolation?
+            xy.add(new StickyAxisAnnotation(aandf.getFacetDescription(),
+                (float) data[0][i], StickyAxisAnnotation.SimpleAxis.X_AXIS));
+            xy.add(new StickyAxisAnnotation(aandf.getFacetDescription(),
+                (float) data[1][i], StickyAxisAnnotation.SimpleAxis.Y_AXIS));
+        }
+
+        doAnnotations(new FLYSAnnotation(aandf.getFacetDescription(), xy),
+            aandf, theme, visible);
+    }
+
+
+    /**
+     * Add W-Annotations to plot.
+     * @param wqkms actual data (double[][]).
+     * @param theme theme to use.
+     */
+    protected void doWAnnotations(
+        Object   wqkms,
+        ArtifactAndFacet aandf,
+        Document theme,
+        boolean  visible
+    ) {
+        Facet facet = aandf.getFacet();
+
+        List<StickyAxisAnnotation> xy = new ArrayList<StickyAxisAnnotation>();
+        // Try to find them as WKms as well...
+        if (wqkms instanceof double[][]) {
+            logger.debug("its double[][] time, baby");
+            double [][] data = (double [][]) wqkms;
+            // TODO Do we need interpolation?
+            for (int i = 0; i< data[0].length; i++) {
+                xy.add(new StickyAxisAnnotation(aandf.getFacetDescription(),
+                    (float) data[1][i], StickyAxisAnnotation.SimpleAxis.Y_AXIS));
+            }
+
+            doAnnotations(new FLYSAnnotation(facet.getDescription(), xy),
+                aandf, theme, visible);
+        }
+        else {
+            logger.debug("its wkms time, baby");
+            WKms data = (WKms) wqkms;
+            // Assume its WKms.
+            // XXX DEAD CODE // double location = getRange()[0];
+            double w = StaticWKmsArtifact.getWAtKmLin(data, getRange()[0]);
+            xy.add(new StickyAxisAnnotation(aandf.getFacetDescription(),
+              (float) w, StickyAxisAnnotation.SimpleAxis.Y_AXIS));
+
+            doAnnotations(new FLYSAnnotation(facet.getDescription(), xy),
+                aandf, 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/flys-artifacts/src/main/java/org/dive4elements/river/exports/ComputedDischargeCurveInfoGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,16 @@
+package org.dive4elements.river.exports;
+
+
+/**
+ * A ChartInfoGenerator that generates meta information for specific computed
+ * discharge curves.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ComputedDischargeCurveInfoGenerator extends ChartInfoGenerator {
+
+    public ComputedDischargeCurveInfoGenerator() {
+        super(new ComputedDischargeCurveGenerator());
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/CrossSectionGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,445 @@
+package org.dive4elements.river.exports;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Paint;
+import java.awt.Stroke;
+import java.text.NumberFormat;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.jfree.chart.LegendItemCollection;
+import org.jfree.chart.annotations.XYBoxAnnotation;
+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;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.geom.Lines;
+import org.dive4elements.river.artifacts.model.CrossSectionFacet;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.HYKFactory;
+import org.dive4elements.river.artifacts.resources.Resources;
+import org.dive4elements.river.jfree.FLYSAnnotation;
+import org.dive4elements.river.jfree.StyledXYSeries;
+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.utils.FLYSUtils;
+import org.dive4elements.river.utils.Formatter;
+import org.dive4elements.river.utils.ThemeUtil;
+
+
+/**
+ * An OutGenerator that generates cross section graphs.
+ */
+public class CrossSectionGenerator
+extends      LongitudinalSectionGenerator
+implements   FacetTypes
+{
+    /** The logger that is used in this generator. */
+    private static Logger logger =
+            Logger.getLogger(CrossSectionGenerator.class);
+
+    public static final String I18N_CHART_TITLE =
+            "chart.cross_section.title";
+
+    public static final String I18N_CHART_SUBTITLE =
+            "chart.cross_section.subtitle";
+
+    public static final String I18N_XAXIS_LABEL =
+            "chart.cross_section.xaxis.label";
+
+    public static final String I18N_YAXIS_LABEL =
+            "chart.cross_section.yaxis.label";
+
+    public static final String I18N_CHART_TITLE_DEFAULT = "Querprofildiagramm";
+    public static final String I18N_XAXIS_LABEL_DEFAULT = "Abstand [m]";
+    public static final String I18N_YAXIS_LABEL_DEFAULT = "W [NN + m]";
+
+
+    /** Trivial Constructor. */
+    public CrossSectionGenerator() {
+        super();
+    }
+
+
+    @Override
+    protected YAxisWalker getYAxisWalker() {
+        return new YAxisWalker() {
+            @Override
+            public int length() {
+                return 1;
+            }
+
+            /** Get identifier for this index. */
+            @Override
+            public String getId(int idx) {
+                return "W";
+            }
+        };
+    }
+
+
+    /**
+     * Get localized chart title.
+     */
+    @Override
+    public String getDefaultChartTitle() {
+        Object[] i18n_msg_args = new Object[] {
+                getRiverName()
+        };
+        return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT, i18n_msg_args);
+    }
+
+
+    /** Always return default subtitle. */
+    @Override
+    protected String getChartSubtitle() {
+        // XXX NOTE: overriding this method disables ChartSettings subtitle!
+        // The default implementation of this method in ChartGenerator returns
+        // the subtitle changed via the chart settings dialog. This method
+        // always returns the subtitle containing river and km, NEVER the
+        // ChartSettings subtitle!
+        return getDefaultChartSubtitle();
+    }
+
+
+    /** Get Charts default subtitle. */
+    @Override
+    protected String getDefaultChartSubtitle() {
+        List<DataProvider> providers =
+                context.getDataProvider(CrossSectionFacet.BLACKBOARD_CS_MASTER_DATA);
+        double km = 0d;
+        if (providers.size() > 0) {
+            FastCrossSectionLine csl = (FastCrossSectionLine) providers.get(0).
+                    provideData(CrossSectionFacet.BLACKBOARD_CS_MASTER_DATA,
+                            null, context);
+            km = csl == null ? -1 : csl.getKm();
+        }
+
+        Object[] args = new Object[] {
+                getRiverName(),
+                km
+        };
+
+        logger.debug("Locale: " + Resources.getLocale(context.getMeta()));
+
+        return msg(I18N_CHART_SUBTITLE, "", args);
+    }
+
+
+    /** Get color for hyk zones by their type (which is the name). */
+    protected Paint colorForHYKZone(String zoneName) {
+        if (zoneName.startsWith("R")) {
+            // Brownish.
+            return new Color(153, 60, 0);
+        }
+        else if (zoneName.startsWith("V")) {
+            // Greenish.
+            return new Color(0, 255, 0);
+        }
+        else if (zoneName.startsWith("B")) {
+            // Grayish.
+            return new Color(128, 128, 128);
+        }
+        else if (zoneName.startsWith("H")) {
+            // Blueish.
+            return new Color(0, 0, 255);
+        }
+        else {
+            // Default.
+            logger.debug("Unknown zone type found.");
+            return new Color(255, 0, 0);
+        }
+    }
+
+    @Override
+    protected void addAnnotationsToRenderer(XYPlot plot) {
+        super.addAnnotationsToRenderer(plot);
+
+        // Paints for the boxes/lines.
+        Stroke basicStroke = new BasicStroke(1.0f);
+
+        // XXX: DEAD CODE // Paint linePaint = new Color(255,  0,0,60);
+        Paint fillPaint = new Color(0,  255,0,60);
+        Paint tranPaint = new Color(0,    0,0, 0);
+
+        // OPTMIMIZE: Pre-calculate positions
+        ChartArea area = new ChartArea(
+                plot.getDomainAxis(0).getRange(),
+                plot.getRangeAxis().getRange());
+
+        for(FLYSAnnotation fa : this.annotations) {
+
+            // Access text styling, if any.
+            Document 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();
+                // XXX: DEAD CODE // lineStyle = themeAccess.parseLineStyle();
+                if (fa.getLabel() != null) {
+                    LegendItemCollection lic = new LegendItemCollection();
+                    LegendItemCollection old = plot.getFixedLegendItems();
+                    lic.add(createLegendItem(theme, fa.getLabel()));
+                    // (Re-)Add prior legend entries.
+                    if (old != null) {
+                        old.addAll(lic);
+                    }
+                    else {
+                        old = lic;
+                    }
+                    plot.setFixedLegendItems(old);
+                }
+            }
+
+            // Hyks.
+            for (HYKFactory.Zone zone: fa.getBoxes()) {
+                // For each zone, create a box to fill with color, a box to draw
+                // the lines and a text to display the type.
+                fillPaint = colorForHYKZone(zone.getName());
+
+                XYBoxAnnotation boxA = new XYBoxAnnotation(zone.getFrom(), area.atGround(),
+                        zone.getTo(), area.ofGround(0.03f), basicStroke, tranPaint, fillPaint);
+                XYBoxAnnotation boxB = new XYBoxAnnotation(zone.getFrom(), area.atGround(),
+                        zone.getTo(), area.atTop(), basicStroke, fillPaint, tranPaint);
+
+                XYTextAnnotation tex = new XYTextAnnotation(zone.getName(),
+                        zone.getFrom() + (zone.getTo() - zone.getFrom()) / 2.0d,
+                        area.ofGround(0.015f));
+                if (textStyle != null) {
+                    textStyle.apply(tex);
+                }
+
+                plot.getRenderer().addAnnotation(boxA, org.jfree.ui.Layer.BACKGROUND);
+                plot.getRenderer().addAnnotation(boxB, org.jfree.ui.Layer.BACKGROUND);
+                plot.getRenderer().addAnnotation(tex,  org.jfree.ui.Layer.BACKGROUND);
+            }
+        }
+    }
+
+    @Override
+    protected String getDefaultXAxisLabel() {
+        return msg(I18N_XAXIS_LABEL, I18N_XAXIS_LABEL_DEFAULT);
+    }
+
+
+    @Override
+    protected String getDefaultYAxisLabel(int pos) {
+        FLYSArtifact flys = (FLYSArtifact) master;
+
+        String unit = FLYSUtils.getRiver(flys).getWstUnit().getName();
+
+        return msg(I18N_YAXIS_LABEL,
+                   I18N_YAXIS_LABEL_DEFAULT,
+                   new Object[] { unit });
+    }
+
+
+    /**
+     * Let one facet do its job.
+     */
+    @Override
+    public void doOut(
+            ArtifactAndFacet artifactFacet,
+            Document         attr,
+            boolean          visible
+            ) {
+        String name = artifactFacet.getFacetName();
+
+        logger.debug("CrossSectionGenerator.doOut: " + name);
+
+        if (name == null) {
+            logger.error("No facet name for doOut(). No output generated!");
+            return;
+        }
+
+        if (name.equals(CROSS_SECTION)) {
+            doCrossSectionOut(
+                    artifactFacet.getData(context),
+                    artifactFacet.getFacetDescription(),
+                    attr,
+                    visible);
+        }
+        else if (name.equals(CROSS_SECTION_WATER_LINE)) {
+            doCrossSectionWaterLineOut(
+                    artifactFacet.getData(context),
+                    artifactFacet.getFacetDescription(),
+                    attr,
+                    visible);
+        }
+        else if (FacetTypes.IS.AREA(name)) {
+            doArea(artifactFacet.getData(context),
+                    artifactFacet,
+                    attr,
+                    visible);
+        }
+        else if (name.equals(HYK)) {
+            doHyk(artifactFacet.getData(context),
+                    artifactFacet.getFacetDescription(),
+                    attr,
+                    visible);
+        }
+        else if (FacetTypes.IS.MANUALLINE(name)) {
+            doCrossSectionWaterLineOut(
+                    artifactFacet.getData(context),
+                    artifactFacet.getFacetDescription(),
+                    attr,
+                    visible);
+        }
+        else if (FacetTypes.IS.MANUALPOINTS(name)) {
+            doPoints(artifactFacet.getData(context),
+                    artifactFacet,
+                    attr, visible, YAXIS.W.idx);
+        }
+        else {
+            logger.warn("CrossSection.doOut: Unknown facet name: " + name);
+            return;
+        }
+    }
+
+
+    /** Look up the axis identifier for a given facet type. */
+    @Override
+    public int axisIdxForFacet(String facetName) {
+        // TODO Where to add thid axis too.
+        return 0;
+    }
+
+
+    /**
+     * Do cross sections waterline out.
+     *
+     * @param seriesName name of the data (line) to display in legend.
+     * @param theme Theme for the data series.
+     */
+    protected void doCrossSectionWaterLineOut(
+            Object   o,
+            String   seriesName,
+            Document theme,
+            boolean  visible
+            ) {
+        logger.debug("CrossSectionGenerator.doCrossSectionWaterLineOut");
+
+        Lines.LineData lines = (Lines.LineData) o;
+        // DO NOT SORT DATA! This destroys the gaps indicated by NaNs.
+        StyledXYSeries series = new StyledXYSeries(seriesName, false, theme);
+
+        if (!ThemeUtil.parseShowLineLabel(theme)) {
+            series.setLabel("");
+        }
+        if (ThemeUtil.parseShowWidth(theme)) {
+            NumberFormat nf = Formatter.getMeterFormat(this.context);
+            String labelAdd = "b=" + nf.format(lines.width) + "m";
+            if (series.getLabel().length() == 0) {
+                series.setLabel(labelAdd);
+            }
+            else {
+                series.setLabel(series.getLabel() + ", " + labelAdd);
+            }
+        }
+        if (ThemeUtil.parseShowLevel(theme) && lines.points.length > 1
+                && lines.points[1].length > 0) {
+            NumberFormat nf = Formatter.getMeterFormat(this.context);
+            FLYSArtifact flys = (FLYSArtifact) master;
+
+            String unit = FLYSUtils.getRiver(flys).getWstUnit().getName();
+
+            String labelAdd = "W=" + nf.format(lines.points[1][0]) + unit;
+            if (series.getLabel().length() == 0) {
+                series.setLabel(labelAdd);
+            }
+            else {
+                series.setLabel(series.getLabel() + ", " + labelAdd);
+            }
+        }
+        if (ThemeUtil.parseShowMiddleHeight(theme) && lines.width != 0) {
+            NumberFormat nf = Formatter.getMeterFormat(this.context);
+            String labelAdd = "T=" + nf.format(lines.area / lines.width) + "m";
+            // : " + lines.area + "/" + lines.width);
+            if (series.getLabel().length() == 0) {
+                series.setLabel(labelAdd);
+            }
+            else {
+                series.setLabel(series.getLabel() + ", " + labelAdd);
+            }
+        }
+
+        StyledSeriesBuilder.addPoints(series, lines.points, false);
+
+        addAxisSeries(series, 0, visible);
+    }
+
+
+    /** Add HYK-Annotations (colorize and label some areas, draw lines. */
+    protected void doHyk(
+            Object   o,
+            String   seriesName,
+            Document theme,
+            boolean  visible
+            ) {
+        logger.debug("CrossSectionGenerator.doHyk");
+
+        List<HYKFactory.Zone> zones = (List<HYKFactory.Zone>) o;
+
+        if (zones == null || zones.isEmpty()) {
+            logger.warn("CrossSectionGenerator.doHYK: empty zone list received.");
+            return;
+        }
+
+        // Actual Styling is done in XYChartGenerator.
+        if (visible) {
+            addAnnotations(new FLYSAnnotation(seriesName, null, zones, theme));
+        }
+    }
+
+
+    /**
+     * Do cross sections out.
+     *
+     * @param seriesName name of the data (line) to display in legend.
+     * @param theme Theme for the data series.
+     */
+    protected void doCrossSectionOut(
+            Object   o,
+            String   seriesName,
+            Document theme,
+            boolean  visible
+            ) {
+        logger.debug("CrossSectionGenerator.doCrossSectionOut");
+
+        XYSeries series = new StyledXYSeries(seriesName, theme);
+
+        StyledSeriesBuilder.addPoints(series, (double [][]) o, false);
+
+        addAxisSeries(series, 0, visible);
+    }
+
+
+    /**
+     * Creates a new <i>ChartSection</i>.
+     *
+     * Overridden to prevent inclusion of subtitle.
+     *
+     * @return a new <i>ChartSection</i>.
+     */
+    @Override
+    protected ChartSection buildChartSection() {
+        ChartSection chartSection = new ChartSection();
+        chartSection.setTitle(getChartTitle());
+        chartSection.setDisplayGrid(isGridVisible());
+        chartSection.setDisplayLogo(showLogo());
+        chartSection.setLogoVPlacement(logoVPlace());
+        chartSection.setLogoHPlacement(logoHPlace());
+        return chartSection;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/CrossSectionInfoGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,14 @@
+package org.dive4elements.river.exports;
+
+
+/**
+ * A ChartInfoGenerator that generates meta information for specific cross
+ * sections.
+ */
+public class CrossSectionInfoGenerator extends ChartInfoGenerator {
+
+    public CrossSectionInfoGenerator() {
+        super(new CrossSectionGenerator());
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/DischargeCurveGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,188 @@
+package org.dive4elements.river.exports;
+
+import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.WQKms;
+import org.dive4elements.river.jfree.Bounds;
+import org.dive4elements.river.jfree.FLYSAnnotation;
+import org.dive4elements.river.jfree.StyledXYSeries;
+import org.dive4elements.river.model.Gauge;
+import org.dive4elements.river.model.River;
+import org.dive4elements.river.utils.FLYSUtils;
+
+import org.apache.log4j.Logger;
+import org.jfree.chart.axis.ValueAxis;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.data.Range;
+import org.jfree.data.xy.XYSeries;
+import org.w3c.dom.Document;
+
+
+/**
+ * An OutGenerator that generates discharge curves.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DischargeCurveGenerator
+extends      XYChartGenerator
+implements   FacetTypes {
+
+    public static enum YAXIS {
+        W(0);
+        protected int idx;
+        private YAXIS(int c) {
+            idx = c;
+        }
+    }
+
+    /** The logger used in this generator. */
+    private static Logger logger =
+        Logger.getLogger(DischargeCurveGenerator.class);
+
+    public static final String I18N_CHART_TITLE =
+        "chart.discharge.curve.title";
+
+    public static final String I18N_CHART_SUBTITLE =
+        "chart.discharge.curve.subtitle";
+
+    public static final String I18N_XAXIS_LABEL =
+        "chart.discharge.curve.xaxis.label";
+
+    public static final String I18N_YAXIS_LABEL =
+        "chart.discharge.curve.yaxis.label";
+
+    public static final String I18N_CHART_TITLE_DEFAULT  = "Abflusskurven";
+    public static final String I18N_XAXIS_LABEL_DEFAULT  = "Q [m\u00b3/s]";
+    public static final String I18N_YAXIS_LABEL_DEFAULT  = "W [cm]";
+
+
+    public DischargeCurveGenerator() {
+        super();
+    }
+
+
+    @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 always null to suppress subtitles.
+     */
+    @Override
+    protected String getDefaultChartTitle() {
+        return null;
+    }
+
+
+    @Override
+    protected String getDefaultXAxisLabel() {
+        return msg(I18N_XAXIS_LABEL, I18N_XAXIS_LABEL_DEFAULT);
+    }
+
+    @Override
+    protected String getDefaultYAxisLabel(int pos) {
+        return msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL_DEFAULT);
+    }
+
+
+    /* TODO is this one really needed? */
+    @Override
+    protected boolean zoomX(XYPlot plot, ValueAxis axis, Bounds bounds, Range x) {
+        boolean zoomin = super.zoom(plot, axis, bounds, x);
+
+        if (!zoomin) {
+            axis.setLowerBound(0d);
+        }
+
+        return zoomin;
+    }
+
+
+    @Override
+    public void doOut(
+        ArtifactAndFacet artifactFacet,
+        Document         theme,
+        boolean          visible
+    ) {
+        String name = artifactFacet.getFacetName();
+        logger.debug("DischargeCurveGenerator.doOut: " + name);
+
+        if (name.equals(DISCHARGE_CURVE)
+                || name.equals(GAUGE_DISCHARGE_CURVE)) {
+            doDischargeOut(
+                (FLYSArtifact)artifactFacet.getArtifact(),
+                artifactFacet.getData(context),
+                artifactFacet.getFacetDescription(),
+                theme,
+                visible);
+        }
+        else if (name.equals(COMPUTED_DISCHARGE_MAINVALUES_Q)
+                || name.equals(MAINVALUES_Q)
+                || name.equals(COMPUTED_DISCHARGE_MAINVALUES_W)
+                || name.equals(MAINVALUES_W))
+        {
+            doAnnotations((FLYSAnnotation) artifactFacet.getData(context),
+                artifactFacet, theme, visible);
+        }
+        else if (FacetTypes.IS.MANUALPOINTS(name)) {
+            doPoints(artifactFacet.getData(context),
+                artifactFacet,
+                theme, visible, YAXIS.W.idx);
+        }
+        else {
+           logger.warn("DischargeCurveGenerator.doOut: Unknown facet name: " + name);
+           return;
+        }
+    }
+
+
+    /**
+     * Add series with discharge curve to diagram.
+     */
+    protected void doDischargeOut(
+        FLYSArtifact artifact,
+        Object        o,
+        String        description,
+        Document      theme,
+        boolean       visible)
+    {
+        WQKms wqkms = (WQKms) o;
+
+        String gaugeName = wqkms.getName();
+
+        River river = FLYSUtils.getRiver(artifact);
+
+        if (river == null) {
+            logger.debug("no river found");
+            return;
+        }
+
+        Gauge gauge = river.determineGaugeByName(gaugeName);
+
+        if (gauge == null) {
+            logger.debug("no gauge found");
+            return;
+        }
+
+        XYSeries series = new StyledXYSeries(description, theme);
+
+        StyledSeriesBuilder.addPointsQW(series, wqkms);
+
+        addAxisSeries(series, YAXIS.W.idx, 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/flys-artifacts/src/main/java/org/dive4elements/river/exports/DischargeCurveInfoGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,16 @@
+package org.dive4elements.river.exports;
+
+
+/**
+ * A ChartInfoGenerator that generates meta information for specific discharge
+ * curves.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DischargeCurveInfoGenerator extends ChartInfoGenerator {
+
+    public DischargeCurveInfoGenerator() {
+        super(new DischargeCurveGenerator());
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/DischargeLongitudinalSectionExporter.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,133 @@
+package org.dive4elements.river.exports;
+
+import au.com.bytecode.opencsv.CSVWriter;
+
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.WQCKms;
+import org.dive4elements.river.artifacts.model.WQKms;
+
+import java.text.NumberFormat;
+
+import org.apache.log4j.Logger;
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DischargeLongitudinalSectionExporter extends WaterlevelExporter {
+
+    /** The logger used in this exporter.*/
+    private static Logger logger =
+        Logger.getLogger(DischargeLongitudinalSectionExporter.class);
+
+
+    public static final String CSV_KM_HEADER =
+        "export.discharge.longitudinal.section.csv.header.km";
+
+    public static final String CSV_W_HEADER =
+        "export.discharge.longitudinal.section.csv.header.w";
+
+    public static final String CSV_CW_HEADER =
+        "export.discharge.longitudinal.section.csv.header.cw";
+
+    public static final String CSV_Q_HEADER =
+        "export.discharge.longitudinal.section.csv.header.q";
+
+    public static final String DEFAULT_CSV_KM_HEADER = "Fluss-Km";
+    public static final String DEFAULT_CSV_W_HEADER  = "W [NN + m]";
+    public static final String DEFAULT_CSV_CW_HEADER = "W korr.";
+    public static final String DEFAULT_CSV_Q_HEADER  = "Q [m\u00b3/s]";
+
+
+    @Override
+    protected void addData(Object d) {
+        if (d instanceof CalculationResult) {
+            d = ((CalculationResult)d).getData();
+            if (d instanceof WQKms []) {
+                data.add((WQKms [])d);
+            }
+        }
+    }
+
+
+    @Override
+    protected void writeCSVHeader(
+        CSVWriter writer,
+        boolean   atGauge,
+        boolean   isQ
+    ) {
+        logger.info("WaterlevelExporter.writeCSVHeader");
+
+        writer.writeNext(new String[] {
+            msg(CSV_KM_HEADER, DEFAULT_CSV_KM_HEADER),
+            msg(CSV_W_HEADER, DEFAULT_CSV_W_HEADER),
+            msg(CSV_CW_HEADER, DEFAULT_CSV_CW_HEADER),
+            msg(CSV_Q_HEADER, DEFAULT_CSV_Q_HEADER),
+            msg(CSV_Q_DESC_HEADER, DEFAULT_CSV_Q_DESC_HEADER)
+        });
+    }
+
+
+    protected void wQKms2CSV(
+        CSVWriter writer,
+        WQKms     wqkms,
+        boolean   atGauge,
+        boolean   isQ
+    ) {
+        logger.debug("WaterlevelExporter.wQKms2CSV");
+
+        int      size   = wqkms.size();
+        double[] result = new double[4];
+
+        NumberFormat kmf = getKmFormatter();
+        NumberFormat wf  = getWFormatter();
+        NumberFormat qf  = getQFormatter();
+
+        for (int i = 0; i < size; i ++) {
+            result = wqkms.get(i, result);
+
+            String name = wqkms.getName();
+            String wc = "";
+            if (wqkms instanceof WQCKms) {
+                wc = wf.format(result[3]);
+            }
+
+            writer.writeNext(new String[] {
+                kmf.format(result[2]),
+                wf.format(result[0]),
+                wc,
+                qf.format(result[1]),
+                name
+            });
+        }
+    }
+
+
+    @Override
+    protected void addWSTColumn(WstWriter writer, WQKms wqkms) {
+        String name = wqkms.getName();
+
+        // is it a W or a Q mode?
+        int wIdx = name.indexOf("W");
+        int qIdx = name.indexOf("Q");
+
+        String wq = null;
+        if (wIdx >= 0) {
+            wq = "W";
+        }
+        else if (qIdx >= 0) {
+            wq = "Q";
+        }
+
+        // we just want to display the first W or Q value in the WST
+        int start = name.indexOf("(");
+        int end   = name.indexOf(")");
+
+        String   tmp    = name.substring(start+1, end);
+        String[] values = tmp.split(";");
+
+        String column = wq + "=" + values[0];
+
+        writer.addColumn(column);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/DischargeLongitudinalSectionGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,125 @@
+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.FLYSAnnotation;
+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((FLYSAnnotation) artifactFacet.getData(context),
+                 artifactFacet, attr, visible);
+        }
+        else if (FacetTypes.IS.MANUALPOINTS(name)) {
+            doPoints(artifactFacet.getData(context),
+                artifactFacet,
+                attr, visible, YAXIS.W.idx);
+        }
+        else {
+            Processor processor = new WOutProcessor();
+            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 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/DischargeLongitudinalSectionInfoGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,17 @@
+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 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/DoubleAttribute.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,34 @@
+package org.dive4elements.river.exports;
+
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DoubleAttribute extends VisibleAttribute {
+
+
+    public DoubleAttribute(String name, double value, boolean visible) {
+        super(name, value, visible);
+    }
+
+
+    /**
+     * Calls VisibleAttribute.toXML() and appends afterwards an attribute
+     * <i>type</i> with value <i>double</i>.
+     *
+     * @param parent The parent Node.
+     *
+     * @return the new Node that represents this Attribute.
+     */
+    @Override
+    public Node toXML(Node parent) {
+        Element ele = (Element) super.toXML(parent);
+        ele.setAttribute("type", "double");
+
+        return ele;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/DurationCurveExporter.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,263 @@
+package org.dive4elements.river.exports;
+
+import java.io.OutputStream;
+import java.text.NumberFormat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Date;
+import java.text.DateFormat;
+import java.util.Locale;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+import au.com.bytecode.opencsv.CSVWriter;
+
+import net.sf.jasperreports.engine.JasperExportManager;
+import net.sf.jasperreports.engine.JasperFillManager;
+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;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+import org.dive4elements.river.artifacts.access.RangeAccess;
+import org.dive4elements.river.artifacts.model.WQDay;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.WKmsJRDataSource;
+import org.dive4elements.river.artifacts.resources.Resources;
+
+import org.dive4elements.river.utils.FLYSUtils;
+import org.dive4elements.river.utils.Formatter;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DurationCurveExporter extends AbstractExporter {
+
+    /** The logger used in this exporter. */
+    private static Logger logger = Logger.getLogger(DurationCurveExporter.class);
+
+
+    public static final String CSV_DURATION_HEADER =
+        "export.duration.curve.csv.header.duration";
+
+    public static final String CSV_W_HEADER =
+        "export.duration.curve.csv.header.w";
+
+    public static final String CSV_Q_HEADER =
+        "export.duration.curve.csv.header.q";
+
+    public static final String DEFAULT_CSV_DURATION_HEADER = "D [Tagen]";
+    public static final String DEFAULT_CSV_W_HEADER  = "W [NN + m]";
+    public static final String DEFAULT_CSV_Q_HEADER  = "Q [m\u00b3/s]";
+
+    public static final String PDF_HEADER_MODE = "export.duration.pdf.mode";
+    public static final String JASPER_FILE = "export.duration.pdf.file";
+
+    /** 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>();
+    }
+
+
+    @Override
+    protected void addData(Object d) {
+        if (d instanceof CalculationResult) {
+            d = ((CalculationResult)d).getData();
+            if (d instanceof WQDay) {
+                data.add((WQDay)d);
+            }
+        }
+    }
+
+
+    protected void writeCSVData(CSVWriter writer) {
+        logger.info("DurationCurveExporter.writeData");
+
+        writeCSVHeader(writer);
+
+        for (WQDay wqday: data) {
+            wQDay2CSV(writer, wqday);
+        }
+    }
+
+
+    protected void writeCSVHeader(CSVWriter writer) {
+        logger.info("DurationCurveExporter.writeCSVHeader");
+
+        writer.writeNext(new String[] {
+            msg(CSV_W_HEADER, DEFAULT_CSV_W_HEADER),
+            msg(CSV_Q_HEADER, DEFAULT_CSV_Q_HEADER),
+            msg(CSV_DURATION_HEADER, DEFAULT_CSV_DURATION_HEADER)
+        });
+    }
+
+
+    protected void wQDay2CSV(CSVWriter writer, WQDay wqday) {
+        logger.debug("DurationCurveExporter.wQDay2CSV");
+
+        int size = wqday.size();
+
+        NumberFormat wf  = getWFormatter();
+        NumberFormat qf  = getQFormatter();
+        NumberFormat df  = getDFormatter();
+
+        if (wqday.isIncreasing()) {
+            for (int i = size-1; i >= 0; i --) {
+                writer.writeNext(new String[] {
+                    wf.format(wqday.getW(i)),
+                    qf.format(wqday.getQ(i)),
+                    df.format(wqday.getDay(i))
+                });
+            }
+        }
+        else {
+            for (int i = 0; i < size; i ++) {
+                writer.writeNext(new String[] {
+                    wf.format(wqday.getW(i)),
+                    qf.format(wqday.getQ(i)),
+                    df.format(wqday.getDay(i))
+                });
+            }
+        }
+    }
+
+
+    /**
+     * Returns the number formatter for W values.
+     *
+     * @return the number formatter for W values.
+     */
+    @Override
+    protected NumberFormat getWFormatter() {
+        return Formatter.getDurationW(context);
+    }
+
+
+    /**
+     * Returns the number formatter for Q values.
+     *
+     * @return the number formatter for Q values.
+     */
+    @Override
+    protected NumberFormat getQFormatter() {
+        return Formatter.getDurationQ(context);
+    }
+
+
+    /**
+     * Returns the number formatter for duration values.
+     *
+     * @return the number formatter for duration values.
+     */
+    protected NumberFormat getDFormatter() {
+        return Formatter.getDurationD(context);
+    }
+
+
+    @Override
+    protected void writePDF(OutputStream out) {
+        WKmsJRDataSource source = createJRData();
+
+        String jasperFile = Resources.getMsg(
+                                context.getMeta(),
+                                JASPER_FILE,
+                                "/jasper/duration_en.jasper");
+        String confPath = Config.getConfigDirectory().toString();
+
+        Map parameters = new HashMap();
+        parameters.put("ReportTitle", "Exported Data");
+        try {
+            JasperPrint print = JasperFillManager.fillReport(
+                confPath + jasperFile,
+                parameters,
+                source);
+            JasperExportManager.exportReportToPdfStream(print, out);
+        }
+        catch(JRException je) {
+            logger.warn("Error generating PDF Report!");
+            je.printStackTrace();
+        }
+    }
+
+    protected WKmsJRDataSource createJRData() {
+        WKmsJRDataSource source = new WKmsJRDataSource();
+
+        addMetaData(source);
+        for (WQDay wqday: data) {
+            addWQDayData(source, wqday);
+        }
+
+        return source;
+    }
+
+
+    protected void addMetaData(WKmsJRDataSource source) {
+        CallMeta meta = context.getMeta();
+
+        FLYSArtifact flys = (FLYSArtifact) master;
+
+        source.addMetaData ("river", FLYSUtils.getRivername(flys));
+
+        Locale locale = Resources.getLocale(meta);
+        DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, locale);
+
+        source.addMetaData("date", df.format(new Date()));
+
+        RangeAccess rangeAccess = new RangeAccess(flys, null);
+        double[] kms = rangeAccess.getKmRange();
+        source.addMetaData("range", String.valueOf(kms[0]));
+
+        source.addMetaData("calculation", Resources.getMsg(
+                                            locale,
+                                            PDF_HEADER_MODE,
+                                            "Duration"));
+    }
+
+    protected void addWQDayData(WKmsJRDataSource source, WQDay wqday) {
+        int size = wqday.size();
+
+        NumberFormat wf  = getWFormatter();
+        NumberFormat qf  = getQFormatter();
+        NumberFormat df  = getDFormatter();
+
+        if (wqday.isIncreasing()) {
+            for (int i = size-1; i >= 0; i --) {
+                source.addData(new String[] {
+                    "",
+                    wf.format(wqday.getW(i)),
+                    qf.format(wqday.getQ(i)),
+                    "", "", "",
+                    df.format(wqday.getDay(i))
+                });
+            }
+        }
+        else {
+            for (int i = 0; i < size; i ++) {
+                source.addData(new String[] {
+                    "",
+                    wf.format(wqday.getW(i)),
+                    qf.format(wqday.getQ(i)),
+                    "", "", "",
+                    df.format(wqday.getDay(i))
+                });
+            }
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/DurationCurveGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,313 @@
+package org.dive4elements.river.exports;
+
+import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.WQDay;
+import org.dive4elements.river.jfree.Bounds;
+import org.dive4elements.river.jfree.FLYSAnnotation;
+import org.dive4elements.river.jfree.StyledXYSeries;
+
+import java.awt.Font;
+import java.awt.geom.Point2D;
+
+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.Range;
+import org.jfree.data.xy.XYSeries;
+import org.w3c.dom.Document;
+
+
+/**
+ * An OutGenerator that generates duration curves.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DurationCurveGenerator
+extends      XYChartGenerator
+implements   FacetTypes
+{
+    public static enum YAXIS {
+        W(0),
+        Q(1);
+        public int idx;
+        private YAXIS(int c) {
+           idx = c;
+        }
+    }
+
+    /** Local logger. */
+    private static Logger logger =
+        Logger.getLogger(DurationCurveGenerator.class);
+
+    public static final String I18N_CHART_TITLE =
+        "chart.duration.curve.title";
+
+    public static final String I18N_CHART_SUBTITLE =
+        "chart.duration.curve.subtitle";
+
+    public static final String I18N_XAXIS_LABEL =
+        "chart.duration.curve.xaxis.label";
+
+    public static final String I18N_YAXIS_LABEL =
+        "chart.duration.curve.yaxis.label";
+
+    public static final String I18N_CHART_TITLE_DEFAULT  =
+        "Dauerlinie";
+
+    public static final String I18N_XAXIS_LABEL_DEFAULT  =
+        "Unterschreitungsdauer [Tage]";
+
+    public static final String I18N_YAXIS_LABEL_DEFAULT  =
+        "W [NN + m]";
+
+
+    public DurationCurveGenerator() {
+        super();
+    }
+
+
+    /**
+     * Create Axis for given index.
+     * @return axis with according internationalized label.
+     */
+    @Override
+    protected NumberAxis createYAxis(int index) {
+        Font labelFont = new Font("Tahoma", Font.BOLD, 14);
+        String label   = getYAxisLabel(index);
+
+        NumberAxis axis = createNumberAxis(index, label);
+        if (index == YAXIS.W.idx) {
+            axis.setAutoRangeIncludesZero(false);
+        }
+        axis.setLabelFont(labelFont);
+        return axis;
+    }
+
+
+    @Override
+    protected String getDefaultChartTitle() {
+        return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT);
+    }
+
+
+    @Override
+    protected String getDefaultChartSubtitle() {
+        double[] dist  = getRange();
+
+        Object[] args = new Object[] {
+            getRiverName(),
+            dist[0]
+        };
+
+        return msg(I18N_CHART_SUBTITLE, "", args);
+    }
+
+
+    @Override
+    protected String getDefaultXAxisLabel() {
+        return msg(I18N_XAXIS_LABEL, I18N_XAXIS_LABEL_DEFAULT);
+    }
+
+
+    @Override
+    protected String getDefaultYAxisLabel(int index) {
+        String label = "default";
+        if (index == YAXIS.W.idx) {
+            label = msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL_DEFAULT);
+        }
+        else if (index == YAXIS.Q.idx) {
+            // TODO i18n for this label
+            label = "Q [m\u00b3/s]";
+            //label = msg(get2YAxisLabelKey(), get2YAxisDefaultLabel());
+        }
+
+        return label;
+    }
+
+
+    @Override
+    protected boolean zoomX(XYPlot plot, ValueAxis axis, Bounds bounds, Range x) {
+        boolean zoomin = super.zoom(plot, axis, bounds, x);
+
+        if (!zoomin) {
+            axis.setLowerBound(0d);
+        }
+
+        axis.setUpperBound(364);
+
+        return zoomin;
+    }
+
+
+    /**
+     * This method overrides the method in the parent class to set the lower
+     * bounds of the Q axis to 0. This axis should never display negative
+     * values on its own.
+     */
+    @Override
+    protected boolean zoomY(XYPlot plot, ValueAxis axis, Bounds bounds, Range x) {
+        boolean zoomin = super.zoom(plot, axis, bounds, x);
+
+        if (!zoomin && axis instanceof IdentifiableNumberAxis) {
+            String id = ((IdentifiableNumberAxis) axis).getId();
+
+            if (YAXIS.Q.toString().equals(id)) {
+                axis.setLowerBound(0d);
+            }
+        }
+
+        return zoomin;
+    }
+
+
+    @Override
+    public void doOut(
+        ArtifactAndFacet artifactFacet,
+        Document         attr,
+        boolean          visible
+    ) {
+        String name = artifactFacet.getFacetName();
+
+        logger.debug("DurationCurveGenerator.doOut: " + name);
+
+        if (name == null || name.length() == 0) {
+            logger.error("No facet given. Cannot create dataset.");
+            return;
+        }
+
+        if (name.equals(DURATION_W)) {
+            doWOut(
+                (WQDay) artifactFacet.getData(context),
+                artifactFacet,
+                attr,
+                visible);
+        }
+        else if (name.equals(DURATION_Q)) {
+            doQOut(
+                (WQDay) artifactFacet.getData(context),
+                artifactFacet,
+                attr,
+                visible);
+        }
+        else if (name.equals(DURATION_MAINVALUES_Q)
+                || name.equals(MAINVALUES_Q)
+                || name.equals(COMPUTED_DISCHARGE_MAINVALUES_W)
+                || name.equals(MAINVALUES_W)
+        ) {
+            doAnnotations(
+                (FLYSAnnotation) artifactFacet.getData(context),
+                artifactFacet,
+                attr,
+                visible);
+        }
+        else if (name.equals(RELATIVE_POINT)) {
+            doPointOut((Point2D) artifactFacet.getData(context),
+                artifactFacet,
+                attr,
+                visible);
+        }
+        else if (FacetTypes.IS.MANUALPOINTS(name)) {
+            doPoints(
+                artifactFacet.getData(context),
+                artifactFacet,
+                attr, visible, YAXIS.W.idx);
+        }
+        else {
+            logger.warn("Unknown facet name: " + name);
+            return;
+        }
+    }
+
+
+    /**
+     * Creates the series for a duration curve's W facet.
+     *
+     * @param wqdays The WQDay store that contains the Ws.
+     * @param theme
+     */
+    protected void doWOut(
+        WQDay            wqdays,
+        ArtifactAndFacet aaf,
+        Document         theme,
+        boolean          visible
+    ) {
+        logger.debug("DurationCurveGenerator.doWOut");
+
+        XYSeries series = new StyledXYSeries(aaf.getFacetDescription(), theme);
+
+        int size = wqdays.size();
+        for (int i = 0; i < size; i++) {
+            int  day = wqdays.getDay(i);
+            double w = wqdays.getW(i);
+
+            series.add(day, w);
+        }
+
+        addAxisSeries(series, YAXIS.W.idx, visible);
+    }
+
+    protected void doPointOut(
+        Point2D point,
+        ArtifactAndFacet aandf,
+        Document theme,
+        boolean visible
+    ){
+        logger.debug("DurationCurveGenerator.doPointOut");
+
+        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
+
+        series.add(point.getX(), point.getY());
+
+        addAxisSeries(series, YAXIS.W.idx, visible);
+    }
+
+
+    /**
+     * Creates the series for a duration curve's Q facet.
+     *
+     * @param wqdays The WQDay store that contains the Qs.
+     * @param theme
+     */
+    protected void doQOut(
+        WQDay            wqdays,
+        ArtifactAndFacet aaf,
+        Document         theme,
+        boolean          visible
+    ) {
+        logger.debug("DurationCurveGenerator.doQOut");
+
+        XYSeries series = new StyledXYSeries(aaf.getFacetDescription(), theme);
+
+        int size = wqdays.size();
+        for (int i = 0; i < size; i++) {
+            int  day = wqdays.getDay(i);
+            double q = wqdays.getQ(i);
+
+            series.add(day, q);
+        }
+
+        addAxisSeries(series, YAXIS.Q.idx, visible);
+    }
+
+
+    @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();
+            }
+        };
+    }
+
+    // MainValue-Annotations should be visualized by a line that goes to the curve itself.
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/DurationCurveInfoGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,17 @@
+package org.dive4elements.river.exports;
+
+
+/**
+ * A ChartInfoGenerator that generates meta information for specific duration
+ * curves.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DurationCurveInfoGenerator
+extends      ChartInfoGenerator
+{
+    public DurationCurveInfoGenerator() {
+        super(new DurationCurveGenerator());
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/EmptySettings.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,78 @@
+package org.dive4elements.river.exports;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+import org.dive4elements.artifactdatabase.state.Settings;
+import org.dive4elements.artifactdatabase.state.Section;
+
+
+/**
+ * An implementation of <i>Settings</i> that doesn't take new <i>Section</i>s
+ * and that always creates an empty <b>settings</b> DOM node in its
+ * <i>toXML()</i> operation.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class EmptySettings implements Settings {
+
+    public EmptySettings() {
+    }
+
+
+    /**
+     * This method has no function. It is not implemented!
+     *
+     * @param section A Section.
+     */
+    @Override
+    public void addSection(Section section) {
+        // do nothing
+    }
+
+
+    /**
+     * Always returns 0.
+     *
+     * @return 0.
+     */
+    @Override
+    public int getSectionCount() {
+        return 0;
+    }
+
+
+    /**
+     * This method always returns null. It is not implemented!
+     *
+     * @param pos A position.
+     *
+     * @return null.
+     */
+    @Override
+    public Section getSection(int pos) {
+        return null;
+    }
+
+
+    /**
+     * This method has no function. It is not implemented!
+     */
+    @Override
+    public void removeSection(Section section) {
+        // do nothing
+    }
+
+
+    /**
+     * This method creates an empty <i>settings</i> DOM node.
+     *
+     * @param parent A parent DOM node.
+     */
+    @Override
+    public void toXML(Node parent) {
+        Document owner = parent.getOwnerDocument();
+        parent.appendChild(owner.createElement("settings"));
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/ExportSection.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,46 @@
+package org.dive4elements.river.exports;
+
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ExportSection extends TypeSection {
+
+    public static final String WIDTH_ATTR  = "width";
+    public static final String HEIGHT_ATTR = "height";
+
+
+    public ExportSection() {
+        super("export");
+    }
+
+
+    public void setWidth(int width) {
+        if (width <= 0) {
+            return;
+        }
+
+        setIntegerValue(WIDTH_ATTR, width);
+    }
+
+
+    public Integer getWidth() {
+        return getIntegerValue(WIDTH_ATTR);
+    }
+
+
+    public void setHeight(int height) {
+        if (height <= 0) {
+            return;
+        }
+
+        setIntegerValue(HEIGHT_ATTR, height);
+    }
+
+
+    public Integer getHeight() {
+        return getIntegerValue(HEIGHT_ATTR);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/FlowVelocityExporter.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,127 @@
+package org.dive4elements.river.exports;
+
+import java.io.OutputStream;
+import java.text.NumberFormat;
+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.FLYSArtifact;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.FlowVelocityData;
+import org.dive4elements.river.utils.FLYSUtils;
+import org.dive4elements.river.utils.Formatter;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class FlowVelocityExporter extends AbstractExporter {
+
+    private static final Logger logger =
+        Logger.getLogger(FlowVelocityExporter.class);
+
+
+    public static final String CSV_KM =
+        "export.flow_velocity.csv.header.km";
+
+    public static final String CSV_V_TOTAL =
+        "export.flow_velocity.csv.header.v_total";
+
+    public static final String CSV_V_MAIN =
+        "export.flow_velocity.csv.header.v_main";
+
+    public static final String CSV_TAU_MAIN =
+        "export.flow_velocity.csv.header.tau_main";
+
+    public static final String CSV_Q =
+        "export.flow_velocity.csv.header.q";
+
+    public static final String CSV_LOCATIONS =
+        "export.flow_velocity.csv.header.locations";
+
+
+    protected List<FlowVelocityData[]> data;
+
+
+    public void init(Document request, OutputStream out, CallContext cc) {
+        super.init(request, out, cc);
+        data = new ArrayList<FlowVelocityData[]>();
+    }
+
+
+    @Override
+    protected void addData(Object d) {
+        if (d instanceof CalculationResult) {
+            d = ((CalculationResult) d).getData();
+
+            if (d instanceof FlowVelocityData[]) {
+                logger.debug("Add new data of type FlowVelocityData");
+                data.add((FlowVelocityData[]) d);
+            }
+        }
+    }
+
+
+    @Override
+    protected void writeCSVData(CSVWriter writer) {
+        logger.info("FlowVelocityExporter.writeCSVData");
+        logger.debug("CSV gets " + data.size() + " FlowVelocityData objects.");
+
+        writeCSVHeader(writer);
+
+        for (FlowVelocityData[] d: data) {
+            data2CSV(writer, d);
+        }
+    }
+
+
+    protected void writeCSVHeader(CSVWriter writer) {
+        writer.writeNext(new String[] {
+            msg(CSV_KM, CSV_KM),
+            msg(CSV_V_TOTAL, CSV_V_TOTAL),
+            msg(CSV_V_MAIN, CSV_V_MAIN),
+            msg(CSV_TAU_MAIN, CSV_TAU_MAIN),
+            msg(CSV_Q, CSV_Q),
+            msg(CSV_LOCATIONS, CSV_LOCATIONS)
+        });
+    }
+
+
+    protected void data2CSV(CSVWriter writer, FlowVelocityData[] fData) {
+        logger.debug("Add next FlowVelocityData to CSV");
+
+        FLYSArtifact flys = (FLYSArtifact) master;
+
+        for (FlowVelocityData data: fData) {
+            for (int i = 0, n = data.size(); i < n; i++) {
+                NumberFormat kmF  = Formatter.getFlowVelocityKM(context);
+                NumberFormat valF = Formatter.getFlowVelocityValues(context);
+                NumberFormat qF   = Formatter.getFlowVelocityQ(context);
+
+                writer.writeNext(new String[] {
+                    kmF.format(data.getKM(i)),
+                    valF.format(data.getVMain(i)),
+                    valF.format(data.getVTotal(i)),
+                    valF.format(data.getTauMain(i)),
+                    qF.format(data.getQ(i)) + "=" + data.getZone(),
+                    FLYSUtils.getLocationDescription(flys, data.getKM(i)),
+                });
+            }
+        }
+    }
+
+
+    @Override
+    protected void writePDF(OutputStream out) {
+        logger.error("TODO: Implement FlowVelocityExporter.writePDF");
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/FlowVelocityGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,547 @@
+package org.dive4elements.river.exports;
+
+import java.util.Arrays;
+
+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.FLYSArtifact;
+
+import org.dive4elements.river.artifacts.access.FlowVelocityAccess;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.FlowVelocityData;
+import org.dive4elements.river.artifacts.model.minfo.BedDiameterResult;
+import org.dive4elements.river.artifacts.model.minfo.BedloadDiameterResult;
+import org.dive4elements.river.model.FlowVelocityMeasurementValue;
+
+import org.dive4elements.river.jfree.Bounds;
+import org.dive4elements.river.jfree.DoubleBounds;
+import org.dive4elements.river.jfree.FLYSAnnotation;
+import org.dive4elements.river.jfree.StyledXYSeries;
+
+import org.dive4elements.river.utils.FLYSUtils;
+
+
+/**
+ * An OutGenerator that generates flow velocity curves.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class FlowVelocityGenerator
+extends      XYChartGenerator
+implements   FacetTypes
+{
+    public enum YAXIS {
+        V(0),
+        T(1),
+        Q(2),
+        D(3);
+        /* TODO Q and Density will come as 4th and 3rd axis. */
+        protected int idx;
+        private YAXIS(int c) {
+           idx = c;
+        }
+    }
+
+    /** The logger that is used in this generator. */
+    private static Logger logger = Logger.getLogger(FlowVelocityGenerator.class);
+
+    /** Key to look up internationalized String for annotations label. */
+    public static final String I18N_ANNOTATIONS_LABEL =
+        "chart.flow_velocity.annotations.label";
+
+    /**
+     * Key to look up internationalized String for LongitudinalSection diagrams
+     * titles.
+     */
+    public static final String I18N_CHART_TITLE =
+        "chart.flow_velocity.section.title";
+
+    /**
+     * Key to look up internationalized String for LongitudinalSection diagrams
+     * subtitles.
+     */
+    public static final String I18N_CHART_SUBTITLE =
+        "chart.flow_velocity.section.subtitle";
+
+    /**
+     * Key to look up internationalized String for LongitudinalSection diagrams
+     * short subtitles.
+     */
+    public static final String I18N_CHART_SHORT_SUBTITLE =
+        "chart.flow_velocity.section.shortsubtitle";
+
+    public static final String I18N_XAXIS_LABEL =
+        "chart.flow_velocity.section.xaxis.label";
+
+    public static final String I18N_YAXIS_LABEL =
+        "chart.flow_velocity.section.yaxis.label";
+
+    public static final String I18N_2YAXIS_LABEL =
+        "chart.flow_velocity.section.yaxis.second.label";
+
+    public static final String I18N_3YAXIS_LABEL =
+        "chart.flow_velocity.section.yaxis.third.label";
+    public static final String I18N_4YAXIS_LABEL = "chart.bedquality.yaxis.label.diameter";
+
+    public static final String I18N_CHART_TITLE_DEFAULT  = "Geschwindigkeit- und Schubspannung";
+    public static final String I18N_XAXIS_LABEL_DEFAULT  = "km";
+    public static final String I18N_YAXIS_LABEL_DEFAULT  = "Geschwindigkeit v [m/s]";
+    public static final String I18N_2YAXIS_LABEL_DEFAULT = "Schubspannung Tau [N]";
+    public static final String I18N_3YAXIS_LABEL_DEFAULT = "Q [m³/s]";
+    public static final String I18N_4YAXIS_LABEL_DEFAULT = "Durchmesser [mm]";
+
+    @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() {
+        FLYSArtifact flys = (FLYSArtifact) master;
+
+        return msg(
+            I18N_XAXIS_LABEL,
+            I18N_XAXIS_LABEL_DEFAULT,
+            new Object[] { FLYSUtils.getRiver(flys).getName() });
+    }
+
+
+    @Override
+    protected String getDefaultYAxisLabel(int index) {
+        String label = "default";
+
+        if (index == YAXIS.V.idx) {
+            label = getVAxisLabel();
+        }
+        else if (index == YAXIS.T.idx) {
+            label = getTAxisLabel();
+        }
+        else if (index == YAXIS.Q.idx) {
+            label = getQAxisLabel();
+        }
+        else if (index == YAXIS.D.idx) {
+            label = getDAxisLabel();
+        }
+
+        return label;
+    }
+
+
+    /**
+     * Get internationalized label for the y axis.
+     */
+    protected String getVAxisLabel() {
+        return msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL_DEFAULT);
+    }
+
+
+    /**
+     * Get internationalized label for the y axis.
+     */
+    protected String getQAxisLabel() {
+        return msg(I18N_3YAXIS_LABEL, I18N_3YAXIS_LABEL_DEFAULT);
+    }
+
+    /**
+     * Get internationalized label for the y axis.
+     */
+    protected String getTAxisLabel() {
+        return msg(I18N_2YAXIS_LABEL, I18N_2YAXIS_LABEL_DEFAULT);
+    }
+
+    /**
+     * Get internationalized label for the y axis.
+     */
+    protected String getDAxisLabel() {
+        return msg(I18N_4YAXIS_LABEL, I18N_4YAXIS_LABEL_DEFAULT);
+    }
+
+    /**
+     * Produce output.
+     * @param artifactAndFacet current facet.
+     * @param attr  theme for facet
+     * @param visible Whether this facets data is actually visible or not.
+     */
+    public void doOut(
+        ArtifactAndFacet artifactAndFacet,
+        Document         attr,
+        boolean          visible
+    ) {
+        String name = artifactAndFacet.getFacetName();
+
+        logger.debug("FlowVelocityGenerator.doOut: " + name);
+
+        if (name == null) {
+            logger.error("No facet name for doOut(). No output generated!");
+            return;
+        }
+
+        Facet facet = artifactAndFacet.getFacet();
+
+        if (facet == null) {
+            return;
+        }
+
+        if (getXBounds(0) != null && getDomainAxisRange() != null) {
+            logger.debug(Arrays.toString(getDomainAxisRangeFromRequest()));
+            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) {
+            FLYSArtifact artifact = (FLYSArtifact)artifactAndFacet.getArtifact();
+            FlowVelocityAccess access = new FlowVelocityAccess(artifact, context);
+            context.putContextValue("startkm", access.getLowerKM());
+            context.putContextValue("endkm", access.getUpperKM());
+        }
+        else if (getXBounds(0) == null && getDomainAxisRange() != null){
+            FLYSArtifact artifact = (FLYSArtifact)artifactAndFacet.getArtifact();
+            FlowVelocityAccess access = new FlowVelocityAccess(artifact, context);
+            Bounds b = new DoubleBounds(access.getLowerKM(), access.getUpperKM());
+            Bounds bounds =
+                calculateZoom(b, getDomainAxisRange());
+            context.putContextValue("startkm", bounds.getLower());
+            context.putContextValue("endkm", bounds.getUpper());
+        }
+        if (name.equals(FLOW_VELOCITY_MAINCHANNEL)) {
+            doMainChannelOut(
+                (FlowVelocityData) artifactAndFacet.getData(context),
+                artifactAndFacet,
+                attr,
+                visible);
+        }
+        else if (name.equals(FLOW_VELOCITY_TOTALCHANNEL)) {
+            doTotalChannelOut(
+                (FlowVelocityData) artifactAndFacet.getData(context),
+                artifactAndFacet,
+                attr,
+                visible);
+        }
+        else if (name.equals(FLOW_VELOCITY_MAINCHANNEL_FILTERED)) {
+            doMainChannelOut(
+                (FlowVelocityData) artifactAndFacet.getData(context),
+                artifactAndFacet,
+                attr,
+                visible);
+        }
+        else if (name.equals(FLOW_VELOCITY_TOTALCHANNEL_FILTERED)) {
+            doTotalChannelOut(
+                (FlowVelocityData) artifactAndFacet.getData(context),
+                artifactAndFacet,
+                attr,
+                visible);
+        }
+        else if (name.equals(FLOW_VELOCITY_DISCHARGE)) {
+            doQOut(
+                (FlowVelocityData) artifactAndFacet.getData(context),
+                artifactAndFacet,
+                attr,
+                visible);
+        }
+        else if (name.equals(FLOW_VELOCITY_TAU)) {
+            doTauOut(
+                (FlowVelocityData) artifactAndFacet.getData(context),
+                artifactAndFacet,
+                attr,
+                visible);
+        }
+        else if (name.equals(FLOW_VELOCITY_TAU_FILTERED)) {
+            doTauOut(
+                (FlowVelocityData) artifactAndFacet.getData(context),
+                artifactAndFacet,
+                attr,
+                visible);
+        }
+
+        else if (name.equals(FLOW_VELOCITY_ANNOTATION)) {
+            doAnnotations(
+                (FLYSAnnotation) 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.V.idx);
+        }
+        else if (name.equals(LONGITUDINAL_ANNOTATION)) {
+            doAnnotations(
+                (FLYSAnnotation) artifactAndFacet.getData(context),
+                 artifactAndFacet,
+                 attr,
+                 visible);
+        }
+        else if (name.equals(FLOW_VELOCITY_MEASUREMENT)) {
+            doVPointOut(
+                 artifactAndFacet.getData(context),
+                 artifactAndFacet,
+                 attr,
+                 visible);
+        }
+        else if (name.equals(BED_QUALITY_BED_DIAMETER_SUBLAYER)) {
+            doBedQualitySubLayerOut(
+                (BedDiameterResult)artifactAndFacet.getData(context),
+                artifactAndFacet,
+                attr,
+                visible);
+        }
+        else if (name.equals(BED_QUALITY_BED_DIAMETER_TOPLAYER)) {
+            doBedQualityTopLayerOut(
+                (BedDiameterResult)artifactAndFacet.getData(context),
+                artifactAndFacet,
+                attr,
+                visible);
+        }
+        else if (name.equals(BED_QUALITY_BEDLOAD_DIAMETER)) {
+            doBedQualityLoadDiameter(
+                (BedloadDiameterResult)artifactAndFacet.getData(context),
+                artifactAndFacet,
+                attr,
+                visible);
+        }
+        else {
+            logger.warn("Unknown facet name: " + name);
+            return;
+        }
+    }
+
+
+    private void doBedQualityLoadDiameter(
+        BedloadDiameterResult data,
+        ArtifactAndFacet aandf,
+        Document attr,
+        boolean visible) {
+        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), attr);
+        StyledSeriesBuilder.addPoints(series, data.getDiameterData(), true);
+
+        addAxisSeries(series, YAXIS.D.idx, visible);
+    }
+
+
+    private void doBedQualityTopLayerOut(
+        BedDiameterResult data,
+        ArtifactAndFacet aandf,
+        Document attr,
+        boolean visible) {
+        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), attr);
+        StyledSeriesBuilder.addPoints(series, data.getDiameterSubData(), true);
+        addAxisSeries(series, YAXIS.D.idx, visible);
+    }
+
+
+    private void doBedQualitySubLayerOut(
+        BedDiameterResult data,
+        ArtifactAndFacet aandf,
+        Document attr,
+        boolean visible
+    ) {
+        logger.debug("Do beddiametersubout");
+        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), attr);
+        StyledSeriesBuilder.addPoints(series, data.getDiameterSubData(), true);
+        addAxisSeries(series, YAXIS.D.idx, visible);
+    }
+
+
+    /**
+     * Process the output for W facets in a longitudinal section curve.
+     *
+     * @param data A FlowVelocityData object
+     * @param aandf The facet. This facet does NOT support any data objects. Use
+     * FLYSArtifact.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 doMainChannelOut(
+        FlowVelocityData data,
+        ArtifactAndFacet aandf,
+        Document         theme,
+        boolean          visible
+    ) {
+        logger.debug("FlowVelocityGenerator.doMainChannelOut");
+
+        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
+
+        StyledSeriesBuilder.addPoints(series, data.getMainChannelPoints(), true);
+
+        addAxisSeries(series, YAXIS.V.idx, visible);
+    }
+
+
+    /** Handle VWQKms. */
+    protected void doVPointOut (
+        Object data,
+        ArtifactAndFacet aandf,
+        Document         theme,
+        boolean          visible
+    ) {
+        logger.debug("FlowVelocityGenerator.doVPointOut");
+
+        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
+
+        FlowVelocityMeasurementValue.FastFlowVelocityMeasurementValue
+            value = (FlowVelocityMeasurementValue.FastFlowVelocityMeasurementValue)
+                data;
+
+        StyledSeriesBuilder.addPoints(series, new double[][] {{value.getStation()},{value.getV()}}, true);
+
+        addAxisSeries(series, YAXIS.V.idx, visible);
+    }
+
+
+    /**
+     * Add items to dataseries which describes the differences.
+     */
+    protected void doTotalChannelOut(
+        FlowVelocityData data,
+        ArtifactAndFacet aandf,
+        Document         theme,
+        boolean          visible
+    ) {
+        logger.debug("FlowVelocityGenerator.doTotalChannelOut");
+
+        if (data == null) {
+            logger.warn("No data to add to FlowVelocity chart.");
+            return;
+         }
+
+        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
+
+        StyledSeriesBuilder.addPoints(series, data.getTotalChannelPoints(), true);
+
+        addAxisSeries(series, YAXIS.V.idx, visible);
+    }
+
+
+
+    /**
+     * @param data A FlowVelocityData object
+     * @param aandf The facet. This facet does NOT support any data objects. Use
+     * FLYSArtifact.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(
+        FlowVelocityData data,
+        ArtifactAndFacet aandf,
+        Document         theme,
+        boolean          visible
+    ) {
+        logger.debug("FlowVelocityGenerator.doTauOut");
+
+        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
+
+        StyledSeriesBuilder.addPoints(series, data.getQPoints(), true);
+
+        addAxisSeries(series, YAXIS.Q.idx, visible);
+    }
+
+    /**
+     * @param data A FlowVelocityData object
+     * @param aandf The facet. This facet does NOT support any data objects. Use
+     * FLYSArtifact.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 doTauOut(
+        FlowVelocityData data,
+        ArtifactAndFacet aandf,
+        Document         theme,
+        boolean          visible
+    ) {
+        logger.debug("FlowVelocityGenerator.doTauOut");
+
+        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
+
+        StyledSeriesBuilder.addPoints(series, data.getTauPoints(), true);
+
+        addAxisSeries(series, YAXIS.T.idx, visible);
+    }
+
+
+    /** Look up the axis identifier for a given facet type. */
+    public int axisIdxForFacet(String facetName) {
+        if (FacetTypes.IS.V(facetName)) {
+            return YAXIS.V.idx;
+        }
+        else if (FacetTypes.IS.T(facetName)) {
+            return YAXIS.T.idx;
+        }
+        else {
+            logger.warn("Could not find axis for facet " + facetName);
+            return YAXIS.V.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 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/FlowVelocityInfoGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,17 @@
+package org.dive4elements.river.exports;
+
+
+/**
+ * A ChartInfoGenerator that generates meta information for specific
+ * flow velocity curves.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class FlowVelocityInfoGenerator
+extends      ChartInfoGenerator
+{
+    public FlowVelocityInfoGenerator() {
+        super(new FlowVelocityGenerator());
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/HistoricalDischargeCurveExporter.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,250 @@
+package org.dive4elements.river.exports;
+
+import java.io.OutputStream;
+import java.text.NumberFormat;
+import java.text.DateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Locale;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+import au.com.bytecode.opencsv.CSVWriter;
+
+import net.sf.jasperreports.engine.JasperExportManager;
+import net.sf.jasperreports.engine.JasperFillManager;
+import net.sf.jasperreports.engine.JasperPrint;
+import net.sf.jasperreports.engine.JRException;
+
+import org.dive4elements.artifacts.common.utils.Config;
+
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.HistoricalDischargeData;
+import org.dive4elements.river.artifacts.model.Timerange;
+import org.dive4elements.river.artifacts.model.WQTimerange;
+import org.dive4elements.river.artifacts.model.WQTJRDataSource;
+import org.dive4elements.river.artifacts.resources.Resources;
+import org.dive4elements.river.utils.FLYSUtils;
+import org.dive4elements.river.utils.Formatter;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class HistoricalDischargeCurveExporter extends AbstractExporter {
+
+    private static final Logger logger =
+        Logger.getLogger(HistoricalDischargeCurveExporter.class);
+
+
+    public static final String CSV_TIMERANGE_HEADER =
+        "export.historical.discharge.csv.header.timerange";
+
+    public static final String CSV_WATERLEVEL_HEADER =
+        "export.historical.discharge.csv.header.waterlevel";
+
+    public static final String CSV_DISCHARGE_HEADER =
+        "export.historical.discharge.csv.header.discharge";
+
+    public static final String CSV_DIFF_HEADER =
+        "export.historical.discharge.csv.header.diff";
+
+    public static final String CSV_GAUGENAME_HEADER =
+        "export.historical.discharge.csv.header.gaugename";
+
+    public static final String PDF_HEADER_MODE =
+        "export.historical.discharge.pdf.mode";
+
+    public static final String JASPER_FILE =
+        "export.historical.discharge.pdf.file";
+
+    protected List<WQTimerange[]> data;
+
+
+    public void init(Document request, OutputStream out, CallContext cc) {
+        super.init(request, out, cc);
+
+        data = new ArrayList<WQTimerange[]>();
+    }
+
+
+    @Override
+    protected void addData(Object d) {
+        logger.debug("Add data of class: " + d.getClass());
+
+        if (d instanceof CalculationResult) {
+            d = ((CalculationResult) d).getData();
+
+            logger.debug("Internal data of CalculationResult: " + d.getClass());
+
+            if (d instanceof HistoricalDischargeData) {
+                d = (WQTimerange[]) ((HistoricalDischargeData) d).getWQTimeranges();
+
+                if (d instanceof WQTimerange[]) {
+                    logger.debug("Add new data of type WQTimerange");
+                    data.add((WQTimerange[]) d);
+                }
+            }
+        }
+    }
+
+
+    @Override
+    protected void writeCSVData(CSVWriter writer) {
+        logger.info("HistoricalDischargeCurveExporter.writeCSVData");
+        logger.debug("CSV gets " + data.size() + " WQTimerange[] objects.");
+
+        writeCSVHeader(writer);
+
+        for (WQTimerange[] arr: data) {
+            for (WQTimerange wqt: arr) {
+                wqt2CSV(writer, wqt);
+            }
+        }
+    }
+
+
+    @Override
+    protected void writePDF(OutputStream out) {
+        WQTJRDataSource source = createJRData();
+
+        String jasperFile = Resources.getMsg(
+                                context.getMeta(),
+                                JASPER_FILE,
+                                "/jasper/historical-discharge_en.jasper");
+        String confPath = Config.getConfigDirectory().toString();
+
+        Map parameters = new HashMap();
+        parameters.put("ReportTitle", "Exported Data");
+        try {
+            JasperPrint print = JasperFillManager.fillReport(
+                confPath + jasperFile,
+                parameters,
+                source);
+            JasperExportManager.exportReportToPdfStream(print, out);
+        }
+        catch(JRException je) {
+            logger.warn("Error generating PDF Report!");
+            je.printStackTrace();
+        }
+    }
+
+
+    protected void writeCSVHeader(CSVWriter writer) {
+        writer.writeNext(new String[] {
+            msg(CSV_TIMERANGE_HEADER, CSV_TIMERANGE_HEADER),
+            msg(CSV_WATERLEVEL_HEADER, CSV_WATERLEVEL_HEADER),
+            msg(CSV_DISCHARGE_HEADER, CSV_DISCHARGE_HEADER),
+            msg(CSV_DIFF_HEADER, CSV_DIFF_HEADER),
+            msg(CSV_GAUGENAME_HEADER, CSV_GAUGENAME_HEADER)
+        });
+    }
+
+
+    protected void wqt2CSV(CSVWriter writer, WQTimerange wqt) {
+        logger.debug("Add next WQTimerange to CSV");
+
+        DateFormat   df = Formatter.getMediumDateFormat(context);
+        NumberFormat wf = Formatter.getHistoricalDischargeW(context);
+        NumberFormat qf = Formatter.getHistoricalDischargeQ(context);
+
+        double[] wq = new double[3];
+
+        String gaugeName = getReferenceGaugename();
+
+        List<WQTimerange.TimerangeItem> sorted = wqt.sort();
+
+        for (int i = 0, n = sorted.size(); i < n; i++) {
+            WQTimerange.TimerangeItem item = sorted.get(i);
+
+            Timerange tr = item.timerange;
+            Date   start = new Date(tr.getStart());
+            Date     end = new Date(tr.getEnd());
+
+            item.get(wq);
+
+            writer.writeNext(new String[] {
+                df.format(start) + " - " + df.format(end),
+                wf.format(wq[0]),
+                qf.format(wq[1]),
+                qf.format(wq[2]),
+                gaugeName
+            });
+        }
+    }
+
+
+    protected WQTJRDataSource createJRData() {
+        WQTJRDataSource source = new WQTJRDataSource();
+
+        addMetaData(source);
+        for (WQTimerange[] arr: data) {
+            for (WQTimerange wqt: arr) {
+                addWQTData(source, wqt);
+            }
+        }
+
+        return source;
+    }
+
+
+    protected void addMetaData(WQTJRDataSource source) {
+        CallMeta meta = context.getMeta();
+
+        FLYSArtifact flys = (FLYSArtifact) master;
+
+        source.addMetaData ("river", FLYSUtils.getRivername(flys));
+
+        Locale locale = Resources.getLocale(meta);
+        DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, locale);
+
+        source.addMetaData("date", df.format(new Date()));
+
+        source.addMetaData("calculation", Resources.getMsg(
+                                            locale,
+                                            PDF_HEADER_MODE,
+                                            "Historical Discharge"));
+    }
+
+
+    protected void addWQTData(WQTJRDataSource source, WQTimerange wqt) {
+        DateFormat   df = Formatter.getShortDateFormat(context);
+        NumberFormat wf = Formatter.getHistoricalDischargeW(context);
+        NumberFormat qf = Formatter.getHistoricalDischargeQ(context);
+
+        double[] wq = new double[3];
+
+        String gaugeName = getReferenceGaugename();
+
+        for (int i = 0, n = wqt.size(); i < n; i++) {
+            Timerange tr = wqt.getTimerange(i);
+            Date   start = new Date(tr.getStart());
+            Date     end = new Date(tr.getEnd());
+
+            wqt.get(i, wq);
+
+            source.addData(new String[] {
+                df.format(start) + " - " + df.format(end),
+                wf.format(wq[0]),
+                qf.format(wq[1]),
+                qf.format(wq[2]),
+                gaugeName
+            });
+        }
+    }
+
+
+    public String getReferenceGaugename() {
+        return FLYSUtils.getReferenceGaugeName((FLYSArtifact) master);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/HistoricalDischargeCurveGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,258 @@
+package org.dive4elements.river.exports;
+
+import java.util.Date;
+
+import org.apache.log4j.Logger;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.data.general.SeriesException;
+import org.jfree.data.time.Day;
+import org.jfree.data.time.RegularTimePeriod;
+import org.jfree.data.time.TimeSeries;
+import org.jfree.data.time.TimeSeriesCollection;
+import org.w3c.dom.Document;
+
+import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.access.HistoricalDischargeAccess;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.HistoricalWQTimerange;
+import org.dive4elements.river.artifacts.model.Timerange;
+import org.dive4elements.river.artifacts.model.WQTimerange;
+import org.dive4elements.river.jfree.StyledTimeSeries;
+import org.dive4elements.river.utils.FLYSUtils;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class HistoricalDischargeCurveGenerator extends TimeseriesChartGenerator
+    implements FacetTypes {
+
+    private static Logger logger = Logger
+        .getLogger(HistoricalDischargeCurveGenerator.class);
+
+    public static final String I18N_CHART_TITLE = "chart.historical.discharge.title";
+
+    public static final String I18N_CHART_SUBTITLE = "chart.historical.discharge.subtitle";
+
+    public static final String I18N_XAXIS_LABEL = "chart.historical.discharge.xaxis.label";
+
+    public static final String I18N_YAXIS_LABEL = "chart.historical.discharge.yaxis.label";
+
+    public static final String I18N_YAXIS_SECOND_LABEL = "chart.historical.discharge.yaxis.second.label";
+
+    public static enum YAXIS {
+        W(0), Q(1);
+
+        protected int idx;
+
+        private YAXIS(int c) {
+            idx = c;
+        }
+    }
+
+    @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
+    protected String getDefaultChartTitle() {
+        return msg(I18N_CHART_TITLE, I18N_CHART_TITLE);
+    }
+
+    @Override
+    protected String getDefaultChartSubtitle() {
+        FLYSArtifact flys = (FLYSArtifact) master;
+        Timerange evalTime = new HistoricalDischargeAccess(flys)
+            .getEvaluationTimerange();
+
+        Object[] args = new Object[] { FLYSUtils.getReferenceGaugeName(flys),
+            evalTime.getStart(), evalTime.getEnd() };
+
+        return msg(I18N_CHART_SUBTITLE, "", args);
+    }
+
+    @Override
+    protected String getDefaultXAxisLabel() {
+        return msg(I18N_XAXIS_LABEL, I18N_XAXIS_LABEL);
+    }
+
+    @Override
+    protected String getDefaultYAxisLabel(int pos) {
+        if (pos == 0) {
+            return msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL);
+        }
+        else if (pos == 1) {
+            return msg(I18N_YAXIS_SECOND_LABEL, I18N_YAXIS_SECOND_LABEL);
+        }
+        else {
+            return "NO TITLE FOR Y AXIS: " + pos;
+        }
+    }
+
+    @Override
+    protected void adjustPlot(XYPlot plot) {
+        super.adjustPlot(plot);
+        plot.setRangeZeroBaselineVisible(true);
+    }
+
+    @Override
+    public void doOut(ArtifactAndFacet artifactFacet, Document theme,
+        boolean visible) {
+        String name = artifactFacet.getFacetName();
+        logger.debug("HistoricalDischargeCurveGenerator.doOut: " + name);
+        logger.debug("Theme description is: "
+            + artifactFacet.getFacetDescription());
+
+        if (name.equals(HISTORICAL_DISCHARGE_Q)) {
+            doHistoricalDischargeOutQ(
+                (FLYSArtifact) artifactFacet.getArtifact(),
+                artifactFacet.getData(context),
+                artifactFacet.getFacetDescription(), theme, visible);
+        }
+        else if (name.equals(HISTORICAL_DISCHARGE_W)) {
+            doHistoricalDischargeOutW(
+                (FLYSArtifact) artifactFacet.getArtifact(),
+                artifactFacet.getData(context),
+                artifactFacet.getFacetDescription(), theme, visible);
+        }
+        else if (name.equals(HISTORICAL_DISCHARGE_Q_DIFF)) {
+            doHistoricalDischargeDifferenceOutQ(
+                (FLYSArtifact) artifactFacet.getArtifact(),
+                artifactFacet.getData(context),
+                artifactFacet.getFacetDescription(), theme, visible);
+        }
+        else if (name.equals(HISTORICAL_DISCHARGE_W_DIFF)) {
+            doHistoricalDischargeDifferenceOutW(
+                (FLYSArtifact) artifactFacet.getArtifact(),
+                artifactFacet.getData(context),
+                artifactFacet.getFacetDescription(), theme, visible);
+        }
+        else if (FacetTypes.IS.MANUALPOINTS(name)) {
+            doPoints(artifactFacet.getData(context), artifactFacet, theme,
+                visible, YAXIS.Q.idx);
+        }
+        else {
+            logger.warn("doOut(): unknown facet name: " + name);
+            return;
+        }
+    }
+
+    protected void doHistoricalDischargeOutQ(FLYSArtifact artifact,
+        Object data, String desc, Document theme, boolean visible) {
+        logger.debug("doHistoricalDischargeOut(): description = " + desc);
+
+        WQTimerange wqt = (WQTimerange) data;
+
+        TimeSeriesCollection tsc = newTimeSeriesCollection(wqt.getTimeranges(),
+            wqt.getQs(), theme, desc);
+
+        addAxisDataset(tsc, YAXIS.Q.idx, visible);
+    }
+
+    protected void doHistoricalDischargeOutW(FLYSArtifact artifact,
+        Object data, String desc, Document theme, boolean visible) {
+        logger.debug("doHistoricalDischargeOut(): description = " + desc);
+
+        WQTimerange wqt = (WQTimerange) data;
+
+        TimeSeriesCollection tsc = newTimeSeriesCollection(wqt.getTimeranges(),
+            wqt.getWs(), theme, desc);
+
+        addAxisDataset(tsc, YAXIS.W.idx, visible);
+    }
+
+    protected void doHistoricalDischargeDifferenceOutQ(FLYSArtifact artifact,
+        Object data, String desc, Document theme, boolean visible) {
+        logger.debug("doHistoricalDischargeDifferenceOut: desc = " + desc);
+
+        HistoricalWQTimerange wqt = (HistoricalWQTimerange) data;
+
+        TimeSeriesCollection tsc = newTimeSeriesCollection(wqt.getTimeranges(),
+            wqt.getDiffs(), theme, desc);
+
+        addAxisDataset(tsc, YAXIS.Q.idx, visible);
+    }
+
+    protected void doHistoricalDischargeDifferenceOutW(FLYSArtifact artifact,
+        Object data, String desc, Document theme, boolean visible) {
+        logger.debug("doHistoricalDischargeDifferenceOut: desc = " + desc);
+
+        HistoricalWQTimerange wqt = (HistoricalWQTimerange) data;
+
+        TimeSeriesCollection tsc = newTimeSeriesCollection(wqt.getTimeranges(),
+            wqt.getDiffs(), theme, desc);
+
+        addAxisDataset(tsc, YAXIS.W.idx, visible);
+    }
+
+    /**
+     * Creates a new TimeSeriesCollection with a single TimeSeries. The
+     * TimeSeries will consist of two RegularTimePeriods for each W/Q value
+     * provided by <i>wqt</i>. This has the effect, that the line in the chart
+     * looks like a "step chart".
+     */
+    protected TimeSeriesCollection newTimeSeriesCollection(
+        Timerange[] timeranges, double[] values, Document theme, String desc) {
+        logger.debug("Create new TimeSeriesCollection for: " + desc);
+
+        TimeSeriesCollection tsc = new TimeSeriesCollection();
+        TimeSeries series = new StyledTimeSeries(desc, theme);
+
+        for (int i = 0, n = timeranges.length; i < n; i++) {
+            RegularTimePeriod[] rtp = newRegularTimePeriod(timeranges[i]);
+
+            try {
+                if (Double.isNaN(values[i])) {
+                    logger.warn("Skip TimePeriod because value is NaN.");
+                    continue;
+                }
+
+                series.add(rtp[0], values[i]);
+                series.add(rtp[1], values[i]);
+
+                if (logger.isDebugEnabled()) {
+                    logger.debug("added Item to TimeSeries:");
+                    logger.debug("   TimePeriod: " + rtp[0] + " - " + rtp[1]);
+                    logger.debug("   Value:      " + values[i]);
+                }
+            }
+            catch (SeriesException se) {
+                logger.warn("Error while adding TimePeriod: " + se);
+            }
+        }
+
+        tsc.addSeries(series);
+
+        return tsc;
+    }
+
+    /**
+     * Creates an array that consists of two <i>Minute</i> periods [start, end].
+     *
+     * @param timerange
+     *            Supports start and end time.
+     *
+     * @return an array with two <i>Minute</i> periods [start, end].
+     */
+    protected RegularTimePeriod[] newRegularTimePeriod(Timerange timerange) {
+        Date start = new Date(timerange.getStart());
+        Date end = new Date(timerange.getEnd() - 1000 * 60 * 60 * 24);
+
+        return new RegularTimePeriod[] { new Day(start), new Day(end) };
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/HistoricalDischargeCurveInfoGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,17 @@
+package org.dive4elements.river.exports;
+
+
+/**
+ * A ChartInfoGenerator that generates meta information for specific
+ * historical discharge curves.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class HistoricalDischargeCurveInfoGenerator
+extends      ChartInfoGenerator
+{
+    public HistoricalDischargeCurveInfoGenerator() {
+        super(new HistoricalDischargeCurveGenerator());
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/HistoricalDischargeWQCurveGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,163 @@
+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.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.access.HistoricalDischargeAccess;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.Timerange;
+import org.dive4elements.river.artifacts.model.WQKms;
+
+import org.dive4elements.river.jfree.FLYSAnnotation;
+import org.dive4elements.river.jfree.StyledValueMarker;
+import org.dive4elements.river.jfree.StyledXYSeries;
+import org.dive4elements.river.utils.FLYSUtils;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class HistoricalDischargeWQCurveGenerator
+extends      XYChartGenerator
+implements   FacetTypes {
+
+    /** Private logger. */
+    private static Logger logger = Logger
+        .getLogger(HistoricalDischargeWQCurveGenerator.class);
+
+    public static final String I18N_CHART_TITLE = "chart.historical.discharge.wq.title";
+
+    public static final String I18N_CHART_SUBTITLE = "chart.historical.discharge.wq.subtitle";
+
+    public static final String I18N_XAXIS_LABEL = "chart.historical.discharge.wq.xaxis.label";
+
+    public static final String I18N_YAXIS_LABEL = "chart.historical.discharge.wq.yaxis.label";
+
+    /** One Y-Axis only, in this chart. */
+    public static enum YAXIS {
+        W(0);
+
+        protected int idx;
+
+        private YAXIS(int c) {
+            idx = c;
+        }
+    }
+
+    @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
+    protected String getDefaultChartTitle() {
+        return msg(I18N_CHART_TITLE, I18N_CHART_TITLE);
+    }
+
+    @Override
+    protected String getDefaultChartSubtitle() {
+        FLYSArtifact flys = (FLYSArtifact) master;
+        Timerange evalTime = new HistoricalDischargeAccess(flys)
+            .getEvaluationTimerange();
+
+        Object[] args = new Object[] { FLYSUtils.getReferenceGaugeName(flys),
+            evalTime.getStart(), evalTime.getEnd() };
+
+        return msg(I18N_CHART_SUBTITLE, "", args);
+    }
+
+    @Override
+    protected String getDefaultXAxisLabel() {
+        return msg(I18N_XAXIS_LABEL, I18N_XAXIS_LABEL);
+    }
+
+    @Override
+    protected String getDefaultYAxisLabel(int pos) {
+        if (pos == 0) {
+            return msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL);
+        }
+        else {
+            return "NO TITLE FOR Y AXIS: " + pos;
+        }
+    }
+
+    @Override
+    public void doOut(ArtifactAndFacet artifactFacet, Document theme,
+        boolean visible) {
+        String name = artifactFacet.getFacetName();
+        logger.debug("HistoricalDischargeWQCurveGenerator.doOut: " + name);
+        logger.debug("Theme description is: "
+            + artifactFacet.getFacetDescription());
+
+        if (name.equals(HISTORICAL_DISCHARGE_WQ_Q)) {
+            doHistoricalDischargeOutQ(
+                (FLYSArtifact) artifactFacet.getArtifact(),
+                artifactFacet.getData(context),
+                artifactFacet.getFacetDescription(), theme, visible);
+        }
+        else if (name.equals(HISTORICAL_DISCHARGE_WQ_W)) {
+            doHistoricalDischargeOutW(
+                (FLYSArtifact) artifactFacet.getArtifact(),
+                artifactFacet.getData(context),
+                artifactFacet.getFacetDescription(), theme, visible);
+        }
+        else if (name.equals(HISTORICAL_DISCHARGE_WQ_CURVE)) {
+            doHistoricalDischargeCurveOut(
+                (FLYSArtifact) artifactFacet.getArtifact(),
+                artifactFacet.getData(context),
+                artifactFacet.getFacetDescription(), theme, visible);
+        }
+        else if (FacetTypes.IS.MANUALPOINTS(name)) {
+            doPoints(artifactFacet.getData(context), artifactFacet, theme,
+                visible, YAXIS.W.idx);
+        }
+        else if (HISTORICAL_DISCHARGE_MAINVALUES_Q.equals(name)) {
+            doAnnotations((FLYSAnnotation)
+                artifactFacet.getData(context), artifactFacet, theme, visible);
+        }
+        else if (HISTORICAL_DISCHARGE_MAINVALUES_W.equals(name)) {
+            doAnnotations((FLYSAnnotation)
+                artifactFacet.getData(context), artifactFacet, theme, visible);
+        }
+        else {
+            logger.warn("doOut(): unknown facet name: " + name);
+            return;
+        }
+    }
+
+    protected void doHistoricalDischargeOutQ(FLYSArtifact artifact,
+        Object data, String desc, Document theme, boolean visible) {
+        double value = Double.valueOf(data.toString());
+        addDomainMarker(new StyledValueMarker(value, theme), visible);
+    }
+
+    protected void doHistoricalDischargeOutW(FLYSArtifact artifact,
+        Object data, String desc, Document theme, boolean visible) {
+        double value = Double.valueOf(data.toString());
+        addValueMarker(new StyledValueMarker(value, theme), visible);
+    }
+
+    protected void doHistoricalDischargeCurveOut(FLYSArtifact artifact,
+        Object data, String desc, Document theme, boolean visible) {
+        XYSeries series = new StyledXYSeries(desc, theme);
+        StyledSeriesBuilder.addPointsQW(series, (WQKms) data);
+
+        addAxisSeries(series, YAXIS.W.idx, visible);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/HistoricalDischargeWQCurveInfoGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,17 @@
+package org.dive4elements.river.exports;
+
+
+/**
+ * A ChartInfoGenerator that generates meta information for specific
+ * historical discharge curves.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class HistoricalDischargeWQCurveInfoGenerator
+extends      ChartInfoGenerator
+{
+    public HistoricalDischargeWQCurveInfoGenerator() {
+        super(new HistoricalDischargeWQCurveGenerator());
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/IdentifiableNumberAxis.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,22 @@
+package org.dive4elements.river.exports;
+
+import org.jfree.chart.axis.NumberAxis;
+
+
+public class IdentifiableNumberAxis extends NumberAxis {
+
+
+    protected String key;
+
+
+    protected IdentifiableNumberAxis(String key, String label) {
+        super(label);
+        this.key = key;
+    }
+
+
+    public String getId() {
+        return key;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/InfoGeneratorHelper.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,405 @@
+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.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class InfoGeneratorHelper {
+
+    /** Private logging instance. */
+    private static final Logger logger =
+        Logger.getLogger(InfoGeneratorHelper.class);
+
+    protected ChartGenerator generator;
+
+
+    public InfoGeneratorHelper(ChartGenerator 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/flys-artifacts/src/main/java/org/dive4elements/river/exports/IntegerAttribute.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,34 @@
+package org.dive4elements.river.exports;
+
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class IntegerAttribute extends VisibleAttribute {
+
+
+    public IntegerAttribute(String name, int value, boolean visible) {
+        super(name, value, visible);
+    }
+
+
+    /**
+     * Calls VisibleAttribute.toXML() and appends afterwards an attribute
+     * <i>type</i> with value <i>integer</i>.
+     *
+     * @param parent The parent Node.
+     *
+     * @return the new Node that represents this Attribute.
+     */
+    @Override
+    public Node toXML(Node parent) {
+        Element ele = (Element) super.toXML(parent);
+        ele.setAttribute("type", "integer");
+
+        return ele;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/LegendProcessor.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,143 @@
+package org.dive4elements.river.exports;
+
+import java.awt.geom.Line2D;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+
+import org.jfree.chart.LegendItem;
+import org.jfree.chart.LegendItemCollection;
+import org.jfree.chart.plot.XYPlot;
+
+
+/** Class to process Plots legends. */
+public abstract class LegendProcessor {
+
+    /** (Empty) shape for aggregated Legend Items. */
+    private static final Line2D.Double SPACE = new Line2D.Double(0,0,0,0);
+
+
+    /** Prevent instantiations. */
+    private LegendProcessor() {
+    }
+
+
+    /**
+     * 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.
+     */
+    protected static String legendItemHash(LegendItem li) {
+        // TODO Do proper implementation. Ensure that only mergable sets are created.
+        // getFillPaint()
+        // getFillPaintTransformer()
+        // getLabel()
+        // getLine()
+        // getLinePaint()
+        // getLineStroke()
+        // getOutminePaint()
+        // getOutlineStroke()
+        // Shape getShape()
+        // String getToolTipText()
+        // String getURLText()
+        // boolean isLineVisible()
+        // boolean isShapeFilled()
+        // boolean isShapeOutlineVisible()
+        // boolean isShapeVisible()
+        String hash = li.getLinePaint().toString();
+        // XXX: DEAD CODE // 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;
+    }
+
+
+    /**
+     * Create new legend entries, dependent on settings.
+     * @param plot The plot for which to modify the legend.
+     * @param threshold How many items are needed for aggregation to
+     *                  be triggered?
+     */
+    public static void aggregateLegendEntries(XYPlot plot, int threshold) {
+        LegendItemCollection old = plot.getLegendItems();
+        // Find "similar" entries if aggregation is enabled.
+
+        int maxListSize = 0;
+        int AGGR_THRESHOLD = threshold;
+
+        if (AGGR_THRESHOLD > old.getItemCount() || AGGR_THRESHOLD <= 0){
+            return;
+        }
+
+        HashMap<String, List<LegendItem>> entries = new LinkedHashMap<String, List<LegendItem>>();
+        for (Iterator<LegendItem> i = old.iterator(); i.hasNext();) {
+            LegendItem item = i.next();
+            String hash = legendItemHash(item);
+            List<LegendItem> itemList = entries.get(hash);
+            if (itemList == null) {
+                itemList = new ArrayList<LegendItem>();
+                entries.put(hash, itemList);
+            }
+            itemList.add(item);
+
+            if (itemList.size() > maxListSize) {
+                maxListSize = itemList.size();
+            }
+        }
+
+        if (maxListSize < AGGR_THRESHOLD) {
+            // No need to do anything.
+            return;
+        }
+
+        // Run over collected entries, merge their names and create new
+        // entry if needed.
+        LegendItemCollection newLegend = new LegendItemCollection();
+        for (List<LegendItem> itemList: entries.values()) {
+            if (itemList.size() >= AGGR_THRESHOLD) {
+                // Now do merging.
+                // XXX: DEAD CODE // LegendItem item = itemList.get(0);
+                // Unfortunately we cannot clone and just setDescription, as this
+                // method was added in JFreeChart 1.0.14 (we are at .13).
+
+                // Remove the shapes of all but the first items,
+                // to prevent "overfill" of legenditemblock.
+                for (int i = 0; i < itemList.size(); i++) {
+                    if (i != 0) {
+                        LegendItem litem = itemList.get(i);
+
+                        // Make shape and line really small.
+                        LegendItem merged = new LegendItem(
+                            "," + litem.getLabel(), litem.getDescription(), litem.getToolTipText(),
+                            litem.getURLText(), false, SPACE,
+                            false, litem.getFillPaint(), false,
+                            litem.getOutlinePaint(), litem.getOutlineStroke(), false,
+                            SPACE, litem.getLineStroke(), litem.getLinePaint());
+                        newLegend.add(merged);
+                    }
+                    else {
+                        newLegend.add(itemList.get(i));
+                    }
+                }
+            }
+            else {
+                // Do not merge entries.
+                for (LegendItem li: itemList) {
+                    newLegend.add(li);
+                }
+            }
+        }
+
+        plot.setFixedLegendItems (newLegend);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/LegendSection.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,54 @@
+package org.dive4elements.river.exports;
+
+
+/**
+ * Settings regarding legend of chart.
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class LegendSection extends TypeSection {
+
+    public static final String VISIBILITY_ATTR  = "visibility";
+    public static final String FONTSIZE_ATTR    = "font-size";
+    public static final String AGGREGATION_ATTR = "aggregation-threshold";
+
+
+    public LegendSection() {
+        super("legend");
+    }
+
+
+    /** Register font size attribute and value. */
+    public void setFontSize(int fontSize) {
+        if (fontSize <= 0) {
+            return;
+        }
+
+        setIntegerValue(FONTSIZE_ATTR, fontSize);
+    }
+
+
+    public Integer getFontSize() {
+        return getIntegerValue(FONTSIZE_ATTR);
+    }
+
+
+    public Integer getAggregationThreshold() {
+        return getIntegerValue(AGGREGATION_ATTR);
+    }
+
+
+    public void setAggregationThreshold(int aggregationThreshold) {
+        setIntegerValue(AGGREGATION_ATTR, Math.abs(aggregationThreshold));
+    }
+
+
+    public void setVisibility(boolean visibility) {
+        setBooleanValue(VISIBILITY_ATTR, visibility);
+    }
+
+
+    public Boolean getVisibility() {
+        return getBooleanValue(VISIBILITY_ATTR);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/LongitudinalSectionGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,604 @@
+package org.dive4elements.river.exports;
+
+import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.geom.Lines;
+import org.dive4elements.river.artifacts.model.AreaFacet;
+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.WOutProcessor;
+import org.dive4elements.river.jfree.FLYSAnnotation;
+import org.dive4elements.river.jfree.StyledAreaSeriesCollection;
+import org.dive4elements.river.jfree.StyledXYSeries;
+import org.dive4elements.river.utils.DataUtil;
+import org.dive4elements.river.utils.FLYSUtils;
+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;
+
+
+/**
+ * An OutGenerator that generates longitudinal section curves.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class LongitudinalSectionGenerator
+extends      XYChartGenerator
+implements   FacetTypes
+{
+    public enum YAXIS {
+        W(0),
+        D(1),
+        Q(2);
+        protected int idx;
+        private YAXIS(int c) {
+            idx = c;
+        }
+    }
+
+    /** The logger that is used in this generator. */
+    private static Logger logger =
+        Logger.getLogger(LongitudinalSectionGenerator.class);
+
+    /** Key to look up internationalized String for annotations label. */
+    public static final String I18N_ANNOTATIONS_LABEL =
+        "chart.longitudinal.annotations.label";
+
+    /**
+     * Key to look up internationalized String for LongitudinalSection diagrams
+     * titles.
+     */
+    public static final String I18N_CHART_TITLE =
+        "chart.longitudinal.section.title";
+
+    /**
+     * Key to look up internationalized String for LongitudinalSection diagrams
+     * subtitles.
+     */
+    public static final String I18N_CHART_SUBTITLE =
+        "chart.longitudinal.section.subtitle";
+
+    /**
+     * Key to look up internationalized String for LongitudinalSection diagrams
+     * short subtitles.
+     */
+    public static final String I18N_CHART_SHORT_SUBTITLE =
+        "chart.longitudinal.section.shortsubtitle";
+
+    public static final String I18N_XAXIS_LABEL =
+        "chart.longitudinal.section.xaxis.label";
+
+    public static final String I18N_YAXIS_LABEL =
+        "chart.longitudinal.section.yaxis.label";
+
+    public static final String I18N_2YAXIS_LABEL =
+        "chart.longitudinal.section.yaxis.second.label";
+
+    public static final String I18N_CHART_TITLE_DEFAULT  = "W-L\u00e4ngsschnitt";
+    public static final String I18N_XAXIS_LABEL_DEFAULT  = "km";
+    public static final String I18N_YAXIS_LABEL_DEFAULT  = "W [NN + m]";
+    public static final String I18N_2YAXIS_LABEL_DEFAULT = "Q [m\u00b3/s]";
+
+    public final static String I18N_WDIFF_YAXIS_LABEL =
+        "chart.w_differences.yaxis.label";
+
+    public final static String I18N_WDIFF_YAXIS_LABEL_DEFAULT = "m";
+
+    public LongitudinalSectionGenerator() {
+        super();
+    }
+
+
+    @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();
+            }
+        };
+    }
+
+
+    /**
+     * Return left most data points x value (on first axis).
+     * Overridden because axis could be inverted.
+     */
+    @Override
+    protected double getLeftX() {
+        if (isInverted()) {
+            return (Double)getXBounds(0).getUpper();
+        }
+        return (Double)getXBounds(0).getLower();
+    }
+
+
+    /**
+     * Return right most data points x value (on first axis).
+     * Overridden because axis could be inverted.
+     */
+    @Override
+    protected double getRightX() {
+        if (isInverted()) {
+            return (Double)getXBounds(0).getLower();
+        }
+        return (Double)getXBounds(0).getUpper();
+    }
+
+
+    /**
+     * 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);
+    }
+
+
+    /**
+     * Returns the default subtitle for this chart.
+     *
+     * @return the default subtitle for this chart.
+     */
+    @Override
+    protected String getDefaultChartSubtitle() {
+        double[] dist = getRange();
+
+        Object[] args = null;
+        if (dist == null) {
+            args = new Object[] {getRiverName()};
+            return msg(getChartShortSubtitleKey(), "", args);
+        }
+        args = new Object[] {
+            getRiverName(),
+            dist[0],
+            dist[1]
+        };
+        return msg(getChartSubtitleKey(), "", args);
+    }
+
+
+    /**
+     * Gets key to look up internationalized String for the charts subtitle.
+     * @return key to look up translated subtitle.
+     */
+    protected String getChartSubtitleKey() {
+        return I18N_CHART_SUBTITLE;
+    }
+
+
+    /**
+     * Gets key to look up internationalized String for the charts short
+     * subtitle.
+     * @return key to look up translated subtitle.
+     */
+    protected String getChartShortSubtitleKey() {
+        return I18N_CHART_SHORT_SUBTITLE;
+    }
+
+
+    /**
+     * Get internationalized label for the x axis.
+     */
+    @Override
+    protected String getDefaultXAxisLabel() {
+        FLYSArtifact flys = (FLYSArtifact) master;
+
+        return msg(
+            I18N_XAXIS_LABEL,
+            I18N_XAXIS_LABEL_DEFAULT,
+            new Object[] { FLYSUtils.getRiver(flys).getName() });
+    }
+
+
+    @Override
+    protected String getDefaultYAxisLabel(int index) {
+        String label = "default";
+
+        if (index == YAXIS.W.idx) {
+            label = getWAxisLabel();
+        }
+        else if (index == YAXIS.Q.idx) {
+            label = msg(getQAxisLabelKey(), getQAxisDefaultLabel());
+        }
+        else if (index == YAXIS.D.idx) {
+            label = msg(I18N_WDIFF_YAXIS_LABEL, I18N_WDIFF_YAXIS_LABEL_DEFAULT);
+        }
+
+        return label;
+    }
+
+
+    /**
+     * Get internationalized label for the y axis.
+     */
+    protected String getWAxisLabel() {
+        FLYSArtifact flys = (FLYSArtifact) master;
+
+        String unit = FLYSUtils.getRiver(flys).getWstUnit().getName();
+
+        return msg(
+            I18N_YAXIS_LABEL,
+            I18N_YAXIS_LABEL_DEFAULT,
+            new Object[] { unit });
+    }
+
+
+    /**
+     * Create Axis for given index.
+     * @return axis with according internationalized label.
+     */
+    @Override
+    protected NumberAxis createYAxis(int index) {
+        NumberAxis axis = super.createYAxis(index);
+
+        // "Q" Axis shall include 0.
+        if (index == YAXIS.Q.idx) {
+            axis.setAutoRangeIncludesZero(true);
+        }
+        else {
+            axis.setAutoRangeIncludesZero(false);
+        }
+
+        return axis;
+    }
+
+
+    /**
+     * Get default value for the second Y-Axis' label (if no translation was
+     * found).
+     */
+    protected String getQAxisDefaultLabel() {
+        return I18N_2YAXIS_LABEL_DEFAULT;
+    }
+
+
+    /**
+     * Get key for internationalization of the second Y-Axis' label.
+     */
+    protected String getQAxisLabelKey() {
+        return I18N_2YAXIS_LABEL;
+    }
+
+
+    /**
+     * Trigger inversion.
+     */
+    @Override
+    protected void adjustAxes(XYPlot plot) {
+        super.adjustAxes(plot);
+        invertXAxis(plot.getDomainAxis());
+    }
+
+
+    /**
+     * This method inverts the x-axis based on the kilometer information of the
+     * selected river. If the head of the river is at kilometer 0, the axis is
+     * not inverted, otherwise it is.
+     *
+     * @param xaxis The domain axis.
+     */
+    protected void invertXAxis(ValueAxis xaxis) {
+        if (isInverted()) {
+            logger.debug("X-Axis.setInverted(true)");
+            xaxis.setInverted(true);
+        }
+    }
+
+
+    /**
+     * Produce output.
+     * @param artifactAndFacet current facet and artifact.
+     * @param attr  theme for facet
+     */
+    @Override
+    public void doOut(
+        ArtifactAndFacet artifactAndFacet,
+        Document         attr,
+        boolean          visible
+    ) {
+        String name = artifactAndFacet.getFacetName();
+
+        logger.debug("LongitudinalSectionGenerator.doOut: " + name);
+
+        if (name == null) {
+            logger.error("No facet name for doOut(). No output generated!");
+            return;
+        }
+
+        Facet facet = artifactAndFacet.getFacet();
+
+        if (facet == null) {
+            return;
+        }
+
+        WOutProcessor processor = new WOutProcessor();
+        if (processor.canHandle(name)) {
+            processor.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(
+                (FLYSAnnotation) artifactAndFacet.getData(context),
+                 artifactAndFacet,
+                 attr,
+                 visible);
+        }
+        else if (name.equals(STATIC_WQKMS_Q)) {
+            doQOut(
+                (WQKms) artifactAndFacet.getData(context),
+                artifactAndFacet,
+                attr,
+                visible);
+        }
+        else if (name.equals(W_DIFFERENCES)) {
+            doWDifferencesOut(
+                (WKms) 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.W.idx);
+        }
+        else {
+            logger.warn("Unknown facet name: " + name);
+            return;
+        }
+    }
+
+    /**
+     * Add items to dataseries which describes the differences.
+     */
+    protected void doWDifferencesOut(
+        WKms       wkms,
+        ArtifactAndFacet aandf,
+        Document   theme,
+        boolean    visible
+    ) {
+        logger.debug("WDifferencesCurveGenerator.doWDifferencesOut");
+        if (wkms == null) {
+            logger.warn("No data to add to WDifferencesChart.");
+            return;
+         }
+
+        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
+
+        if (logger.isDebugEnabled()) {
+            if (wkms.size() > 0) {
+                logger.debug("Generate series: " + series.getKey());
+                logger.debug("Start km: " + wkms.getKm(0));
+                logger.debug("End   km: " + wkms.getKm(wkms.size() - 1));
+                logger.debug("Values  : " + wkms.size());
+            }
+        }
+
+        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
+     * FLYSArtifact.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;
+    }
+
+
+    /**
+     * Get name of series (displayed in legend).
+     * @return name of the series.
+     */
+    protected String getSeriesName(WQKms 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;
+    }
+
+
+    /** Look up the axis identifier for a given facet type. */
+    public int axisIdxForFacet(String facetName) {
+        if (FacetTypes.IS.W(facetName)) {
+            return YAXIS.W.idx;
+        }
+        else if (FacetTypes.IS.Q(facetName)) {
+            return YAXIS.Q.idx;
+        }
+        else {
+            logger.warn("Could not find axis for facet " + facetName);
+            return YAXIS.W.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("LongitudinalSectionGenerator.doArea");
+        StyledAreaSeriesCollection area = new StyledAreaSeriesCollection(theme);
+
+        String seriesName = aandf.getFacetDescription();
+
+        AreaFacet.Data data = (AreaFacet.Data) o;
+
+        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);
+        }
+        // Add area to the respective axis.
+        addAreaSeries(area, axisIdxForFacet(data.getRootFacetName()), 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/flys-artifacts/src/main/java/org/dive4elements/river/exports/LongitudinalSectionInfoGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,17 @@
+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 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/MapGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,344 @@
+package org.dive4elements.river.exports;
+
+import com.vividsolutions.jts.geom.Envelope;
+
+import org.dive4elements.artifactdatabase.data.StateData;
+import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifactdatabase.state.Settings;
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+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.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+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.states.WaterlevelGroundDifferences;
+import org.dive4elements.river.collections.FLYSArtifactCollection;
+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;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+
+public class MapGenerator implements OutGenerator, FacetTypes {
+
+    private static Logger logger = Logger.getLogger(MapGenerator.class);
+
+    protected FLYSArtifactCollection collection;
+
+    protected Artifact master;
+
+    protected Settings settings;
+
+    protected Document request;
+
+    protected OutputStream out;
+
+    protected CallContext context;
+
+    protected List<WMSLayerFacet> layers;
+
+    protected Envelope maxExtent;
+    protected Envelope initialExtent;
+
+    protected String srid;
+
+
+
+    @Override
+    public void init(Document request, OutputStream out, CallContext context) {
+        logger.debug("MapGenerator.init");
+
+        this.request  = request;
+        this.out      = out;
+        this.context  = context;
+
+        this.layers = new ArrayList<WMSLayerFacet>();
+
+        this.maxExtent = null;
+        this.initialExtent = null;
+    }
+
+
+    @Override
+    public void setMasterArtifact(Artifact master) {
+        logger.debug("MapGenerator.setMasterArtifact");
+        this.master = master;
+    }
+
+    @Override
+    public void setCollection(FLYSArtifactCollection collection) {
+        this.collection = collection;
+    }
+
+    @Override
+    public void doOut(
+        ArtifactAndFacet artifactFacet,
+        Document         attr,
+        boolean          visible)
+    {
+        String name = artifactFacet.getFacetName();
+
+        logger.debug("MapGenerator.doOut: " +
+            artifactFacet.getArtifact().identifier() + " | " + name);
+        FLYSArtifact flys = (FLYSArtifact) artifactFacet.getArtifact();
+
+        Facet nativeFacet = artifactFacet.getFacet();
+
+        if (nativeFacet instanceof WMSLayerFacet) {
+            WMSLayerFacet wms = (WMSLayerFacet) nativeFacet;
+            Envelope   extent = wms.getOriginalExtent();
+
+            layers.add(wms);
+
+            setMaxExtent(extent);
+            setSrid(wms.getSrid());
+
+            if (FLOODMAP_WSPLGEN.equals(name)) {
+                setInitialExtent(extent);
+                createWSPLGENLayer(flys, wms, attr);
+            }
+            else if (FLOODMAP_BARRIERS.equals(name)) {
+                createBarriersLayer(flys, wms);
+            }
+            else if (FLOODMAP_USERSHAPE.equals(name)) {
+                createUserShapeLayer(flys, wms);
+            }
+            else {
+                logger.debug("doOut: createDatabaseLayer for facet name: " + name);
+                createDatabaseLayer(flys, wms, attr);
+            }
+        }
+        else {
+            logger.warn("Facet not supported: " + nativeFacet.getClass());
+        }
+    }
+
+
+    protected void createWSPLGENLayer(
+        FLYSArtifact  flys,
+        WMSLayerFacet wms,
+        Document      attr
+    ) {
+        try {
+            if(wms instanceof WSPLGENLayerFacet) {
+                // Retrieve waterlevel ground differences from artifact
+                StateData dFrom = flys.getData(WaterlevelGroundDifferences.LOWER_FIELD);
+                StateData dTo   = flys.getData(WaterlevelGroundDifferences.UPPER_FIELD);
+                StateData dStep = flys.getData(WaterlevelGroundDifferences.DIFF_FIELD);
+
+                String fromStr = dFrom != null ? (String) dFrom.getValue() : null;
+                String toStr   = dTo   != null ? (String) dTo.getValue()   : null;
+                String stepStr = dStep != null ? (String) dStep.getValue() : null;
+
+                float from = Float.parseFloat(fromStr);
+                float to   = Float.parseFloat(toStr);
+                float step = Float.parseFloat(stepStr);
+
+                ArtifactMapfileGenerator mfg = new ArtifactMapfileGenerator();
+                mfg.createUeskLayer(
+                    flys,
+                    (WSPLGENLayerFacet) wms,
+                    ThemeUtil.createDynamicMapserverStyle(attr, from, to, step),
+                    context);
+            }
+            else {
+                logger.warn("Cannot create WSPLGEN layer from: " +
+                            wms.getClass());
+            }
+        }
+        catch (IOException ioe) {
+            logger.error(ioe, ioe);
+        }
+    }
+
+
+    protected void createBarriersLayer(FLYSArtifact flys, WMSLayerFacet wms) {
+        ArtifactMapfileGenerator mfg = new ArtifactMapfileGenerator();
+
+        try {
+            mfg.createBarriersLayer(flys, wms);
+        }
+        catch (FileNotFoundException fnfe) {
+            logger.error(fnfe, fnfe);
+        }
+        catch (IOException ioe) {
+            logger.error(ioe, ioe);
+        }
+    }
+
+
+    protected void createUserShapeLayer(FLYSArtifact flys, WMSLayerFacet wms) {
+        ArtifactMapfileGenerator mfg = new ArtifactMapfileGenerator();
+
+        try {
+            mfg.createUserShapeLayer(flys, wms);
+        }
+        catch (FileNotFoundException fnfe) {
+            logger.error(fnfe, fnfe);
+        }
+        catch (IOException ioe) {
+            logger.error(ioe, ioe);
+        }
+    }
+
+
+    protected void createDatabaseLayer(
+        FLYSArtifact  flys,
+        WMSLayerFacet wms,
+        Document      attr
+    ) {
+        logger.debug("createDatabaseLayer for facet: " + wms.getName());
+
+        ArtifactMapfileGenerator mfg = new ArtifactMapfileGenerator();
+
+        try {
+            File baseDir = mfg.getShapefileBaseDir();
+            File artDir  = new File(baseDir, flys.identifier());
+
+            if (artDir != null && !artDir.exists()) {
+                logger.debug("Create new directory: " + artDir.getPath());
+                artDir.mkdir();
+            }
+
+            if (wms instanceof WMSDBLayerFacet) {
+                mfg.createDatabaseLayer(
+                    flys,
+                    (WMSDBLayerFacet) wms,
+                    ThemeUtil.createMapserverStyle(attr));
+            }
+            else {
+                logger.warn("Cannot create DB layer from: " + wms.getClass());
+            }
+        }
+        catch (FileNotFoundException fnfe) {
+            logger.error(fnfe, fnfe);
+        }
+        catch (IOException ioe) {
+            logger.error(ioe, ioe);
+        }
+    }
+
+
+    @Override
+    public void generate()
+    throws IOException
+    {
+        logger.debug("MapGenerator.generate");
+
+        ArtifactMapfileGenerator mfg = new ArtifactMapfileGenerator();
+        mfg.generate();
+
+        Document response = XMLUtils.newDocument();
+        ElementCreator c  = new ElementCreator(
+            response,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element root   = c.create("floodmap");
+        Element layers = c.create("layers");
+
+        response.appendChild(root);
+        root.appendChild(layers);
+
+        appendLayers(layers);
+        appendMapInformation(root, c);
+
+        XMLUtils.toStream(response, out);
+    }
+
+
+    protected void appendLayers(Element parent) {
+        for (WMSLayerFacet facet: layers) {
+            parent.appendChild(facet.toXML(parent.getOwnerDocument()));
+        }
+    }
+
+
+    protected void setMaxExtent(Envelope maxExtent) {
+        if (maxExtent == null) {
+            return;
+        }
+
+        if (this.maxExtent == null) {
+            logger.debug("Set max extent to: " + maxExtent);
+            this.maxExtent = new Envelope(maxExtent);
+            return;
+        }
+
+        this.maxExtent.expandToInclude(maxExtent);
+    }
+
+
+    protected void setInitialExtent(Envelope initialExtent) {
+        if (this.initialExtent == null && initialExtent != null) {
+            logger.debug("Set initial extent to: " + initialExtent);
+            this.initialExtent = new Envelope(initialExtent);
+        }
+    }
+
+
+    protected void setSrid(String srid) {
+        if (srid == null || srid.length() == 0) {
+            return;
+        }
+
+        this.srid = srid;
+    }
+
+
+    protected void appendMapInformation(Element parent, ElementCreator c) {
+        String mE = GeometryUtils.jtsBoundsToOLBounds(this.maxExtent);
+
+        Element maxExtent = c.create("maxExtent");
+        maxExtent.setTextContent(mE);
+
+        if(this.initialExtent != null) {
+            String iE = GeometryUtils.jtsBoundsToOLBounds(this.initialExtent);
+            Element initExtent = c.create("initialExtent");
+            initExtent.setTextContent(iE);
+            parent.appendChild(initExtent);
+        }
+
+        Element srid = c.create("srid");
+        srid.setTextContent(this.srid);
+
+        // TODO zoom levels
+        // TODO resolutation
+
+        parent.appendChild(maxExtent);
+        parent.appendChild(srid);
+    }
+
+
+    /**
+     * Returns an instance of <i>EmptySettings</i> currently!
+     *
+     * @return an instance of <i>EmptySettings</i>.
+     */
+    @Override
+    public Settings getSettings() {
+        return new EmptySettings();
+    }
+
+
+    @Override
+    public void setSettings(Settings settings) {
+        this.settings = settings;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/MiddleBedHeightExporter.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,160 @@
+package org.dive4elements.river.exports;
+
+import java.io.OutputStream;
+import java.text.NumberFormat;
+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.FLYSArtifact;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.MiddleBedHeightData;
+import org.dive4elements.river.utils.FLYSUtils;
+import org.dive4elements.river.utils.Formatter;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class MiddleBedHeightExporter extends AbstractExporter {
+
+    /** Private logger. */
+    private static final Logger logger =
+        Logger.getLogger(MiddleBedHeightExporter.class);
+
+    public static final String CSV_KM =
+        "export.bedheight_middle.csv.header.km";
+
+    public static final String CSV_SOUNDING =
+        "export.bedheight_middle.csv.header.sounding";
+
+    public static final String CSV_HEIGHT =
+        "export.bedheight_middle.csv.header.height";
+
+    public static final String CSV_UNCERTAINTY =
+        "export.bedheight_middle.csv.header.uncertainty";
+
+    public static final String CSV_DATA_GAP =
+        "export.bedheight_middle.csv.header.datagap";
+
+    public static final String CSV_SOUNDING_WIDTH =
+        "export.bedheight_middle.csv.header.soundingwidth";
+
+    public static final String CSV_WIDTH =
+        "export.bedheight_middle.csv.header.width";
+
+    public static final String CSV_LOCATIONS =
+        "export.bedheight_middle.csv.header.locations";
+
+
+    protected List<MiddleBedHeightData[]> data;
+
+
+    public void init(Document request, OutputStream out, CallContext cc) {
+        super.init(request, out, cc);
+        data = new ArrayList<MiddleBedHeightData[]>();
+    }
+
+
+    @Override
+    protected void addData(Object d) {
+        if (d instanceof CalculationResult) {
+            d = ((CalculationResult) d).getData();
+
+            if (d instanceof MiddleBedHeightData[]) {
+                logger.debug("Add new data of type MiddleBedHeightData");
+                data.add((MiddleBedHeightData[]) d);
+            }
+        }
+    }
+
+
+    @Override
+    protected void writeCSVData(CSVWriter writer) {
+        logger.info("MiddleBedHeightExporter.writeCSVData");
+        logger.debug("CSV gets " + data.size() + " MiddleBedHeightData objects.");
+
+        writeCSVHeader(writer);
+
+        for (MiddleBedHeightData[] d: data) {
+            data2CSV(writer, d);
+        }
+    }
+
+
+    protected void writeCSVHeader(CSVWriter writer) {
+        writer.writeNext(new String[] {
+            msg(CSV_KM, CSV_KM),
+            msg(CSV_SOUNDING, CSV_SOUNDING),
+            msg(CSV_HEIGHT, CSV_HEIGHT),
+            msg(CSV_UNCERTAINTY, CSV_UNCERTAINTY),
+            msg(CSV_DATA_GAP, CSV_DATA_GAP),
+            msg(CSV_SOUNDING_WIDTH, CSV_SOUNDING_WIDTH),
+            msg(CSV_WIDTH, CSV_WIDTH),
+            msg(CSV_LOCATIONS, CSV_LOCATIONS)
+        });
+    }
+
+
+    protected void data2CSV(CSVWriter writer, MiddleBedHeightData[] mData) {
+        logger.debug("Add next MiddleBedHeightData to CSV");
+
+        FLYSArtifact flys = (FLYSArtifact) master;
+
+        NumberFormat kmF     = Formatter.getMiddleBedHeightKM(context);
+        NumberFormat heightF = Formatter.getMiddleBedHeightHeight(context);
+        NumberFormat uncertF = Formatter.getMiddleBedHeightUncert(context);
+        NumberFormat gapF    = Formatter.getMiddleBedHeightDataGap(context);
+        NumberFormat soundF  = Formatter.getMiddleBedHeightSounding(context);
+        NumberFormat widthF  = Formatter.getMiddleBedHeightWidth(context);
+
+        heightF.setMaximumFractionDigits(1);
+        soundF.setMaximumFractionDigits(1);
+        for (MiddleBedHeightData data: mData) {
+            for (int i = 0, n = data.size(); i < n; i++) {
+                int start = data.getStartYear();
+                int end   = data.getEndYear();
+
+                if (start == end) {
+                    writer.writeNext(new String[] {
+                        kmF.format(data.getKM(i)),
+                        data.getDescription(),
+                        heightF.format(data.getMiddleHeight(i)),
+                        uncertF.format(data.getUncertainty(i)),
+                        gapF.format(data.getDataGap(i)) + "%",
+                        soundF.format(data.getSoundingWidth(i)),
+                        widthF.format(data.getWidth(i)),
+                        FLYSUtils.getLocationDescription(flys, data.getKM(i)),
+                    });
+                }
+                else {
+                    writer.writeNext(new String[] {
+                        kmF.format(data.getKM(i)),
+                        data.getDescription(),
+                        heightF.format(data.getMiddleHeight(i)),
+                        "",
+                        "",
+                        "",
+                        "",
+                        FLYSUtils.getLocationDescription(flys, data.getKM(i)),
+                    });
+                }
+
+            }
+        }
+    }
+
+
+    @Override
+    protected void writePDF(OutputStream out) {
+        logger.error("TODO: Implement FlowVelocityExporter.writePDF");
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/MiddleBedHeightGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,274 @@
+package org.dive4elements.river.exports;
+
+import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+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.FLYSAnnotation;
+import org.dive4elements.river.jfree.StyledXYSeries;
+import org.dive4elements.river.utils.FLYSUtils;
+
+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() {
+        FLYSArtifact flys = (FLYSArtifact) master;
+
+        return msg(
+            I18N_XAXIS_LABEL,
+            I18N_XAXIS_LABEL_DEFAULT,
+            new Object[] { FLYSUtils.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) {
+           FLYSArtifact flys = (FLYSArtifact) master;
+           String unit = FLYSUtils.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() {
+        return msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL_DEFAULT);
+    }
+
+
+    /**
+     * 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(
+                (FLYSAnnotation) 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 (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
+     * FLYSArtifact.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);
+
+        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 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/MiddleBedHeightInfoGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,17 @@
+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 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/NormalizedReferenceCurveGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,36 @@
+package org.dive4elements.river.exports;
+
+/**
+ * An OutGenerator that generates reference curves.
+ */
+public class NormalizedReferenceCurveGenerator
+extends      ReferenceCurveGenerator
+{
+    public static final String I18N_NORMALIZED_CHART_TITLE =
+        "chart.normalized.reference.curve.title";
+
+    public static final String I18N_NORMALIZED_CHART_TITLE_DEFAULT  =
+        "Reduzierte Bezugslinie";
+
+    public NormalizedReferenceCurveGenerator() {
+    }
+
+    /** Get default chart title. */
+    @Override
+    protected String getDefaultChartTitle() {
+        return msg(
+            I18N_NORMALIZED_CHART_TITLE,
+            I18N_NORMALIZED_CHART_TITLE_DEFAULT);
+    }
+
+    @Override
+    protected String facetName() {
+        return REFERENCE_CURVE_NORMALIZED;
+    }
+
+    @Override
+    protected boolean doNormalize() {
+        return true;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/NormalizedReferenceCurveInfoGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,15 @@
+package org.dive4elements.river.exports;
+
+
+/**
+ * A ChartInfoGenerator that generates meta information for specific reference
+ * curves.
+ */
+public class NormalizedReferenceCurveInfoGenerator
+extends      ChartInfoGenerator
+{
+    public NormalizedReferenceCurveInfoGenerator() {
+        super(new NormalizedReferenceCurveGenerator());
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/OutGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,83 @@
+package org.dive4elements.river.exports;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.w3c.dom.Document;
+
+import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
+import org.dive4elements.artifactdatabase.state.Settings;
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.collections.FLYSArtifactCollection;
+
+
+/**
+ * An OutGenerator is used to create a collected outputs of a list of Artifacts.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface OutGenerator {
+
+    /**
+     * Initializes the OutGenerator with meta information which are necessary
+     * for the output generation.
+     *
+     * @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);
+
+    /**
+     * This method is used to tell the OutGenerator which artifact is the master
+     * artifact which is used for special operations.
+     *
+     * @param master The master artifact.
+     */
+    void setMasterArtifact(Artifact master);
+
+    /**
+     * This method is used to set the Collection of the OutGenerator.
+     *
+     * @param collection A reference to the collection.
+     */
+    void setCollection(FLYSArtifactCollection collection);
+
+    /**
+     * Creates the output of an Artifact and appends that single output to the
+     * total output.
+     *
+     * @param bundle The Facet and artifact that provides information and data for the
+     * single output.
+     * @param attr A document that might contain some attributes used while
+     * producing the output.
+     * @param visible Specifies, if this output should be visible or not.
+     */
+    void doOut(ArtifactAndFacet bundle, Document attr, boolean visible);
+
+    /**
+     * Writes the collected output of all artifacts specified in the
+     * <i>request</i> (see init()) document to the OutputStream <i>out</i> (see
+     * init()).
+     */
+    void generate() throws IOException;
+
+    /**
+     * This method is used to set a <i>Settings</i> object for the <i>Output</i>
+     * that is produced by this <i>OutGenerator</i>.
+     *
+     * @param settings The <i>Settings</i> that might be used while
+     * <i>Output</i> creation.
+     */
+    void setSettings(Settings settings);
+
+    /**
+     * Returns the Settings for the Output produced by this OutGenerator.
+     *
+     * @return the Settings for the Output produced by this OutGenerator.
+     */
+    Settings getSettings();
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/OutputHelper.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,515 @@
+package org.dive4elements.river.exports;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.xpath.XPathConstants;
+
+import org.apache.log4j.Logger;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import org.dive4elements.artifactdatabase.Backend;
+import org.dive4elements.artifactdatabase.Backend.PersistentArtifact;
+import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.ArtifactDatabase;
+import org.dive4elements.artifacts.ArtifactDatabaseException;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.artifacts.common.ArtifactNamespaceContext;
+import org.dive4elements.artifacts.common.utils.ClientProtocolUtils;
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.context.FLYSContext;
+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.ThemeFactory;
+
+public class OutputHelper {
+    /** The logger used in this class. */
+    private static Logger log = Logger.getLogger(OutputHelper.class);
+
+    protected String identifier;
+
+    public OutputHelper(String identifier) {
+        this.identifier = identifier;
+    }
+    /**
+     * Creates a concrete output.
+     *
+     * @param generator The OutGenerator that creates the output.
+     * @param outputName The name of the requested output.
+     * @param attributes The collection's attributes for this concrete output
+     * type.
+     * @param context The context object.
+     */
+    public void doOut(
+        OutGenerator generator,
+        String       outName,
+        String       facet,
+        Document     attributes,
+        CallContext  context)
+    throws IOException
+    {
+        boolean debug = log.isDebugEnabled();
+
+        if (debug) {
+            log.debug("FLYSArtifactCollection.doOut: " + outName);
+        }
+
+        ThemeList themeList = new ThemeList(attributes);
+
+        int size = themeList.size();
+        if (debug) {
+            log.debug("Output will contain " + size + " elements.");
+        }
+
+        List<ArtifactAndFacet> dataProviders =
+            doBlackboardPass(themeList, context);
+
+        try {
+            for (int i = 0; i < size; i++) {
+                ManagedFacet theme = themeList.get(i);
+
+                if (theme == null) {
+                    log.debug("Theme is empty - no output is generated.");
+                    continue;
+                }
+
+                String art = theme.getArtifact();
+                String facetName = theme.getName();
+
+                if (debug) {
+                    log.debug("Do output for...");
+                    log.debug("... artifact: " + art);
+                    log.debug("... facet: " + facetName);
+                }
+
+                if (outName.equals("export") && !facetName.equals(facet)) {
+                    continue;
+                }
+
+                // Skip invisible themes.
+                if (theme.getVisible() == 0) {
+                    continue;
+                }
+
+                if (outName.equals("sq_overview")) {
+                    generator.doOut(
+                        dataProviders.get(i),
+                        attributes,
+                        theme.getActive() == 1);
+                }
+                else {
+                    generator.doOut(
+                            dataProviders.get(i),
+                            getFacetThemeFromAttribute(
+                                art,
+                                outName,
+                                facetName,
+                                theme.getDescription(),
+                                theme.getIndex(),
+                                context),
+                            theme.getActive() == 1);
+                }
+            }
+        }
+        catch (ArtifactDatabaseException ade) {
+            log.error(ade, ade);
+        }
+    }
+    /**
+     * Returns the attribute that belongs to an artifact and facet stored in
+     * this collection.
+     *
+     * @param uuid The Artifact's uuid.
+     * @param outname The name of the requested output.
+     * @param facet The name of the requested facet.
+     * @param context The CallContext.
+     *
+     * @return an attribute in form of a document.
+     */
+    protected Document getFacetThemeFromAttribute(
+        String      uuid,
+        String      outName,
+        String      facet,
+        String      pattern,
+        int         index,
+        CallContext context)
+    throws    ArtifactDatabaseException
+    {
+        boolean debug = log.isDebugEnabled();
+
+        if (debug) {
+            log.debug(
+                "FLYSArtifactCollection.getFacetThemeFromAttribute(facet="
+                + facet + ", index=" + index + ")");
+        }
+
+        ArtifactDatabase db = context.getDatabase();
+        CallMeta       meta = context.getMeta();
+
+        Document attr = db.getCollectionItemAttribute(identifier, uuid, meta);
+
+        if (attr == null) {
+            attr = initItemAttribute(uuid, facet, pattern, index, outName, context);
+
+            if (attr == null) {
+                return null;
+            }
+        }
+
+        if (debug) {
+            log.debug("Search attribute of collection item: " + uuid);
+        }
+
+        Node tmp = (Node) XMLUtils.xpath(
+            attr,
+            "/art:attribute",
+            XPathConstants.NODE,
+            ArtifactNamespaceContext.INSTANCE);
+
+        if (tmp == null) {
+            log.warn("No attribute found. Operation failed.");
+            return null;
+        }
+
+        if (debug) {
+            log.debug("Search theme for facet '" + facet + "' in attribute.");
+        }
+
+        Map<String, String> vars = new HashMap<String, String>();
+        vars.put("facet", facet);
+        vars.put("index", String.valueOf(index));
+
+        Node theme = (Node) XMLUtils.xpath(
+            tmp,
+            "art:themes/theme[@facet=$facet and @index=$index]",
+            XPathConstants.NODE,
+            ArtifactNamespaceContext.INSTANCE,
+            vars);
+
+        if (theme == null) {
+            log.warn("Could not find the theme in attribute of: " + facet + " " + uuid);
+
+            Theme t = getThemeForFacet(
+                uuid, facet, pattern, index, outName, context);
+
+            if (t == null) {
+                log.warn("No theme found for facet: " + facet);
+                return null;
+            }
+
+            addThemeToAttribute(uuid, attr, t, context);
+            theme = t.toXML().getFirstChild();
+        }
+
+        Document doc = XMLUtils.newDocument();
+        doc.appendChild(doc.importNode(theme, true));
+
+        return doc;
+    }
+    /**
+     * Adds the theme of a facet to a CollectionItem's attribute.
+     *
+     * @param uuid The uuid of the artifact.
+     * @param attr The current attribute of an artifact.
+     * @param t The theme to add.
+     * @param context The CallContext.
+     */
+    protected void addThemeToAttribute(
+        String      uuid,
+        Document    attr,
+        Theme       t,
+        CallContext context)
+    {
+        log.debug("FLYSArtifactCollection.addThemeToAttribute: " + uuid);
+
+        if (t == null) {
+            log.warn("Theme is empty - cancel adding it to attribute!");
+            return;
+        }
+
+        XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(
+            attr,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Node tmp = (Node) XMLUtils.xpath(
+            attr,
+            "/art:attribute",
+            XPathConstants.NODE,
+            ArtifactNamespaceContext.INSTANCE);
+
+        if (tmp == null) {
+            tmp = ec.create("attribute");
+            attr.appendChild(tmp);
+        }
+
+        Node themes = (Node) XMLUtils.xpath(
+            tmp,
+            "art:themes",
+            XPathConstants.NODE,
+            ArtifactNamespaceContext.INSTANCE);
+
+        if (themes == null) {
+            themes = ec.create("themes");
+            tmp.appendChild(themes);
+        }
+
+        themes.appendChild(attr.importNode(t.toXML().getFirstChild(), true));
+
+        try {
+            setCollectionItemAttribute(uuid, attr, context);
+        }
+        catch (ArtifactDatabaseException e) {
+            // do nothing
+            log.warn("Cannot set attribute of item: " + uuid);
+        }
+    }
+
+    /**
+     * Sets the attribute of a CollectionItem specified by <i>uuid</i> to a new
+     * value <i>attr</i>.
+     *
+     * @param uuid The uuid of the CollectionItem.
+     * @param attr The new attribute for the CollectionItem.
+     * @param context The CallContext.
+     */
+    public void setCollectionItemAttribute(
+        String      uuid,
+        Document    attr,
+        CallContext context)
+    throws ArtifactDatabaseException
+    {
+        Document doc = ClientProtocolUtils.newSetItemAttributeDocument(
+            uuid,
+            attr);
+
+        if (doc == null) {
+            log.warn("Cannot set item attribute: No attribute found.");
+            return;
+        }
+
+        ArtifactDatabase db = context.getDatabase();
+        CallMeta       meta = context.getMeta();
+
+        db.setCollectionItemAttribute(identifier, uuid, doc, meta);
+    }
+
+
+    /**
+     * Show blackboard (context) to each facet and create a list of
+     * ArtifactAndFacets on the fly (with the same ordering as the passed
+     * ThemeList).
+     * @param themeList ThemeList to create a ArtifactAndFacetList along.
+     * @param context   The "Blackboard".
+     */
+    protected List<ArtifactAndFacet> doBlackboardPass(
+        ThemeList themeList, CallContext context
+    ) {
+        ArrayList<ArtifactAndFacet> dataProviders =
+            new ArrayList<ArtifactAndFacet>();
+        int size = themeList.size();
+
+        try {
+            // Collect all ArtifactAndFacets for blackboard pass.
+            for (int i = 0; i < size; i++) {
+                ManagedFacet theme = themeList.get(i);
+                if (theme == null) {
+                    log.warn("A ManagedFacet in ThemeList is null.");
+                    continue;
+                }
+                String uuid        = theme.getArtifact();
+                Artifact artifact  = getArtifact(uuid, context);
+                FLYSArtifact flys  = (FLYSArtifact) artifact;
+
+                ArtifactAndFacet artifactAndFacet = new ArtifactAndFacet(
+                    artifact,
+                    flys.getNativeFacet(theme));
+
+                // XXX HELP ME PLEASE
+                artifactAndFacet.setFacetDescription(theme.getDescription());
+
+                // Show blackboard to facet.
+                artifactAndFacet.register(context);
+
+                // Add to themes.
+                dataProviders.add(i, artifactAndFacet);
+            }
+        }
+        catch (ArtifactDatabaseException ade) {
+            log.error("ArtifactDatabaseException!", ade);
+        }
+
+        return dataProviders;
+    }
+    /**
+     * Returns a concrete Artifact of this collection specified by its uuid.
+     *
+     * @param uuid The Artifact's uuid.
+     * @param context The CallContext.
+     *
+     * @return an Artifact.
+     */
+    protected Artifact getArtifact(String uuid, CallContext context)
+    throws    ArtifactDatabaseException
+    {
+        log.debug("FLYSArtifactCollection.getArtifact");
+
+        Backend backend               = Backend.getInstance();
+        PersistentArtifact persistent = backend.getArtifact(uuid);
+
+        return persistent != null ? persistent.getArtifact() : null;
+    }
+
+    /**
+     * Initializes the attribute of an collection item with the theme of a
+     * specific facet.
+     *
+     * @param uuid The uuid of an artifact.
+     * @param facet The name of a facet.
+     * @param context The CallContext.
+     *
+     * @param the new attribute.
+     */
+    protected Document initItemAttribute(
+        String      uuid,
+        String      facet,
+        String      pattern,
+        int         index,
+        String      outName,
+        CallContext context)
+    {
+        boolean debug = log.isDebugEnabled();
+
+        if (debug) {
+            log.debug("FLYSArtifactCollection.initItemAttribute");
+        }
+
+        Theme t = getThemeForFacet(uuid, facet, pattern, index, outName, context);
+
+        if (t == null) {
+            log.info("Could not find theme for facet. Cancel initialization.");
+            return null;
+        }
+
+        Document attr = XMLUtils.newDocument();
+        addThemeToAttribute(uuid, attr, t, context);
+
+        if (debug) {
+            log.debug("initItemAttribute for facet " + facet + ": "
+                + XMLUtils.toString(attr));
+        }
+
+        return attr;
+    }
+
+        /**
+     * Returns the theme of a specific facet.
+     *
+     * @param uuid The uuid of an artifact.
+     * @param facet The name of the facet.
+     * @param context The CallContext object.
+     *
+     * @return the desired theme.
+     */
+    protected Theme getThemeForFacet(
+        String uuid,
+        String facet,
+        String pattern,
+        int    index,
+        String outName,
+        CallContext context)
+    {
+        log.info("FLYSArtifactCollection.getThemeForFacet: " + facet);
+
+        FLYSContext flysContext = context instanceof FLYSContext
+            ? (FLYSContext) context
+            : (FLYSContext) context.globalContext();
+
+        // Push artifact in flysContext.
+        ArtifactDatabase db = context.getDatabase();
+        try {
+            FLYSArtifact artifact = (FLYSArtifact) db.getRawArtifact(uuid);
+            log.debug("Got raw artifact");
+            flysContext.put(FLYSContext.ARTIFACT_KEY, artifact);
+        }
+        catch (ArtifactDatabaseException dbe) {
+            log.error("Exception caught when trying to get art.", dbe);
+        }
+
+        Theme t = ThemeFactory.getTheme(
+                      flysContext,
+                      facet,
+                      pattern,
+                      outName,
+                      "default");
+
+        if (t != null) {
+            log.debug("found theme for facet '" + facet + "'");
+            t.setFacet(facet);
+            t.setIndex(index);
+        }
+        else {
+            log.warn("unable to find theme for facet '" + facet + "'");
+        }
+
+        return t;
+    }
+
+    /**
+     * Inner class to structure/order the themes of a chart.
+     */
+    private static class ThemeList {
+        private Logger logger = Logger.getLogger(ThemeList.class);
+        protected List<ManagedFacet> themes;
+
+        public ThemeList(Document output) {
+            themes = new ArrayList<ManagedFacet>();
+            parse(output);
+        }
+
+        protected void parse(Document output) {
+            NodeList themeList = (NodeList) XMLUtils.xpath(
+                output,
+                "art:output/art:facet",
+                XPathConstants.NODESET,
+                ArtifactNamespaceContext.INSTANCE);
+
+            int num = themeList != null ? themeList.getLength() : 0;
+
+            logger.debug("Output has " +  num + " elements.");
+
+            if (num == 0) {
+                return;
+            }
+
+            for (int i = 0; i < num; i++) {
+                Element theme = (Element) themeList.item(i);
+
+                ManagedDomFacet facet = new ManagedDomFacet(theme);
+                themes.add(facet);
+            }
+
+            Collections.sort(themes);
+        }
+
+        public ManagedFacet get(int idx) {
+            return themes.get(idx);
+        }
+
+        public int size() {
+            return themes.size();
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/ReferenceCurveExporter.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,370 @@
+package org.dive4elements.river.exports;
+
+import au.com.bytecode.opencsv.CSVWriter;
+
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.artifacts.common.utils.Config;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.WINFOArtifact;
+
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.WWQQ;
+import org.dive4elements.river.artifacts.model.WWQQJRDataSource;
+
+import org.dive4elements.river.artifacts.resources.Resources;
+
+import org.dive4elements.river.utils.FLYSUtils;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import java.text.DateFormat;
+import java.text.NumberFormat;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import net.sf.jasperreports.engine.JRException;
+import net.sf.jasperreports.engine.JasperExportManager;
+import net.sf.jasperreports.engine.JasperFillManager;
+import net.sf.jasperreports.engine.JasperPrint;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+
+/**
+ * (CSV)Exporter for Reference Curves.
+ */
+public class ReferenceCurveExporter extends AbstractExporter {
+
+    /** The logger used in this exporter. */
+    private static Logger logger = Logger.getLogger(ReferenceCurveExporter.class);
+
+    public static final String RC_CSV_KM_HEADER =
+        "export.reference_curve.csv.header.km";
+
+    public static final String RC_CSV_W_CM_HEADER =
+        "export.reference_curve.csv.header.w.cm";
+
+    public static final String RC_CSV_W_M_HEADER =
+        "export.reference_curve.csv.header.w.m";
+
+    public static final String RC_CSV_Q_HEADER =
+        "export.reference_curve.csv.header.w.q";
+
+    public static final String CSV_LOCATION_HEADER =
+        "export.waterlevel.csv.header.location";
+
+    public static final String DEFAULT_CSV_LOCATION_HEADER = "Lage";
+
+    public static final String RC_DEFAULT_CSV_KM_HEADER = "Fluss-Km";
+    public static final String RC_DEFAULT_CSV_W_M_HEADER  = "W (m + NHN)";
+    public static final String RC_DEFAULT_CSV_W_CM_HEADER  = "W (cm am Pegel)";
+    public static final String RC_DEFAULT_CSV_Q_HEADER  = "gleichw. Q (m\u00b3/s)";
+
+    public static final String PDF_HEADER_MODE = "export.reference_curve.pdf.mode";
+    public static final String JASPER_FILE = "export.reference_curve.pdf.file";
+    public static final String JASPER_FILE_GAUGE = "export.reference_curve.pdf.file.gauge";
+    public static final String JASPER_FILE_GAUGE_END = "export.reference_curve.pdf.file.gauge.end";
+    public static final String JASPER_FILE_GAUGE_START_END = "export.reference_curve.pdf.file.gauge.start.end";
+
+    /** The storage that contains all WKms objects for the different facets. */
+    protected List<WWQQ[]> data;
+
+    protected boolean startAtGauge = false;
+
+    protected boolean endAtGauge = false;
+
+
+    @Override
+    public void init(Document request, OutputStream out, CallContext context) {
+        logger.debug("ReferenceCurveExporter.init");
+
+        super.init(request, out, context);
+
+        this.data = new ArrayList<WWQQ[]>();
+    }
+
+
+    /**
+     * Genereate data in csv format.
+     */
+    @Override
+    public void generate()
+    throws IOException
+    {
+        logger.debug("ReferenceCurveExporter.generate");
+
+        if (facet == null) {
+            throw new IOException("invalid (null) facet for exporter");
+        }
+        else if (facet.equals(AbstractExporter.FACET_CSV)) {
+            generateCSV();
+        }
+        else if (facet.equals(AbstractExporter.FACET_PDF)) {
+            generatePDF();
+        }
+        else {
+            throw new IOException("invalid facet (" + facet + ") for exporter");
+        }
+    }
+
+
+    /**
+     * Adds given data.
+     * @param d A CalculationResult with WWQQ[].
+     */
+    @Override
+    protected void addData(Object d) {
+        logger.debug("ReferenceCurveExporter.addData");
+
+        if (d instanceof CalculationResult) {
+            d = ((CalculationResult)d).getData();
+            if (d instanceof WWQQ []) {
+                WWQQ[] wwqqs = (WWQQ []) d;
+                for (WWQQ wwqq: wwqqs) {
+                    if (wwqq.startAtGauge()) {
+                        startAtGauge = true;
+                    }
+                    // TODO this one probably has to be inverted.
+                    if (wwqq.endAtGauge()) {
+                        endAtGauge = true;
+                    }
+                }
+                data.add(wwqqs);
+                logger.debug("ReferenceCurveExporter.addData wwqq[].");
+            }
+            else {
+                logger.warn("ReferenceCurveExporter.addData/1 unknown type ("
+                    + d + ").");
+            }
+        }
+        else {
+            logger.warn("ReferenceCurveExporter.addData/2 unknown type ("
+                + d + ").");
+        }
+    }
+
+
+    /**
+     * Lets writer write all data (including header).
+     * @param writer Writer to write data with.
+     */
+    @Override
+    protected void writeCSVData(CSVWriter writer) {
+        logger.debug("ReferenceCurveExporter.writeData");
+
+        writeCSVHeader(writer);
+
+        for (WWQQ[] tmp: data) {
+            for (WWQQ ww: tmp) {
+                wWQQ2CSV(writer, ww);
+            }
+        }
+    }
+
+
+    /**
+     * Lets csvwriter write the header (first line in file).
+     * @param writer Writer to write header with.
+     */
+    protected void writeCSVHeader(CSVWriter writer) {
+        logger.info("ReferenceCurveExporter.writeCSVHeader");
+
+        StepCSVWriter stepWriter = new StepCSVWriter();
+        stepWriter.setCSVWriter(writer);
+
+        stepWriter.addNexts(
+            msg(RC_CSV_KM_HEADER, RC_DEFAULT_CSV_KM_HEADER),
+            msg(RC_CSV_W_M_HEADER, RC_DEFAULT_CSV_W_M_HEADER)
+            );
+        if (startAtGauge) {
+            stepWriter.addNext(
+                msg(RC_CSV_W_CM_HEADER, RC_DEFAULT_CSV_W_CM_HEADER));
+        }
+        stepWriter.addNexts(
+            msg(RC_CSV_Q_HEADER, RC_DEFAULT_CSV_Q_HEADER),
+            msg(CSV_LOCATION_HEADER, DEFAULT_CSV_LOCATION_HEADER),
+            msg(RC_CSV_KM_HEADER, RC_DEFAULT_CSV_KM_HEADER),
+            msg(RC_CSV_W_M_HEADER, RC_DEFAULT_CSV_W_M_HEADER)
+            );
+        if (endAtGauge) {
+            stepWriter.addNext(
+                msg(RC_CSV_W_CM_HEADER, RC_DEFAULT_CSV_W_CM_HEADER)
+                );
+        }
+        stepWriter.addNexts(
+                msg(RC_CSV_Q_HEADER, RC_DEFAULT_CSV_Q_HEADER),
+                msg(CSV_LOCATION_HEADER, DEFAULT_CSV_LOCATION_HEADER)
+                );
+
+        stepWriter.flush();
+    }
+
+
+    protected void wWQQ2CSV(CSVWriter writer, WWQQ ww) {
+        logger.debug("ReferenceCurveExporter.wWQQ2CSV");
+
+        NumberFormat kmf = getKmFormatter();
+        NumberFormat wf  = getWFormatter();
+        NumberFormat qf  = getQFormatter();
+
+        int         size = ww.size();
+
+        FLYSArtifact flys       = (FLYSArtifact) master;
+
+        StepCSVWriter stepWriter = new StepCSVWriter();
+        stepWriter.setCSVWriter(writer);
+
+        String startLocationDescription = FLYSUtils.getLocationDescription(
+            flys, ww.getStartKm());
+
+        String endLocationDescription = FLYSUtils.getLocationDescription(
+            flys, ww.getEndKm());
+
+        for (int i = 0; i < size; i ++) {
+            stepWriter.addNexts(kmf.format(ww.getStartKm()));
+            stepWriter.addNext(wf.format(ww.getW1(i)));
+            if (startAtGauge) {
+                stepWriter.addNext(wf.format(ww.getRelHeight1Cm(i)));
+            }
+            stepWriter.addNexts(
+                qf.format(ww.getQ1(i)), // "Q"
+                startLocationDescription,
+                kmf.format(ww.getEndKm())
+                );
+            stepWriter.addNext(wf.format(ww.getW2(i)));
+            if (endAtGauge) {
+                if (ww.endAtGauge()) {
+                    stepWriter.addNext(wf.format(ww.getRelHeight2Cm(i)));
+                }
+                else {
+                    stepWriter.addNext("-");
+                }
+            }
+            stepWriter.addNexts(
+                qf.format(ww.getQ2(i)), // "Q"
+                endLocationDescription
+                );
+            stepWriter.flush();
+        }
+    }
+
+
+    @Override
+    protected void writePDF(OutputStream out) {
+        WWQQJRDataSource source = createJRData();
+
+        String filename = JASPER_FILE;
+        if (startAtGauge && endAtGauge) {
+            filename = JASPER_FILE_GAUGE_START_END;
+        }
+        else if (startAtGauge) {
+            filename = JASPER_FILE_GAUGE;
+        }
+        else if (endAtGauge) {
+            filename = JASPER_FILE_GAUGE_END;
+        }
+
+        String jasperFile = Resources.getMsg(
+                                context.getMeta(),
+                                filename,
+                                "/jasper/reference_en.jasper");
+        String confPath = Config.getConfigDirectory().toString();
+
+        Map parameters = new HashMap();
+        parameters.put("ReportTitle", "Exported Data");
+        try {
+            JasperPrint print = JasperFillManager.fillReport(
+                confPath + jasperFile,
+                parameters,
+                source);
+            JasperExportManager.exportReportToPdfStream(print, out);
+        }
+        catch(JRException je) {
+            logger.warn("Error generating PDF Report!");
+            je.printStackTrace();
+        }
+    }
+
+    protected WWQQJRDataSource createJRData() {
+        WWQQJRDataSource source = new WWQQJRDataSource();
+
+        addMetaData(source);
+
+        for (WWQQ[] tmp: data) {
+            for (WWQQ ww: tmp) {
+                addWWQQData(source, ww);
+            }
+        }
+        return source;
+    }
+
+
+    protected void addMetaData(WWQQJRDataSource source) {
+        CallMeta meta = context.getMeta();
+
+        WINFOArtifact flys = (WINFOArtifact) master;
+
+        source.addMetaData ("river", FLYSUtils.getRivername(flys));
+
+        Locale locale = Resources.getLocale(meta);
+        DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, locale);
+
+        source.addMetaData("date", df.format(new Date()));
+
+        source.addMetaData("calculation", Resources.getMsg(
+                                            locale,
+                                            PDF_HEADER_MODE,
+                                            "Reference Curve"));
+    }
+
+
+    protected void addWWQQData(WWQQJRDataSource source, WWQQ ww) {
+        NumberFormat kmf = getKmFormatter();
+        NumberFormat wf  = getWFormatter();
+        NumberFormat qf  = getQFormatter();
+
+        int          size = ww.size();
+
+        FLYSArtifact flys = (FLYSArtifact) master;
+
+        String startLocationDescription = FLYSUtils.getLocationDescription(
+            flys, ww.getStartKm());
+
+        String endLocationDescription = FLYSUtils.getLocationDescription(
+            flys, ww.getEndKm());
+
+        for (int i = 0; i < size; i ++) {
+            String start = "-";
+            String end = "-";
+            if (startAtGauge) {
+                start = wf.format(ww.getRelHeight1Cm(i));
+            }
+            if (ww.endAtGauge()) {
+                end = wf.format(ww.getRelHeight2Cm(i));
+            }
+            source.addData(new String[] {
+                kmf.format(ww.getStartKm()),
+                startLocationDescription,
+                wf.format(ww.getW1(i)),
+                qf.format(ww.getQ1(i)), // "Q"
+                kmf.format(ww.getEndKm()),
+                endLocationDescription,
+                wf.format(ww.getW2(i)),
+                qf.format(ww.getQ2(i)), // "Q"
+                start,
+                end
+            });
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/ReferenceCurveGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,271 @@
+package org.dive4elements.river.exports;
+
+import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.WW;
+import org.dive4elements.river.artifacts.model.WW.ApplyFunctionIterator;
+import org.dive4elements.river.artifacts.model.WWAxisTypes;
+import org.dive4elements.river.jfree.FLYSAnnotation;
+import org.dive4elements.river.jfree.StyledXYSeries;
+import org.dive4elements.river.utils.Formatter;
+
+import java.awt.geom.Point2D;
+
+import org.apache.log4j.Logger;
+import org.jfree.chart.axis.NumberAxis;
+import org.jfree.chart.axis.NumberTickUnit;
+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.
+ */
+public class ReferenceCurveGenerator
+extends      XYChartGenerator
+implements   FacetTypes
+{
+    public static enum YAXIS {
+        W(0);
+
+        public int idx;
+        private YAXIS(int c) {
+           idx = c;
+        }
+    }
+
+    /** House logger. */
+    private static Logger logger =
+        Logger.getLogger(ReferenceCurveGenerator.class);
+
+    public static final String I18N_CHART_TITLE =
+        "chart.reference.curve.title";
+
+    public static final String I18N_CHART_SUBTITLE =
+        "chart.reference.curve.subtitle";
+
+    public static final String I18N_X_AXIS_IN_CM =
+        "chart.reference.curve.x.axis.in.cm";
+
+    public static final String I18N_X_AXIS_IN_M =
+        "chart.reference.curve.x.axis.in.m";
+
+    public static final String I18N_Y_AXIS_IN_CM =
+        "chart.reference.curve.y.axis.in.cm";
+
+    public static final String I18N_Y_AXIS_IN_M =
+        "chart.reference.curve.y.axis.in.m";
+
+    public static final String I18N_CHART_TITLE_DEFAULT  =
+        "Bezugslinie";
+
+
+    public ReferenceCurveGenerator() {
+    }
+
+    /**
+     * Create Axis for given index.
+     * @return axis with according internationalized label.
+     */
+    @Override
+    protected NumberAxis createYAxis(int index) {
+        NumberAxis axis = super.createYAxis(index);
+        axis.setAutoRangeIncludesZero(false);
+        return axis;
+    }
+
+
+    /** Get default chart title. */
+    @Override
+    protected String getDefaultChartTitle() {
+        return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT);
+    }
+
+    @Override
+    protected String getDefaultChartSubtitle() {
+        Object[] args = new Object[] {
+            getRiverName(),
+        };
+
+        return msg(I18N_CHART_SUBTITLE, "", args);
+    }
+
+
+    /** True if axis is in cm (because at gauge). */
+    protected boolean getInCm(int index) {
+        Object obj = context.getContextValue("reference.curve.axis.scale");
+        return obj instanceof WWAxisTypes && ((WWAxisTypes)obj).getInCm(index);
+    }
+
+
+    /** Get Label for X-axis (W). */
+    @Override
+    protected String getDefaultXAxisLabel() {
+        return msg(getInCm(0) ? I18N_X_AXIS_IN_CM : I18N_X_AXIS_IN_M);
+    }
+
+
+    /**
+     * Get Label for primary and other Y Axes.
+     * @param index Axis-Index (0-based).
+     */
+    @Override
+    protected String getDefaultYAxisLabel(int index) {
+        return msg(getInCm(1) ? I18N_Y_AXIS_IN_CM : I18N_Y_AXIS_IN_M);
+    }
+
+    protected String facetName() {
+        return REFERENCE_CURVE;
+    }
+
+
+    /**
+     * Called for each facet/them in the out mapped to this generator.
+     * @param artifactFacet artifact and facet for this theme.
+     * @param theme         styling info.
+     * @param visible       Whether or not the theme is visible.
+     */
+    @Override
+    public void doOut(
+        ArtifactAndFacet artifactFacet,
+        Document         theme,
+        boolean          visible
+    ) {
+        String name = artifactFacet.getFacetName();
+
+        logger.debug("ReferenceCurveGenerator.doOut: " + name);
+
+        if (name == null || name.length() == 0) {
+            logger.error("No facet given. Cannot create dataset.");
+            return;
+        }
+
+        if (name.equals(facetName())) {
+            doReferenceOut(artifactFacet.getData(context), theme, visible);
+        }
+        else if (FacetTypes.IS.MANUALPOINTS(name)) {
+            doPoints(
+                artifactFacet.getData(context),
+                artifactFacet,
+                theme,
+                visible,
+                YAXIS.W.idx);
+        }
+        else if (name.equals(RELATIVE_POINT)) {
+            doPointOut(
+                (Point2D) artifactFacet.getData(context),
+                artifactFacet,
+                theme,
+                visible);
+        }
+        else if (name.equals(MAINVALUES_W)) {
+            doAnnotations(
+                ((FLYSAnnotation) artifactFacet.getData(context)).flipStickyAxis(),
+                artifactFacet,
+                theme,
+                visible);
+
+        }
+        else {
+            logger.warn("Unknown facet name: " + name);
+        }
+    }
+
+    protected boolean doNormalize() {
+        return false;
+    }
+
+
+    /** Register DataSeries with (maybe transformed) points. */
+    public void doReferenceOut(
+        Object   data,
+        Document theme,
+        boolean  visible
+    ) {
+        WW ww = (WW)data;
+
+        Object obj = context.getContextValue("reference.curve.axis.scale");
+
+        WWAxisTypes wwat = obj instanceof WWAxisTypes
+            ? (WWAxisTypes)obj
+            : new WWAxisTypes(ww);
+
+        ApplyFunctionIterator iter = wwat.transform(ww, doNormalize());
+
+        XYSeries series = new StyledXYSeries(
+            ww.getName(), false, theme);
+
+        double [] values = new double[2];
+
+        while (iter.hasNext()) {
+            iter.next(values);
+            series.add(values[0], values[1], false);
+        }
+
+        addAxisSeries(series, YAXIS.W.idx, visible);
+    }
+
+    // TODO resolve duplicate in DurationCurveGenerator
+    protected void doPointOut(
+        Point2D point,
+        ArtifactAndFacet aandf,
+        Document theme,
+        boolean visible
+    ){
+        logger.debug("ReferenceCurveGenerator.doPointOut");
+
+        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
+
+        series.add(point.getX(), point.getY());
+
+        addAxisSeries(series, YAXIS.W.idx, visible);
+    }
+
+
+    /** Set the tick units for given axis. */
+    protected void setAxisTickUnit(double tick, ValueAxis axis) {
+        TickUnits units = new TickUnits();
+        units.add(new NumberTickUnit(tick, Formatter.getWaterlevelW(context)));
+        axis.setStandardTickUnits(units);
+        axis.setAutoTickUnitSelection(true);
+    }
+
+    @Override
+    protected void localizeDomainAxis(ValueAxis domainAxis) {
+        super.localizeDomainAxis(domainAxis);
+        if (getInCm(0)) {
+            setAxisTickUnit(100d, domainAxis);
+        }
+        else {
+            setAxisTickUnit(1d, domainAxis);
+        }
+    }
+
+
+    @Override
+    protected void localizeRangeAxis(ValueAxis rangeAxis) {
+        super.localizeRangeAxis(rangeAxis);
+        setAxisTickUnit(1d, rangeAxis);
+    }
+
+    /** Get Walker to iterate over all axes. */
+    @Override
+    protected YAxisWalker getYAxisWalker() {
+        return new YAxisWalker() {
+            /** Get number of items. */
+            @Override
+            public int length() {
+                return YAXIS.values().length;
+            }
+
+            /** Get identifier for this index. */
+            @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 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/ReferenceCurveInfoGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,15 @@
+package org.dive4elements.river.exports;
+
+
+/**
+ * A ChartInfoGenerator that generates meta information for specific reference
+ * curves.
+ */
+public class ReferenceCurveInfoGenerator
+extends      ChartInfoGenerator
+{
+    public ReferenceCurveInfoGenerator() {
+        super(new ReferenceCurveGenerator());
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/ReportGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,96 @@
+package org.dive4elements.river.exports;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+
+import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifactdatabase.state.Settings;
+
+import org.dive4elements.river.artifacts.model.Calculation;
+import org.dive4elements.river.collections.FLYSArtifactCollection;
+
+import org.w3c.dom.Document;
+
+public class ReportGenerator
+implements   OutGenerator
+{
+    private static Logger logger = Logger.getLogger(ReportGenerator.class);
+
+    protected Document     result;
+    protected OutputStream out;
+    protected CallContext  context;
+
+    public ReportGenerator() {
+    }
+
+    @Override
+    public void init(Document request, OutputStream out, CallContext context) {
+        logger.debug("init");
+        this.out     = out;
+        this.context = context;
+        result       = null;
+    }
+
+    @Override
+    public void setMasterArtifact(Artifact master) {
+        // not needed
+    }
+
+    @Override
+    public void setCollection(FLYSArtifactCollection collection) {
+        // not needed
+    }
+
+    @Override
+    public void doOut(
+        ArtifactAndFacet artifactFacet,
+        Document         attr,
+        boolean          visible
+    ) {
+        logger.debug("doOut");
+        Facet facet = artifactFacet.getFacet();
+        if (facet != null) {
+            Calculation report = (Calculation) artifactFacet.getData(context);
+            if (result == null) {
+                result = XMLUtils.newDocument();
+                report.toXML(result, context.getMeta());
+            }
+        }
+    }
+
+    @Override
+    public void generate() throws IOException {
+        logger.debug("generate");
+        XMLUtils.toStream(result != null
+            ? result
+            : XMLUtils.newDocument(), out);
+    }
+
+
+    /**
+     * Returns an instance of <i>EmptySettings</i> currently!
+     *
+     * @return an instance of <i>EmptySettings</i>.
+     */
+    public Settings getSettings() {
+        return new EmptySettings();
+    }
+
+
+    /**
+     * This method is not implemented. Override it in subclasses if those need a
+     * <i>Settings</i> object.
+     */
+    public void setSettings(Settings settings) {
+        // do nothing
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/StepCSVWriter.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,51 @@
+package org.dive4elements.river.exports;
+
+import au.com.bytecode.opencsv.CSVWriter;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * Class to overcome shortcoming of CSVWriter to accept String-Arrays only.
+ * The StepCSVWriter buffers incoming values, such that rows in a csv can be
+ * created more dynamically. Do not forget to call flush().
+ */
+public class StepCSVWriter {
+
+    /** Writer to use when calling flush. */
+    CSVWriter writer = null;
+    /** Buffer of strings (values). */
+    ArrayList<String> buffer;
+
+
+    /** Trivial constructor. */
+    public StepCSVWriter() {
+        buffer = new ArrayList<String>();
+    }
+
+
+    /** Set writer. */
+    public void setCSVWriter(CSVWriter writer) {
+        this.writer = writer;
+    }
+
+
+    /** Add a value to next flush. */
+    public void addNext(String value) {
+        buffer.add(value);
+    }
+
+
+    /** Add many values to next flush. */
+    public void addNexts(String ... values) {
+        buffer.addAll(Arrays.asList(values));
+    }
+
+
+    /** Write the row with csvwriter. */
+    public void flush() {
+        writer.writeNext(buffer.toArray(new String[buffer.size()]));
+        buffer.clear();
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/StringAttribute.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,34 @@
+package org.dive4elements.river.exports;
+
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class StringAttribute extends VisibleAttribute {
+
+
+    public StringAttribute(String name, String value, boolean visible) {
+        super(name, value, visible);
+    }
+
+
+    /**
+     * Calls VisibleAttribute.toXML() and appends afterwards an attribute
+     * <i>type</i> with value <i>string</i>.
+     *
+     * @param parent The parent Node.
+     *
+     * @return the new Node that represents this Attribute.
+     */
+    @Override
+    public Node toXML(Node parent) {
+        Element ele = (Element) super.toXML(parent);
+        ele.setAttribute("type", "string");
+
+        return ele;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/StyledSeriesBuilder.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,240 @@
+package org.dive4elements.river.exports;
+
+import org.apache.log4j.Logger;
+
+import org.jfree.data.xy.XYSeries;
+
+import org.dive4elements.river.artifacts.model.WKms;
+import org.dive4elements.river.artifacts.model.WQKms;
+import org.dive4elements.river.artifacts.model.WWQQ;
+
+/**
+ * Helper to create and modify StyledXYSeries.
+ */
+public class StyledSeriesBuilder {
+
+    /**
+     * JFreeChart and the area calculation will fail if we use Double.INFINITY
+     * or Double.MAX_VALUE (probably because these are really used in
+     * calculations). We define and use a more handy value instead.
+     */
+    final static double BIG_DOUBLE_VALUE = 1234567d;
+
+    private static final Logger logger = Logger.getLogger
+        (StyledSeriesBuilder.class);
+
+
+    /**
+     * Trivial, hidden constructor.
+     */
+    private StyledSeriesBuilder() {
+    }
+
+
+    /**
+     * Add points to series.
+     *
+     * @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.
+     */
+    public static void addPoints(XYSeries series, double[][] points, boolean skipNANs) {
+        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;
+            }
+            series.add(xPoints[i], yPoints[i], false);
+        }
+    }
+
+
+    /**
+     * Add points to series (km to 1st dim, w to 2nd dim).
+     *
+     * @param series Series to add points to.
+     * @param wkms WKms to add to series.
+     */
+    public static void addPoints(XYSeries series, WKms wkms) {
+        if (wkms == null) {
+            return;
+        }
+
+        int size = wkms.size();
+
+        for (int i = 0; i < size; i++) {
+            series.add(wkms.getKm(i), wkms.getW(i), false);
+        }
+    }
+
+
+    /**
+     * Add points to dataset with an offset (shift all points by given amount).
+     * @param series series to add data to.
+     * @param wkms WKms of which the Ws will be shifted.
+     * @param off the offset.
+     */
+    public static void addUpperBand(XYSeries series, WKms wkms, double off) {
+        if (wkms == null) {
+            return;
+        }
+
+        int size = wkms.size();
+
+        for (int i = 0; i < size; i++) {
+            series.add(wkms.getKm(i), wkms.getW(i)+off, false);
+        }
+    }
+
+
+    /**
+     * Add points to dataset with an offset (shift all points 'down' by given
+     * amount).
+     * @param series series to add data to.
+     * @param wkms WKms of which the Ws will be shifted.
+     * @param off the offset.
+     */
+    public static void addLowerBand(XYSeries series, WKms wkms, double off) {
+        addUpperBand(series, wkms, -off);
+    }
+
+
+    /**
+     * Add points to series (km to 1st dim, q to 2nd dim).
+     *
+     * @param series Series to add points to.
+     * @param wqkms WQKms to add to series.
+     */
+    public static void addPointsKmQ(XYSeries series, WQKms wqkms) {
+        if (wqkms == null) {
+            return;
+        }
+
+        int size = wqkms.size();
+
+        for (int i = 0; i < size; i++) {
+            series.add(wqkms.getKm(i), wqkms.getQ(i), false);
+        }
+    }
+
+
+    /**
+     * Add points to series (km to 1st dim, q to 2nd dim), adding points
+     * to achieve a step-like curve.
+     *
+     * @param series Series to add points to.
+     * @param wqkms WQKms to add to series.
+     */
+    public static void addStepPointsKmQ(XYSeries series, WQKms wqkms) {
+        if (wqkms == null) {
+            return;
+        }
+
+        int size = wqkms.size();
+
+        for (int i = 0; i < size; i++) {
+            if (i==0) {
+                series.add(wqkms.getKm(i), wqkms.getQ(i), false);
+            }
+            else {
+                //Add two points.
+                double halveX = (wqkms.getKm(i-1) + wqkms.getKm(i)) / 2d;
+                series.add(halveX, wqkms.getQ(i-1), false);
+                series.add(halveX, wqkms.getQ(i), false);
+            }
+            if (i == size-1) {
+                series.add(wqkms.getKm(i), wqkms.getQ(i), false);
+            }
+        }
+    }
+
+
+    /**
+     * Add points to series (q to 1st dim, w to 2nd dim).
+     *
+     * @param series Series to add points to.
+     * @param wqkms WQKms to add to series.
+     */
+    public static void addPointsQW(XYSeries series, WQKms wqkms) {
+        if (wqkms == null) {
+            return;
+        }
+
+        int size = wqkms.size();
+
+        for (int i = 0; i < size; i++) {
+            series.add(wqkms.getQ(i), wqkms.getW(i), false);
+        }
+    }
+
+
+    /**
+     * Add points to series (q to 1st dim, w to 2nd dim).
+     *
+     * @param series Series to add points to.
+     * @param qs the Qs to add, assumed same length than ws.
+     * @param ws the Ws to add, assumed same length than qs.
+     */
+    public static void addPointsQW(XYSeries series, double[] qs, double ws[]) {
+        if (ws == null || qs == null) {
+            return;
+        }
+
+        int size = qs.length;
+
+        for (int i = 0; i < size; i++) {
+            series.add(qs[i], ws[i], false);
+        }
+    }
+
+
+    /**
+     * Add points to series (q to 1st dim, w to 2nd dim).
+     *
+     * @param series Series to add points to.
+     * @param wwqq WWQQ to add to series.
+     */
+    public static void addPoints(XYSeries series, WWQQ wwqq) {
+        if (wwqq == null) {
+            return;
+        }
+
+        int size = wwqq.size();
+
+        for (int i = 0; i < size; i++) {
+            series.add(wwqq.getW1(i), wwqq.getW2(i), false);
+        }
+    }
+
+
+    /**
+     * Create a Series such that an infinitely big area can be filled
+     * between the newly created and the given series.
+     */
+    public static XYSeries createGroundAtInfinity(XYSeries series) {
+        XYSeries ground = new XYSeries(series.getKey() + /** TODO rand + */ "INF");
+        ground.add(series.getMinX(), -BIG_DOUBLE_VALUE);
+        ground.add(series.getMaxX(), -BIG_DOUBLE_VALUE);
+        return ground;
+    }
+
+
+    /**
+     * Create a Series such that an infinitely big area can be filled
+     * between the newly created and the given series.
+     */
+    public static XYSeries createCeilingAtInfinity(XYSeries series) {
+        XYSeries ground = new XYSeries(series.getKey() + /** TODO rand + */ "INF");
+        ground.add(series.getMinX(), BIG_DOUBLE_VALUE);
+        ground.add(series.getMaxX(), BIG_DOUBLE_VALUE);
+        return ground;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/TimeseriesChartGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,892 @@
+package org.dive4elements.river.exports;
+
+import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
+import org.dive4elements.river.artifacts.resources.Resources;
+import org.dive4elements.river.jfree.Bounds;
+import org.dive4elements.river.jfree.CollisionFreeXYTextAnnotation;
+import org.dive4elements.river.jfree.DoubleBounds;
+import org.dive4elements.river.jfree.FLYSAnnotation;
+import org.dive4elements.river.jfree.StyledTimeSeries;
+import org.dive4elements.river.jfree.TimeBounds;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.ImageIcon;
+
+import org.apache.log4j.Logger;
+import org.jfree.chart.ChartFactory;
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.LegendItem;
+import org.jfree.chart.LegendItemCollection;
+import org.jfree.chart.annotations.XYAnnotation;
+import org.jfree.chart.annotations.XYImageAnnotation;
+import org.jfree.chart.annotations.XYTextAnnotation;
+import org.jfree.chart.axis.ValueAxis;
+import org.jfree.chart.plot.Marker;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
+import org.jfree.data.Range;
+import org.jfree.data.general.Series;
+import org.jfree.data.time.Day;
+import org.jfree.data.time.TimeSeries;
+import org.jfree.data.time.TimeSeriesCollection;
+import org.jfree.data.xy.XYDataset;
+import org.jfree.ui.Layer;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.w3c.dom.Document;
+
+/**
+ * @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
+
+    protected List<Marker> domainMarker;
+
+    protected List<Marker> valueMarker;
+
+    protected Map<String, String> attributes;
+
+    protected boolean domainZeroLineVisible;
+
+    private static final Logger logger =
+        Logger.getLogger(TimeseriesChartGenerator.class);
+
+    public static final int AXIS_SPACE = 5;
+
+    protected Map<Integer, Bounds> xBounds;
+
+    protected Map<Integer, Bounds> yBounds;
+
+
+    /**
+     * The default constructor that initializes internal datastructures.
+     */
+    public TimeseriesChartGenerator() {
+        super();
+
+        xBounds = new HashMap<Integer, Bounds>();
+        yBounds = new HashMap<Integer, Bounds>();
+        domainMarker = new ArrayList<Marker>();
+        valueMarker = new ArrayList<Marker>();
+        attributes = new HashMap<String, String>();
+    }
+
+
+
+    @Override
+    public JFreeChart generateChart() {
+        logger.info("Generate Timeseries Chart.");
+
+        JFreeChart chart = ChartFactory.createTimeSeriesChart(
+            getChartTitle(),
+            getXAxisLabel(),
+            getYAxisLabel(0),
+            null,
+            isLegendVisible(),
+            false,
+            false);
+
+        XYPlot plot = (XYPlot) chart.getPlot();
+
+        chart.setBackgroundPaint(Color.WHITE);
+        plot.setBackgroundPaint(Color.WHITE);
+
+        addSubtitles(chart);
+        adjustPlot(plot);
+        addDatasets(plot);
+        adjustAxes(plot);
+        addDomainAxisMarker(plot);
+        addValueAxisMarker(plot);
+        adaptZoom(plot);
+
+        applySeriesAttributes(plot);
+
+        addAnnotationsToRenderer(plot);
+        addLogo(plot);
+        aggregateLegendEntries(plot);
+        return chart;
+    }
+
+
+    /**
+     * Return left most data points x value (on first axis).
+     * Shortcut, especially to be overridden in (LS) charts where
+     * axis could be inverted.
+     */
+    protected double getLeftX() {
+        return (Long)getXBounds(0).getLower();
+    }
+
+
+    /**
+     * Return right most data points x value (on first axis).
+     * Shortcut, especially to be overridden in (LS) charts where
+     * axis could be inverted.
+     */
+    protected double getRightX() {
+        return (Long)getXBounds(0).getUpper();
+    }
+
+
+    /**
+     * Add a logo as background annotation to plot.
+     * Copy from XYChartGenerator.
+     */
+    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:
+         XYChartGenerator.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 = XYChartGenerator.class.getResource("/images/intevation.png");
+        }
+        else { // TODO else if ...
+            imageURL = XYChartGenerator.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 = ((Long)getXBounds(0).getUpper() + (Long)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);
+    }
+
+
+    @Override
+    protected Series getSeriesOf(XYDataset dataset, int idx) {
+        return ((TimeSeriesCollection) dataset).getSeries(idx);
+    }
+
+
+    /**
+     * This method creates new instances of TimeseriesAxisDataset.
+     *
+     * @param idx The symbol for the new TimeseriesAxisDataset.
+     */
+    @Override
+    protected AxisDataset createAxisDataset(int idx) {
+        logger.debug("Create a new AxisDataset for index: " + idx);
+        return new TimeseriesAxisDataset(idx);
+    }
+
+
+    @Override
+    protected void combineXBounds(Bounds bounds, int index) {
+        if (bounds != null) {
+            Bounds old = getXBounds(index);
+
+            if (old != null) {
+                bounds = bounds.combine(old);
+            }
+
+            setXBounds(index, bounds);
+        }
+    }
+
+
+    @Override
+    protected void combineYBounds(Bounds bounds, int index) {
+        if (bounds != null) {
+            Bounds old = getYBounds(index);
+
+            if (old != null) {
+                bounds = bounds.combine(old);
+            }
+
+            setYBounds(index, bounds);
+        }
+    }
+
+
+    // TODO REPLACE THIS METHOD WITH getBoundsForAxis(index)
+    @Override
+    public Range[] getRangesForAxis(int index) {
+        // TODO
+        Bounds[] bounds = getBoundsForAxis(index);
+
+        return new Range[] {
+            new Range(
+                bounds[0].getLower().doubleValue(),
+                bounds[0].getUpper().doubleValue()),
+            new Range(
+                bounds[1].getLower().doubleValue(),
+                bounds[1].getUpper().doubleValue())
+        };
+    }
+
+
+    @Override
+    public Bounds getXBounds(int axis) {
+        return xBounds.get(axis);
+    }
+
+
+    @Override
+    protected void setXBounds(int axis, Bounds bounds) {
+        xBounds.put(axis, bounds);
+    }
+
+
+    @Override
+    public Bounds getYBounds(int axis) {
+        return yBounds.get(axis);
+    }
+
+
+    @Override
+    protected void setYBounds(int axis, Bounds bounds) {
+        if (bounds != null) {
+            yBounds.put(axis, bounds);
+        }
+    }
+
+
+    public Bounds[] getBoundsForAxis(int index) {
+        logger.debug("Return x and y bounds for axis at: " + index);
+
+        Bounds rx = getXBounds(Integer.valueOf(index));
+        Bounds ry = getYBounds(Integer.valueOf(index));
+
+        if (rx == null) {
+            logger.warn("Range for x axis not set." +
+                        " Using default values: 0 - 1.");
+            rx = new TimeBounds(0l, 1l);
+        }
+
+        if (ry == null) {
+            logger.warn("Range for y axis not set." +
+                        " Using default values: 0 - 1.");
+            ry = new DoubleBounds(0l, 1l);
+        }
+
+        logger.debug("X Bounds at index " + index + " is: " + rx);
+        logger.debug("Y Bounds at index " + index + " is: " + ry);
+
+        return new Bounds[] {rx, ry};
+    }
+
+
+    /** Get (zoom)values from request. */
+    public Bounds getDomainAxisRange() {
+        String[] ranges = getDomainAxisRangeFromRequest();
+
+        if (ranges == null || ranges.length < 2) {
+            logger.debug("No zoom range for domain axis specified.");
+            return null;
+        }
+
+        if (ranges[0] == null || ranges[1] == null) {
+            logger.warn("Invalid ranges for domain axis specified!");
+            return null;
+        }
+
+        try {
+            double lower = Double.parseDouble(ranges[0]);
+            double upper = Double.parseDouble(ranges[1]);
+
+            return new DoubleBounds(lower, upper);
+        }
+        catch (NumberFormatException nfe) {
+            logger.warn("Invalid ranges for domain axis specified: " + nfe);
+        }
+
+        return null;
+    }
+
+
+    public Bounds getValueAxisRange() {
+        String[] ranges = getValueAxisRangeFromRequest();
+
+        if (ranges == null || ranges.length < 2) {
+            logger.debug("No zoom range for domain axis specified.");
+            return null;
+        }
+
+        if (ranges[0] == null || ranges[1] == null) {
+            logger.warn("Invalid ranges for domain axis specified!");
+            return null;
+        }
+
+        try {
+            double lower = Double.parseDouble(ranges[0]);
+            double upper = Double.parseDouble(ranges[1]);
+
+            return new DoubleBounds(lower, upper);
+        }
+        catch (NumberFormatException nfe) {
+            logger.warn("Invalid ranges for domain axis specified: " + nfe);
+        }
+
+        return null;
+    }
+
+
+    protected void adaptZoom(XYPlot plot) {
+        logger.debug("Adapt zoom of Timeseries chart.");
+
+        zoomX(plot, plot.getDomainAxis(), getXBounds(0), getDomainAxisRange());
+
+        Bounds valueAxisBounds = getValueAxisRange();
+
+        for (int j = 0, n = plot.getRangeAxisCount(); j < n; j++) {
+            zoomY(
+                plot,
+                plot.getRangeAxis(j),
+                getYBounds(j),
+                valueAxisBounds);
+        }
+    }
+
+
+    /**
+     * @param plot the plot.
+     * @param axis the value (x, time) axis of which to set bounds.
+     * @param total the current bounds (?).
+     */
+    protected void zoomX(
+        XYPlot    plot,
+        ValueAxis axis,
+        Bounds    total,//we could equally nicely getXBounds(0)
+        Bounds    user
+    ) {
+        if (logger.isDebugEnabled()) {
+            logger.debug("== Zoom X axis ==");
+            logger.debug("    Total axis range  : " + total);
+            logger.debug("    User defined range: " + user);
+        }
+
+        if (user != null) {
+            long min  = total.getLower().longValue();
+            long max  = total.getUpper().longValue();
+            long diff = max > min ? max - min : min - max;
+
+            long newMin = Math.round(min + user.getLower().doubleValue() * diff);
+            long newMax = Math.round(min + user.getUpper().doubleValue() * diff);
+
+            TimeBounds newBounds = new TimeBounds(newMin, newMax);
+
+            logger.debug("    Zoom axis to: " + newBounds);
+
+            newBounds.applyBounds(axis, AXIS_SPACE);
+        }
+        else {
+            logger.debug("No user specified zoom values found!");
+            if (total != null && axis != null) {
+                total.applyBounds(axis, AXIS_SPACE);
+            }
+        }
+    }
+
+
+    /**
+     * @param user zoom values in percent.
+     */
+    protected void zoomY(
+        XYPlot    plot,
+        ValueAxis axis,
+        Bounds    total,
+        Bounds    user
+    ) {
+        if (logger.isDebugEnabled()) {
+            logger.debug("== Zoom Y axis ==");
+            logger.debug("    Total axis range  : " + total);
+            logger.debug("    User defined range: " + user);
+        }
+
+        if (user != null) {
+            double min  = total.getLower().doubleValue();
+            double max  = total.getUpper().doubleValue();
+            double diff = max > min ? max - min : min - max;
+
+            double newMin = min + user.getLower().doubleValue() * diff;
+            double newMax = min + user.getUpper().doubleValue() * diff;
+
+            DoubleBounds newBounds = new DoubleBounds(newMin, newMax);
+
+            logger.debug("    Zoom axis to: " + newBounds);
+
+            newBounds.applyBounds(axis, AXIS_SPACE);
+        }
+        else {
+            logger.debug("No user specified zoom values found!");
+            if (total != null && axis != null) {
+                total.applyBounds(axis, AXIS_SPACE);
+            }
+        }
+    }
+
+
+    /**
+     * Adjusts the axes of a plot. This method sets the <i>labelFont</i> of the
+     * X axis.
+     *
+     * (Duplicate in XYChartGenerator).
+     *
+     * @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);
+    }
+
+
+    protected Date decodeXAxisValue(JSONArray array) throws JSONException, ParseException {
+        try {
+            double x = array.getDouble(0);
+            long l = (new Double(x)).longValue();
+            return new Date(l);
+        }
+        catch(JSONException ex) {
+            String str = array.getString(0);
+            DateFormat df = DateFormat.getDateInstance(
+                    DateFormat.MEDIUM, Resources.getLocale(context.getMeta()));
+            return df.parse(str);
+        }
+    }
+
+    /**
+     * Do Points out.
+     */
+    protected void doPoints(
+        Object     o,
+        ArtifactAndFacet aandf,
+        Document   theme,
+        boolean    visible,
+        int        axisIndex
+    ) {
+        String seriesName = aandf.getFacetDescription();
+        TimeSeries series = new StyledTimeSeries(seriesName, theme);
+
+        // Add text annotations for single points.
+        List<XYTextAnnotation> xy = new ArrayList<XYTextAnnotation>();
+        HashMap<Day, String> names = new HashMap<Day, String>();
+
+        try {
+            JSONArray points = new JSONArray((String) o);
+            for (int i = 0, P = points.length(); i < P; i++) {
+                JSONArray array = points.getJSONArray(i);
+
+                double y    = array.getDouble(1);
+                String name = array.getString(2);
+                boolean act = array.getBoolean(3);
+                if (!act) {
+                    continue;
+                }
+
+                Date date = decodeXAxisValue(array);
+
+                Day day = new Day(date);
+                series.add(day, y, false);
+                names.put(day, name);
+            }
+        }
+        catch(JSONException ex) {
+            logger.error("Could not decode json");
+        }
+        catch(ParseException ex) {
+            logger.error("Could not parse date string");
+        }
+
+        TimeSeriesCollection tsc = new TimeSeriesCollection();
+        tsc.addSeries(series);
+        // Add Annotations.
+        for (int i = 0, S = series.getItemCount(); i < S; i++) {
+            double x = tsc.getXValue(0, i);
+            double y = tsc.getYValue(0, i);
+            xy.add(new CollisionFreeXYTextAnnotation(
+                       names.get(series.getTimePeriod(i)), x, y));
+            logger.debug("doPoints(): x=" + x + " y=" + y);
+        }
+        FLYSAnnotation annotations =
+            new FLYSAnnotation(null, null, null, theme);
+        annotations.setTextAnnotations(xy);
+
+        // Do not generate second legend entry. (null was passed for the aand before).
+        doAnnotations(annotations, null, theme, visible);
+
+        addAxisDataset(tsc, axisIndex, visible);
+    }
+
+    public void addDomainAxisMarker(XYPlot plot) {
+        logger.debug("domainmarkers: " + domainMarker.size());
+        for (Marker marker: domainMarker) {
+            logger.debug("adding domain marker");
+            plot.addDomainMarker(marker, Layer.BACKGROUND);
+        }
+        domainMarker.clear();
+    }
+
+    public void addValueAxisMarker(XYPlot plot) {
+        for (Marker marker: valueMarker) {
+            logger.debug("adding value marker..");
+            plot.addRangeMarker(marker, Layer.BACKGROUND);
+        }
+        valueMarker.clear();
+    }
+
+    public void addAttribute(String seriesKey, String name) {
+        attributes.put(seriesKey, name);
+    }
+
+    private LegendItem getLegendItemFor(XYPlot plot, String interSeriesKey) {
+        LegendItemCollection litems = plot.getLegendItems();
+        Iterator<LegendItem> iter = litems.iterator();
+        while(iter.hasNext()) {
+            LegendItem item = iter.next();
+            if(interSeriesKey.startsWith(item.getSeriesKey().toString())) {
+                return item;
+            }
+        }
+        return null;
+    }
+
+    protected void applySeriesAttributes(XYPlot plot) {
+        int count  = plot.getDatasetCount();
+        for (int i = 0; i < count; i++) {
+            XYDataset data = plot.getDataset(i);
+            if (data == null) {
+                continue;
+            }
+
+            int seriesCount = data.getSeriesCount();
+            for (int j = 0; j < seriesCount; j++) {
+                StyledTimeSeries series =
+                    (StyledTimeSeries)getSeriesOf(data, j);
+                String key = series.getKey().toString();
+
+                if (attributes.containsKey(key)) {
+                    // Interpolated points are drawn unfilled
+                    if (attributes.get(key).equals("interpolate")) {
+                        XYLineAndShapeRenderer renderer =
+                                series.getStyle().getRenderer();
+                        renderer.setSeriesPaint(
+                            j,
+                            renderer.getSeriesFillPaint(j));
+                        renderer.setSeriesShapesFilled(j, false);
+
+                        LegendItem legendItem = getLegendItemFor(plot, key);
+                        if(legendItem != null) {
+                            LegendItem interLegend = new LegendItem(
+                                    legendItem.getLabel(),
+                                    legendItem.getDescription(),
+                                    legendItem.getToolTipText(),
+                                    legendItem.getURLText(),
+                                    legendItem.isShapeVisible(),
+                                    legendItem.getShape(),
+                                    false, // shapeFilled?
+                                    legendItem.getFillPaint(),
+                                    true,  // shapeOutlineVisible?
+                                    renderer.getSeriesFillPaint(j),
+                                    legendItem.getOutlineStroke(),
+                                    legendItem.isLineVisible(),
+                                    legendItem.getLine(),
+                                    legendItem.getLineStroke(),
+                                    legendItem.getLinePaint()
+                                    );
+                            interLegend.setSeriesKey(series.getKey());
+                            logger.debug("applySeriesAttributes: draw unfilled legend item");
+                            plot.getLegendItems().add(interLegend);
+                        }
+                    }
+                }
+
+                if (attributes.containsKey(key)) {
+                    if(attributes.get(key).equals("outline")) {
+                        XYLineAndShapeRenderer renderer =
+                            series.getStyle().getRenderer();
+                        renderer.setSeriesPaint(
+                            j,
+                            renderer.getSeriesFillPaint(j));
+                        renderer.setDrawOutlines(true);
+                    }
+                }
+            }
+        }
+    }
+
+    /** Two Ranges that span a rectangular area. */
+    public static class Area {
+        protected Range xRange;
+        protected Range yRange;
+
+        public Area(Range rangeX, Range rangeY) {
+            this.xRange = rangeX;
+            this.yRange = rangeY;
+        }
+
+        public Area(ValueAxis axisX, ValueAxis axisY) {
+            this.xRange = axisX.getRange();
+            this.yRange = axisY.getRange();
+        }
+
+        public double ofLeft(double percent) {
+            return xRange.getLowerBound()
+                + xRange.getLength() * percent;
+        }
+
+        public double ofRight(double percent) {
+            return xRange.getUpperBound()
+                - xRange.getLength() * percent;
+        }
+
+        public double ofGround(double percent) {
+            return yRange.getLowerBound()
+                + yRange.getLength() * percent;
+        }
+
+        public double atTop() {
+            return yRange.getUpperBound();
+        }
+
+        public double atGround() {
+            return yRange.getLowerBound();
+        }
+
+        public double atRight() {
+            return xRange.getUpperBound();
+        }
+
+        public double atLeft() {
+            return xRange.getLowerBound();
+        }
+
+        public double above(double percent, double base) {
+            return base + yRange.getLength() * percent;
+        }
+    }
+
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/TypeSection.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,136 @@
+package org.dive4elements.river.exports;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifactdatabase.state.Attribute;
+import org.dive4elements.artifactdatabase.state.DefaultSection;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class TypeSection extends DefaultSection {
+
+    private static final Logger logger = Logger.getLogger(TypeSection.class);
+
+    public TypeSection(String key) {
+        super(key);
+    }
+
+
+    /** Set a string value for a attribute with additional (choice) type. */
+    public void setChoiceStringValue(String key, String value, String choiceType) {
+        if (value == null || value.length() == 0) {
+            return;
+        }
+
+        Attribute attr = getAttribute(key);
+        if (attr == null) {
+            attr = new ChoiceStringAttribute(key, value, true, choiceType);
+            addAttribute(key, attr);
+        }
+        else {
+            attr.setValue(value);
+        }
+    }
+
+
+    public void setStringValue(String key, String value) {
+        if (value == null || value.length() == 0) {
+            return;
+        }
+
+        Attribute attr = getAttribute(key);
+        if (attr == null) {
+            attr = new StringAttribute(key, value, true);
+            addAttribute(key, attr);
+        }
+        else {
+            attr.setValue(value);
+        }
+    }
+
+
+    public String getStringValue(String key) {
+        Attribute attr = getAttribute(key);
+
+        if (attr instanceof StringAttribute) {
+            return (String) attr.getValue();
+        }
+
+        logger.debug("attribute " + key + " not found in typesection.getString");
+
+        return null;
+    }
+
+
+    public void setIntegerValue(String key, int value) {
+        Attribute attr = getAttribute(key);
+        if (attr == null) {
+            attr = new IntegerAttribute(key, value, true);
+            addAttribute(key, attr);
+        }
+        else {
+            attr.setValue(value);
+        }
+    }
+
+
+    public Integer getIntegerValue(String key) {
+        Attribute attr = getAttribute(key);
+
+        if (attr instanceof IntegerAttribute) {
+            return (Integer) attr.getValue();
+        }
+
+        return null;
+    }
+
+
+
+    public void setDoubleValue(String key, double value) {
+        Attribute attr = getAttribute(key);
+        if (attr == null) {
+            attr = new DoubleAttribute(key, value, true);
+            addAttribute(key, attr);
+        }
+        else {
+            attr.setValue(value);
+        }
+    }
+
+
+    public Double getDoubleValue(String key) {
+        Attribute attr = getAttribute(key);
+
+        if (attr instanceof DoubleAttribute) {
+            return (Double) attr.getValue();
+        }
+
+        return null;
+    }
+
+
+    public void setBooleanValue(String key, boolean value) {
+        Attribute attr = getAttribute(key);
+        if (attr == null) {
+            attr = new BooleanAttribute(key, value, true);
+            addAttribute(key, attr);
+        }
+        else {
+            attr.setValue(value);
+        }
+    }
+
+
+    public Boolean getBooleanValue(String key) {
+        Attribute attr = getAttribute(key);
+
+        if (attr instanceof BooleanAttribute) {
+            return (Boolean) attr.getValue();
+        }
+
+        return 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/flys-artifacts/src/main/java/org/dive4elements/river/exports/VisibleAttribute.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,39 @@
+package org.dive4elements.river.exports;
+
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import org.dive4elements.artifactdatabase.state.DefaultAttribute;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class VisibleAttribute extends DefaultAttribute {
+
+    protected boolean visible;
+
+
+    public VisibleAttribute(String name, Object value, boolean visible) {
+        super(name, value);
+        this.visible = visible;
+    }
+
+
+    /**
+     * This implementation of Attribute calls DefaultAttribute.toXML() first.
+     * After this, a new Attr <i>display</i> is added to the resulting Node.
+     *
+     * @param parent The parent Node.
+     *
+     * @return a new Node that represents this Attribute.
+     */
+    @Override
+    public Node toXML(Node parent) {
+        Element ele = (Element) super.toXML(parent);
+        ele.setAttribute("display", String.valueOf(visible));
+
+        return ele;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/WDifferencesCurveGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,109 @@
+package org.dive4elements.river.exports;
+
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.WKms;
+
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.plot.XYPlot;
+
+
+/**
+ * An OutGenerator that generates w differences curves.
+ */
+public class WDifferencesCurveGenerator
+extends      LongitudinalSectionGenerator
+implements   FacetTypes
+{
+    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;
+    }
+
+
+    /**
+     *
+     */
+    @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 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/WDifferencesCurveInfoGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,15 @@
+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 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/WDifferencesExporter.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,258 @@
+package org.dive4elements.river.exports;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.text.NumberFormat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Date;
+import java.text.DateFormat;
+import java.util.Locale;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+import au.com.bytecode.opencsv.CSVWriter;
+
+import net.sf.jasperreports.engine.JasperExportManager;
+import net.sf.jasperreports.engine.JasperFillManager;
+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;
+
+import org.dive4elements.river.artifacts.WINFOArtifact;
+
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.WKms;
+import org.dive4elements.river.artifacts.model.WKmsJRDataSource;
+import org.dive4elements.river.artifacts.resources.Resources;
+
+import org.dive4elements.river.utils.FLYSUtils;
+import org.dive4elements.river.utils.Formatter;
+
+/**
+ * (CSV)Exporter for WDifferences.
+ */
+public class WDifferencesExporter extends AbstractExporter {
+
+    /** The logger used in this exporter. */
+    private static Logger logger = Logger.getLogger(WDifferencesExporter.class);
+
+
+    public static final String WDIFF_CSV_KM_HEADER =
+        "export.w_differences.csv.header.km";
+
+    public static final String WDIFF_CSV_W_HEADER =
+        "export.w_differences.csv.header.w";
+
+    public static final String WDIFF_DEFAULT_CSV_KM_HEADER = "Fluss-Km";
+    public static final String WDIFF_DEFAULT_CSV_W_HEADER  = "m";
+
+    public static final String PDF_HEADER_MODE = "export.wdifferences.pdf.mode";
+    public static final String JASPER_FILE = "export.wdifferences.pdf.file";
+
+    /** 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[]>();
+    }
+
+
+    /**
+     * Genereate data in csv format.
+     */
+    @Override
+    public void generate()
+    throws IOException
+    {
+        logger.debug("WDifferencesExporter.generate");
+
+        if (facet == null) {
+            throw new IOException("invalid (null) facet for exporter");
+        }
+        else if (facet.equals(AbstractExporter.FACET_CSV)) {
+            generateCSV();
+        }
+        else if (facet.equals(AbstractExporter.FACET_PDF)) {
+            generatePDF();
+        }
+        else {
+            throw new IOException("invalid facet (" + facet + ") for exporter");
+        }
+    }
+
+
+    /**
+     * Adds given data.
+     * @param d either a WKms or a CalculationResult to add to data.
+     */
+    @Override
+    protected void addData(Object d) {
+        if (d instanceof CalculationResult) {
+            d = ((CalculationResult)d).getData();
+            if (d instanceof WKms []) {
+                data.add((WKms [])d);
+            }
+        }
+        else if (d instanceof WKms) {
+            data.add(new WKms[] { (WKms) d });
+        }
+    }
+
+
+    /**
+     * Lets writer write all data (including header).
+     * @param writer Writer to write data with.
+     */
+    @Override
+    protected void writeCSVData(CSVWriter writer) {
+        logger.info("WDifferencesExporter.writeData");
+
+        writeCSVHeader(writer);
+
+        for (WKms[] tmp: data) {
+            for (WKms wkms: tmp) {
+                wKms2CSV(writer, wkms);
+            }
+        }
+    }
+
+
+    /**
+     * Lets csvwriter write the header (first line in file).
+     * @param writer Writer to write header with.
+     */
+    protected void writeCSVHeader(CSVWriter writer) {
+        logger.info("WDifferencesExporter.writeCSVHeader");
+
+        writer.writeNext(new String[] {
+            msg(WDIFF_CSV_KM_HEADER, WDIFF_DEFAULT_CSV_KM_HEADER),
+            msg(WDIFF_CSV_W_HEADER, WDIFF_DEFAULT_CSV_W_HEADER)
+        });
+    }
+
+
+    protected void wKms2CSV(CSVWriter writer, WKms wkms) {
+        logger.debug("WDifferencesExporter.wKms2CSV");
+
+        NumberFormat kmf  = getKmFormatter();
+        NumberFormat wf   = getWFormatter();
+        int          size = wkms.size();
+
+        for (int i = 0; i < size; i ++) {
+
+            writer.writeNext(new String[] {
+                kmf.format(wkms.getKm(i)),
+                wf.format(wkms.getW(i))
+            });
+        }
+    }
+
+
+    /**
+     * Returns the number formatter for kilometer values.
+     *
+     * @return the number formatter for kilometer values.
+     */
+    protected NumberFormat getKmFormatter() {
+        return Formatter.getWaterlevelKM(context);
+    }
+
+
+    /**
+     * Returns the number formatter for W values.
+     *
+     * @return the number formatter for W values.
+     */
+    protected NumberFormat getWFormatter() {
+        return Formatter.getWaterlevelW(context);
+    }
+
+
+    @Override
+    protected void writePDF(OutputStream out) {
+        WKmsJRDataSource source = createJRData();
+
+        String jasperFile = Resources.getMsg(
+                                context.getMeta(),
+                                JASPER_FILE,
+                                "/jasper/wdifferences_en.jasper");
+        String confPath = Config.getConfigDirectory().toString();
+
+        Map parameters = new HashMap();
+        parameters.put("ReportTitle", "Exported Data");
+        try {
+            JasperPrint print = JasperFillManager.fillReport(
+                confPath + jasperFile,
+                parameters,
+                source);
+            JasperExportManager.exportReportToPdfStream(print, out);
+        }
+        catch(JRException je) {
+            logger.warn("Error generating PDF Report!");
+            je.printStackTrace();
+        }
+    }
+
+    protected WKmsJRDataSource createJRData() {
+        WKmsJRDataSource source = new WKmsJRDataSource();
+
+        addMetaData(source);
+        for (WKms[] tmp: data) {
+            for (WKms wkms: tmp) {
+                addWKmsData(source, wkms);
+            }
+        }
+        return source;
+    }
+
+
+    protected void addMetaData(WKmsJRDataSource source) {
+        CallMeta meta = context.getMeta();
+
+        WINFOArtifact flys = (WINFOArtifact) master;
+
+        source.addMetaData ("river", FLYSUtils.getRivername(flys));
+
+        Locale locale = Resources.getLocale(meta);
+        DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, locale);
+
+        source.addMetaData("date", df.format(new Date()));
+
+        String differences = FLYSUtils.getWDifferences(flys, context);
+        source.addMetaData("differences", differences);
+
+        source.addMetaData("calculation", Resources.getMsg(
+                                            locale,
+                                            PDF_HEADER_MODE,
+                                            "W Differences"));
+    }
+
+    protected void addWKmsData(WKmsJRDataSource source, WKms wkms) {
+        NumberFormat kmf  = getKmFormatter();
+        NumberFormat wf   = getWFormatter();
+        int          size = wkms.size();
+
+        for (int i = 0; i < size; i ++) {
+
+            source.addData(new String[] {
+                kmf.format(wkms.getKm(i)),
+                wf.format(wkms.getW(i))
+            });
+        }
+    }
+
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/WaterlevelExporter.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,854 @@
+package org.dive4elements.river.exports;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.text.DateFormat;
+import java.text.NumberFormat;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+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;
+
+import org.dive4elements.river.artifacts.model.ConstantWQKms;
+
+import net.sf.jasperreports.engine.JasperExportManager;
+import net.sf.jasperreports.engine.JasperFillManager;
+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;
+
+import org.dive4elements.river.model.Gauge;
+
+import org.dive4elements.river.artifacts.access.FixRealizingAccess;
+import org.dive4elements.river.artifacts.access.RangeAccess;
+import org.dive4elements.river.artifacts.FixationArtifact;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.WINFOArtifact;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.Segment;
+import org.dive4elements.river.artifacts.model.WQCKms;
+import org.dive4elements.river.artifacts.model.WQKms;
+import org.dive4elements.river.artifacts.model.WKmsJRDataSource;
+import org.dive4elements.river.artifacts.model.WQKmsResult;
+import org.dive4elements.river.artifacts.resources.Resources;
+
+import org.dive4elements.river.utils.FLYSUtils;
+import org.dive4elements.river.utils.FLYSUtils.WQ_MODE;
+import org.dive4elements.river.utils.Formatter;
+
+/**
+ * Generates different output formats (wst, csv, pdf) of data that resulted from
+ * a waterlevel computation.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class WaterlevelExporter extends AbstractExporter {
+
+    /** The logger used in this exporter.*/
+    private static Logger logger = Logger.getLogger(WaterlevelExporter.class);
+
+    public static final String FACET_WST = "wst";
+
+    public static final String CSV_KM_HEADER =
+        "export.waterlevel.csv.header.km";
+
+    public static final String CSV_W_HEADER =
+        "export.waterlevel.csv.header.w";
+
+    public static final String CSV_Q_HEADER =
+        "export.waterlevel.csv.header.q";
+
+    public static final String CSV_Q_DESC_HEADER =
+        "export.waterlevel.csv.header.q.desc";
+
+    public static final String CSV_W_DESC_HEADER =
+        "export.waterlevel.csv.header.w.desc";
+
+    public static final String CSV_LOCATION_HEADER =
+        "export.waterlevel.csv.header.location";
+
+    public static final String CSV_GAUGE_HEADER =
+        "export.waterlevel.csv.header.gauge";
+
+    public static final String CSV_META_RESULT =
+        "export.waterlevel.csv.meta.result";
+
+    public static final String CSV_META_CREATION =
+        "export.waterlevel.csv.meta.creation";
+
+    public static final String CSV_META_CALCULATIONBASE =
+        "export.waterlevel.csv.meta.calculationbase";
+
+    public static final String CSV_META_RIVER =
+        "export.waterlevel.csv.meta.river";
+
+    public static final String CSV_META_RANGE =
+        "export.waterlevel.csv.meta.range";
+
+    public static final String CSV_META_GAUGE =
+        "export.waterlevel.csv.meta.gauge";
+
+    public static final String CSV_META_Q =
+        "export.waterlevel.csv.meta.q";
+
+    public static final String CSV_META_W =
+        "export.waterlevel.csv.meta.w";
+
+    public static final String CSV_NOT_IN_GAUGE_RANGE =
+        "export.waterlevel.csv.not.in.gauge.range";
+
+    public static final Pattern NUMBERS_PATTERN =
+        Pattern.compile("\\D*(\\d++.\\d*)\\D*");
+
+    public static final String DEFAULT_CSV_KM_HEADER       = "Fluss-Km";
+    public static final String DEFAULT_CSV_W_HEADER        = "W [NN + m]";
+    public static final String DEFAULT_CSV_Q_HEADER        = "Q [m\u00b3/s]";
+    public static final String DEFAULT_CSV_Q_DESC_HEADER   = "Bezeichnung";
+    public static final String DEFAULT_CSV_W_DESC_HEADER   = "W/Pegel [cm]";
+    public static final String DEFAULT_CSV_LOCATION_HEADER = "Lage";
+    public static final String DEFAULT_CSV_GAUGE_HEADER    = "Bezugspegel";
+    public static final String DEFAULT_CSV_NOT_IN_GAUGE_RANGE =
+        "außerhalb des gewählten Bezugspegels";
+
+    public static final String PDF_HEADER_MODE = "export.waterlevel.pdf.mode";
+    public static final String JASPER_FILE     = "export.waterlevel.pdf.file";
+
+    /** The storage that contains all WQKms objects for the different facets.*/
+    protected List<WQKms[]> data;
+
+
+    public void init(Document request, OutputStream out, CallContext context) {
+        logger.debug("WaterlevelExporter.init");
+
+        super.init(request, out, context);
+
+        this.data = new ArrayList<WQKms[]>();
+    }
+
+
+    @Override
+    public void generate()
+    throws IOException
+    {
+        logger.debug("WaterlevelExporter.generate");
+
+        if (facet != null && facet.equals(AbstractExporter.FACET_CSV)) {
+            generateCSV();
+        }
+        else if (facet != null && facet.equals(FACET_WST)) {
+            generateWST();
+        }
+        else if (facet != null && facet.equals(AbstractExporter.FACET_PDF)) {
+            generatePDF();
+        }
+        else {
+            throw new IOException("invalid facet for exporter");
+        }
+    }
+
+
+    @Override
+    protected void addData(Object d) {
+        if (d instanceof CalculationResult) {
+            d = ((CalculationResult)d).getData();
+            if (d instanceof WQKms []) {
+                data.add((WQKms [])d);
+            }
+            else if (d instanceof WQKmsResult) {
+                data.add(((WQKmsResult) d).getWQKms());
+            }
+        }
+    }
+
+
+    /**
+     * This method is used to prepare the column titles of waterlevel exports.
+     * Titles in this export include the Q value. If a Q value matches a named
+     * main value (as HQ100 or MNQ) this named main value should be used as
+     * title. This method resets the name of the <i>wqkms</i> object if such
+     * named main value fits to the chosen Q.
+     *
+     * @param winfo A WINFO Artifact.
+     * @param wqkms A WQKms object that should be prepared.
+     */
+    protected String getColumnTitle(WINFOArtifact winfo, WQKms wqkms) {
+        logger.debug("WaterlevelExporter.getColumnTitle");
+
+        String name = wqkms.getName();
+
+        logger.debug("Name of WQKms = '" + name + "'");
+
+        if (name.indexOf("W=") >= 0) {
+            return name;
+        }
+
+        Matcher m = NUMBERS_PATTERN.matcher(name);
+
+        if (m.matches()) {
+            String raw = m.group(1);
+
+            try {
+                double v = Double.valueOf(raw);
+
+                String nmv = FLYSUtils.getNamedMainValue(winfo, v);
+
+                if (nmv != null && nmv.length() > 0) {
+                    nmv  = FLYSUtils.stripNamedMainValue(nmv);
+                    nmv += "=" + String.valueOf(v);
+                    logger.debug("Set named main value '" + nmv + "'");
+
+                    return nmv;
+                }
+            }
+            catch (NumberFormatException nfe) {
+                // do nothing here
+            }
+        }
+
+        return name;
+    }
+
+
+    protected String getCSVRowTitle(WINFOArtifact winfo, WQKms wqkms) {
+        logger.debug("WaterlevelExporter.prepareNamedValue");
+
+        String name = wqkms.getName();
+
+        logger.debug("Name of WQKms = '" + name + "'");
+
+        WQ_MODE wqmode = FLYSUtils.getWQMode(winfo);
+
+        if (wqmode == WQ_MODE.WFREE || wqmode == WQ_MODE.QGAUGE) {
+            return localizeWQKms(winfo, wqkms);
+        }
+
+        Double v = wqkms.getRawValue();
+
+        String nmv = FLYSUtils.getNamedMainValue(winfo, v);
+
+        if (nmv != null && nmv.length() > 0) {
+            nmv = FLYSUtils.stripNamedMainValue(nmv);
+            logger.debug("Set named main value '" + nmv + "'");
+
+            return nmv;
+        }
+
+        return localizeWQKms(winfo, wqkms);
+    }
+
+
+    /**
+     * Get a string like 'W=' or 'Q=' with a number following in localized
+     * format.
+     */
+    protected String localizeWQKms(WINFOArtifact winfo, WQKms wqkms) {
+        WQ_MODE wqmode   = FLYSUtils.getWQMode(winfo);
+        Double  rawValue = wqkms.getRawValue();
+
+        if (rawValue == null) {
+            return wqkms.getName();
+        }
+
+        NumberFormat nf = Formatter.getRawFormatter(context);
+
+        if (wqmode == WQ_MODE.WFREE || wqmode == WQ_MODE.WGAUGE) {
+            return "W=" + nf.format(rawValue);
+        }
+        else {
+            return "Q=" + nf.format(rawValue);
+        }
+    }
+
+
+    @Override
+    protected void writeCSVData(CSVWriter writer) {
+        logger.info("WaterlevelExporter.writeData");
+
+        WQ_MODE mode    = FLYSUtils.getWQMode((FLYSArtifact)master);
+        boolean atGauge = mode == WQ_MODE.QGAUGE || mode == WQ_MODE.WGAUGE;
+        boolean isQ     = mode == WQ_MODE.QGAUGE || mode == WQ_MODE.QFREE;
+        FLYSUtils.WQ_INPUT input
+            = FLYSUtils.getWQInputMode((FLYSArtifact)master);
+
+        writeCSVMeta(writer);
+        writeCSVHeader(writer, atGauge, isQ);
+
+        for (WQKms[] tmp: data) {
+            for (WQKms wqkms: tmp) {
+                wQKms2CSV(writer, wqkms, atGauge, isQ);
+            }
+        }
+    }
+
+
+    protected void writeCSVMeta(CSVWriter writer) {
+        logger.info("WaterlevelExporter.writeCSVMeta");
+
+        // TODO use Access instead of FLYSUtils
+
+        CallMeta meta = context.getMeta();
+
+        FLYSArtifact flys = (FLYSArtifact) master;
+
+        writer.writeNext(new String[] {
+            Resources.getMsg(
+                meta,
+                CSV_META_RESULT,
+                CSV_META_RESULT,
+                new Object[] { FLYSUtils.getRivername(flys) })
+        });
+
+        Locale locale = Resources.getLocale(meta);
+        DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, locale);
+
+        writer.writeNext(new String[] {
+            Resources.getMsg(
+                meta,
+                CSV_META_CREATION,
+                CSV_META_CREATION,
+                new Object[] { df.format(new Date()) })
+        });
+
+        writer.writeNext(new String[] {
+            Resources.getMsg(
+                meta,
+                CSV_META_CALCULATIONBASE,
+                CSV_META_CALCULATIONBASE,
+                new Object[] { "" }) // TODO what is required at this place?
+        });
+
+        writer.writeNext(new String[] {
+            Resources.getMsg(
+                meta,
+                CSV_META_RIVER,
+                CSV_META_RIVER,
+                new Object[] { FLYSUtils.getRivername(flys) })
+        });
+
+        RangeAccess rangeAccess = new RangeAccess(flys, null);
+        double[] kms = rangeAccess.getKmRange();
+        writer.writeNext(new String[] {
+            Resources.getMsg(
+                meta,
+                CSV_META_RANGE,
+                CSV_META_RANGE,
+                new Object[] { kms[0], kms[kms.length-1] })
+        });
+
+        writer.writeNext(new String[] {
+            Resources.getMsg(
+                meta,
+                CSV_META_GAUGE,
+                CSV_META_GAUGE,
+                new Object[] { FLYSUtils.getGaugename(flys) })
+        });
+
+        FLYSUtils.WQ_MODE wq = FLYSUtils.getWQMode(flys);
+        if (wq == FLYSUtils.WQ_MODE.QFREE || wq == FLYSUtils.WQ_MODE.QGAUGE) {
+            double[] qs  = FLYSUtils.getQs(flys);
+            FLYSUtils.WQ_INPUT input = FLYSUtils.getWQInputMode(flys);
+
+            String data = "";
+
+            if ((input == FLYSUtils.WQ_INPUT.ADAPTED ||
+                input == FLYSUtils.WQ_INPUT.RANGE) &&
+                qs != null && qs.length > 0)
+            {
+                data = String.valueOf(qs[0]);
+                data += " - " + String.valueOf(qs[qs.length-1]);
+            }
+            else if (input == FLYSUtils.WQ_INPUT.SINGLE && qs != null){
+                data = String.valueOf(qs[0]);
+                for (int i = 1; i < qs.length; i++) {
+                    data += ", " + String.valueOf(qs[i]);
+                }
+            }
+            else {
+                logger.warn("Could not determine Q range!");
+            }
+
+            writer.writeNext(new String[] {
+                Resources.getMsg(
+                    meta,
+                    CSV_META_Q,
+                    CSV_META_Q,
+                    new Object[] {data})
+            });
+        }
+        else {
+            double[] ws = FLYSUtils.getWs(flys);
+
+            String lower = "";
+            String upper = "";
+
+            if (ws != null && ws.length > 0) {
+                lower = String.valueOf(ws[0]);
+                upper = String.valueOf(ws[ws.length-1]);
+            }
+            else {
+                logger.warn("Could not determine W range!");
+            }
+
+            writer.writeNext(new String[] {
+                Resources.getMsg(
+                    meta,
+                    CSV_META_W,
+                    CSV_META_W,
+                    new Object[] { lower, upper })
+            });
+        }
+
+        writer.writeNext(new String[] { "" });
+    }
+
+
+    /**
+     * Write the header, with different headings depending on whether at a
+     * gauge or at a location.
+     */
+    protected void writeCSVHeader(
+        CSVWriter writer,
+        boolean   atGauge,
+        boolean   isQ
+    ) {
+        logger.info("WaterlevelExporter.writeCSVHeader");
+
+        String unit = FLYSUtils.getRiver((FLYSArtifact) master).getWstUnit().getName();
+
+        if (atGauge) {
+            writer.writeNext(new String[] {
+                msg(CSV_KM_HEADER, DEFAULT_CSV_KM_HEADER),
+                msg(CSV_W_HEADER, DEFAULT_CSV_W_HEADER, new Object[] { unit }),
+                msg(CSV_Q_HEADER, DEFAULT_CSV_Q_HEADER),
+                (isQ
+                    ? msg(CSV_Q_DESC_HEADER, DEFAULT_CSV_Q_DESC_HEADER)
+                    : msg(CSV_W_DESC_HEADER, DEFAULT_CSV_W_DESC_HEADER)),
+                msg(CSV_LOCATION_HEADER, DEFAULT_CSV_LOCATION_HEADER),
+                msg(CSV_GAUGE_HEADER, DEFAULT_CSV_GAUGE_HEADER)
+            });
+        }
+        else {
+            writer.writeNext(new String[] {
+                msg(CSV_KM_HEADER, DEFAULT_CSV_KM_HEADER),
+                    // TODO flys/issue1128 (unit per river)
+                msg(CSV_W_HEADER, DEFAULT_CSV_W_HEADER, new Object[] { unit }),
+                msg(CSV_Q_HEADER, DEFAULT_CSV_Q_HEADER),
+                msg(CSV_LOCATION_HEADER, DEFAULT_CSV_LOCATION_HEADER)
+            });
+        }
+    }
+
+
+    /** Linearly search for gauge which is valid at km. */
+    private static Gauge findGauge(double km, List<Gauge> gauges) {
+        for (Gauge gauge: gauges) {
+            if (gauge.getRange().contains(km)) {
+                return gauge;
+            }
+        }
+        return null;
+    }
+
+    private static Segment findSegment(double km, List<Segment> segments) {
+        for (Segment segment: segments) {
+            if (segment.inside(km)) {
+                return segment;
+            }
+        }
+        return null;
+    }
+
+
+    private void writeRow4(CSVWriter writer, double wqkm[], FLYSArtifact flys) {
+        NumberFormat kmf = getKmFormatter();
+        NumberFormat wf  = getWFormatter();
+        NumberFormat qf  = getQFormatter();
+
+       writer.writeNext(new String[] {
+           kmf.format(wqkm[2]),
+           wf.format(wqkm[0]),
+           qf.format(wqkm[1]),
+           FLYSUtils.getLocationDescription(flys, wqkm[2])
+       });
+    }
+
+
+    /** Write an csv-row at gauge location. */
+    private void writeRow6(CSVWriter writer, double wqkm[], String wOrQDesc,
+        FLYSArtifact flys, String gaugeName) {
+        NumberFormat kmf = getKmFormatter();
+        NumberFormat wf  = getWFormatter();
+        NumberFormat qf  = getQFormatter();
+
+        writer.writeNext(new String[] {
+            kmf.format(wqkm[2]),
+            wf.format(wqkm[0]),
+            qf.format(wqkm[1]),
+            wOrQDesc,
+            FLYSUtils.getLocationDescription(flys, wqkm[2]),
+            gaugeName
+        });
+    }
+
+
+    /**
+     * Write "rows" of csv data from wqkms with writer.
+     */
+    protected void wQKms2CSV(
+        CSVWriter writer,
+        WQKms     wqkms,
+        boolean   atGauge,
+        boolean   isQ
+    ) {
+        logger.debug("WaterlevelExporter.wQKms2CSV");
+
+        // Skip constant data.
+        if (wqkms instanceof ConstantWQKms) {
+            return;
+        }
+
+        NumberFormat kmf = getKmFormatter();
+        NumberFormat wf  = getWFormatter();
+        NumberFormat qf  = getQFormatter();
+
+        int      size   = wqkms.size();
+        double[] result = new double[3];
+
+        FLYSArtifact flys       = (FLYSArtifact) master;
+        List<Gauge>  gauges     = FLYSUtils.getGauges(flys);
+        Gauge        gauge      = FLYSUtils.getGauge(flys);
+        String       gaugeName  = gauge.getName();
+        String       desc       = "";
+        String       notinrange = msg(
+            CSV_NOT_IN_GAUGE_RANGE,
+            DEFAULT_CSV_NOT_IN_GAUGE_RANGE);
+
+        double a = gauge.getRange().getA().doubleValue();
+        double b = gauge.getRange().getB().doubleValue();
+
+        if (flys instanceof WINFOArtifact && isQ) {
+            desc = getCSVRowTitle((WINFOArtifact)flys, wqkms);
+        }
+        else if (!isQ) {
+            Double value = FLYSUtils.getValueFromWQ(wqkms);
+            desc         = value != null
+                ? Formatter.getWaterlevelW(context).format(value) : null;
+        }
+
+        long startTime = System.currentTimeMillis();
+
+        String colDesc = desc;
+        List<Segment> segments = null;
+        boolean isFixRealize = false;
+        if (flys instanceof WINFOArtifact) {
+            if (wqkms != null && wqkms.getRawValue() != null) {
+                WINFOArtifact winfo = (WINFOArtifact) flys;
+                colDesc = FLYSUtils.getNamedMainValue(winfo, wqkms.getRawValue());
+            }
+        }
+        else if (flys instanceof FixationArtifact) {
+            // Get W/Q input per gauge for this case.
+            FixRealizingAccess fixAccess = new FixRealizingAccess(flys, getCallContext());
+            segments = fixAccess.getSegments();
+            if (segments != null && !segments.isEmpty()) {
+                isFixRealize = true;
+            }
+        }
+
+        if (atGauge) { // "At gauge" needs more output.
+
+            // Kms tend to be close together so caching the last sector
+            // is a good time saving heuristic.
+            Segment lastSegment = null;
+            Gauge   lastGauge   = null;
+
+            NumberFormat nf =
+                Formatter.getFormatter(context.getMeta(), 0, 0);
+
+            for (int i = 0; i < size; ++i) {
+                result = wqkms.get(i, result);
+                double km = result[2];
+
+                if (segments != null) {
+                    Segment found = lastSegment != null
+                                    && lastSegment.inside(km)
+                        ? lastSegment
+                        : findSegment(km, segments);
+
+                    if (found != null) {
+                        colDesc = nf.format(found.getValues()[0]);
+                    }
+                    lastSegment = found;
+                }
+
+                String gaugeN;
+                if (isFixRealize) {
+                    Gauge found = lastGauge != null
+                                  && lastGauge.getRange().contains(km)
+                        ? lastGauge
+                        : findGauge(km, gauges);
+
+                    gaugeN = found != null ? found.getName() : notinrange;
+                    lastGauge = found;
+                }
+                else {
+                    // TODO issue1114: Take correct gauge
+                    gaugeN = km >= a && km <= b
+                        ? gaugeName
+                        : notinrange;
+                }
+                writeRow6(writer, result, colDesc, flys, gaugeN);
+            }
+        }
+        else { // Not at gauge.
+            for (int i = 0; i < size; ++i) {
+                result = wqkms.get(i, result);
+                writeRow4(writer, result, flys);
+            }
+        }
+
+        long stopTime = System.currentTimeMillis();
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("Writing CSV took " +
+                (float)(stopTime-startTime)/1000f + " secs.");
+        }
+    }
+
+
+    /**
+     * Generates the output in WST format.
+     */
+    protected void generateWST()
+    throws    IOException
+    {
+        logger.info("WaterlevelExporter.generateWST");
+
+        int cols = data.get(0).length;
+        WstWriter writer = new WstWriter(cols);
+
+        writeWSTData(writer);
+
+        writer.write(out);
+    }
+
+
+    protected void writeWSTData(WstWriter writer) {
+        logger.debug("WaterlevelExporter.writeWSTData");
+
+        double[] result = new double[4];
+
+        for (WQKms[] tmp: data) {
+            for (WQKms wqkms: tmp) {
+                if (wqkms instanceof ConstantWQKms) {
+                    continue;
+                }
+                int size = wqkms != null ? wqkms.size() : 0;
+
+                addWSTColumn(writer, wqkms);
+
+                for (int i = 0; i < size; i++) {
+                    result = wqkms.get(i, result);
+
+                    writer.add(result);
+                }
+
+                if (wqkms instanceof WQCKms) {
+                    addWSTColumn(writer, wqkms);
+
+                    for (int c = 0; c < size; c++) {
+                        result = wqkms.get(c, result);
+
+                        writer.addCorrected(result);
+                    }
+                }
+            }
+        }
+    }
+
+
+    /**
+     * This method is used to register a new column at <i>writer</i>. The name /
+     * title of the column depends on the Q or W value of <i>wqkms</i>. If a Q
+     * was selected and the Q fits to a named main value, the title is set to
+     * the named main value. Otherwise, the name returned by
+     * <i>WQKms.getName()</i> is set.
+     *
+     * @param writer The WstWriter.
+     * @param wqkms The new WST column.
+     */
+    protected void addWSTColumn(WstWriter writer, WQKms wqkms) {
+        if (wqkms instanceof ConstantWQKms) {
+            return;
+        }
+        if (master instanceof WINFOArtifact) {
+            writer.addColumn(getColumnTitle((WINFOArtifact) master, wqkms));
+        }
+        else {
+            writer.addColumn(wqkms.getName());
+        }
+    }
+
+
+    /**
+     *
+     */
+    @Override
+    protected void writePDF(OutputStream out) {
+        logger.debug("write PDF");
+        WKmsJRDataSource source = createJRData();
+
+        String jasperFile = Resources.getMsg(
+                                context.getMeta(),
+                                JASPER_FILE,
+                                "/jasper/waterlevel_en.jasper");
+        String confPath = Config.getConfigDirectory().toString();
+
+
+        Map parameters = new HashMap();
+        parameters.put("ReportTitle", "Exported Data");
+        try {
+            JasperPrint print = JasperFillManager.fillReport(
+                confPath + jasperFile,
+                parameters,
+                source);
+            JasperExportManager.exportReportToPdfStream(print, out);
+        }
+        catch(JRException je) {
+            logger.warn("Error generating PDF Report!", je);
+        }
+    }
+
+    protected WKmsJRDataSource createJRData() {
+        WKmsJRDataSource source = new WKmsJRDataSource();
+
+        WQ_MODE mode    = FLYSUtils.getWQMode((FLYSArtifact)master);
+        boolean atGauge = mode == WQ_MODE.QGAUGE || mode == WQ_MODE.WGAUGE;
+        boolean isQ     = mode == WQ_MODE.QGAUGE || mode == WQ_MODE.QFREE;
+
+        addMetaData(source);
+        for (WQKms[] tmp: data) {
+            for (WQKms wqkms: tmp) {
+                addWKmsData(wqkms, atGauge, isQ, source);
+            }
+        }
+        return source;
+    }
+
+    protected void addMetaData(WKmsJRDataSource source) {
+        CallMeta meta = context.getMeta();
+
+        FLYSArtifact flys = (FLYSArtifact) master;
+
+        source.addMetaData ("river", FLYSUtils.getRivername(flys));
+
+        Locale locale = Resources.getLocale(meta);
+        DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, locale);
+
+        source.addMetaData("date", df.format(new Date()));
+
+        RangeAccess rangeAccess = new RangeAccess(flys, null);
+        double[] kms = rangeAccess.getKmRange();
+        source.addMetaData("range", kms[0] + " - " + kms[kms.length-1]);
+
+        source.addMetaData("gauge", FLYSUtils.getGaugename(flys));
+
+        source.addMetaData("calculation", Resources.getMsg(
+                                            locale,
+                                            PDF_HEADER_MODE,
+                                            "Waterlevel"));
+    }
+
+    protected void addWKmsData(
+        WQKms wqkms,
+        boolean atGauge,
+        boolean isQ,
+        WKmsJRDataSource source)
+    {
+        logger.debug("WaterlevelExporter.addWKmsData");
+
+        // Skip constant data.
+        if (wqkms instanceof ConstantWQKms) {
+            return;
+        }
+
+        NumberFormat kmf = getKmFormatter();
+        NumberFormat wf  = getWFormatter();
+        NumberFormat qf  = getQFormatter();
+
+        int      size   = wqkms.size();
+        double[] result = new double[3];
+
+        FLYSArtifact flys       = (FLYSArtifact) master;
+        Gauge        gauge      = FLYSUtils.getGauge(flys);
+        String       gaugeName  = gauge.getName();
+        String       desc       = "";
+        String       notinrange = msg(
+            CSV_NOT_IN_GAUGE_RANGE,
+            DEFAULT_CSV_NOT_IN_GAUGE_RANGE);
+
+        double a = gauge.getRange().getA().doubleValue();
+        double b = gauge.getRange().getB().doubleValue();
+
+        if (flys instanceof WINFOArtifact && isQ) {
+            desc = getCSVRowTitle((WINFOArtifact)flys, wqkms);
+        }
+        else if (!isQ) {
+            Double value = FLYSUtils.getValueFromWQ(wqkms);
+            desc         = value != null
+                ? Formatter.getWaterlevelW(context).format(value) : null;
+        }
+
+        long startTime = System.currentTimeMillis();
+
+        for (int i = 0; i < size; i ++) {
+            result = wqkms.get(i, result);
+
+            if (atGauge) {
+                source.addData(new String[] {
+                    kmf.format(result[2]),
+                    wf.format(result[0]),
+                    qf.format(result[1]),
+                    desc,
+                    FLYSUtils.getLocationDescription(flys, result[2]),
+                    result[2] >= a && result[2] <= b
+                        ? gaugeName
+                        : notinrange
+                });
+            }
+            else {
+                source.addData(new String[] {
+                    kmf.format(result[2]),
+                    wf.format(result[0]),
+                    qf.format(result[1]),
+                    desc,
+                    FLYSUtils.getLocationDescription(flys, result[2]),
+                    result[2] >= a && result[2] <= b
+                        ? gaugeName
+                        : notinrange
+                });
+            }
+        }
+
+        long stopTime = System.currentTimeMillis();
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("Writing PDF data took " +
+                (float)(stopTime-startTime)/1000f + " secs.");
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/WstWriter.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,234 @@
+package org.dive4elements.river.exports;
+
+import java.io.BufferedWriter;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.river.artifacts.model.WstLine;
+
+
+/**
+ * A writer that creates WSTs.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class WstWriter {
+
+    /** The logger used in this class. */
+    private static Logger logger = Logger.getLogger(WstWriter.class);
+
+    /** The default unit that is written into the header of the WST. */
+    public static final String DEFAULT_UNIT = "Wassserstand [NN + m]";
+
+    /** The lines that need to be included for the export. */
+    protected Map<Double, WstLine> lines;
+
+    /** The column names. */
+    protected List<String> columnNames;
+
+    /** The locale used to format the values. */
+    protected Locale locale;
+
+    /** The number of discharge columns. */
+    protected int cols;
+
+    /** The last Q values. */
+    protected double[] qs;
+
+
+
+    /**
+     * This constructor creates a new WstWriter with a number of Q columns.
+     *
+     * @param cols The number of columns of the resulting WST.
+     */
+    public WstWriter(int cols) {
+        this.columnNames = new ArrayList<String>(cols);
+        this.lines       = new HashMap<Double, WstLine>();
+        this.qs          = new double[cols];
+        this.locale      = Locale.US;
+    }
+
+
+    /**
+     * This method is used to create the WST from the data that has been
+     * inserted using add(double[]) before.
+     * @param out Where to write to.
+     */
+    public void write(OutputStream out) {
+        logger.info("WstWriter.write");
+
+        PrintWriter writer = new PrintWriter(
+            new BufferedWriter(
+                new OutputStreamWriter(out)));
+
+        this.qs = new double[cols];
+
+        writeHeader(writer);
+
+        Collection<WstLine> collection = new TreeMap(lines).values();
+
+        for (WstLine line: collection) {
+            writeWLine(writer, line);
+        }
+
+        writer.flush();
+        writer.close();
+    }
+
+
+    /**
+     * This method is used to add a new line to the WST.
+     *
+     * @param wqkms A 3dim double array with [W,Q, KM].
+     */
+    public void add(double[] wqkms) {
+        Double km = wqkms[2];
+
+        WstLine line = lines.get(km);
+
+        if (line == null) {
+            line = new WstLine(km.doubleValue());
+            lines.put(km, line);
+        }
+
+        line.add(wqkms[0], wqkms[1]);
+    }
+
+
+    public void addCorrected(double[] wqckms) {
+        Double km = wqckms[2];
+
+        WstLine line = lines.get(km);
+
+        if (line == null) {
+            line = new WstLine(km.doubleValue());
+            lines.put(km, line);
+        }
+
+        line.add(wqckms[3], wqckms[1]);
+    }
+
+
+    /**
+     * Adds a further column name.
+     *
+     * @param name The name of the new column.
+     */
+    public void addColumn(String name) {
+        if (name != null) {
+            cols++;
+
+            String basename = name;
+
+            int i = 0;
+            while (columnNames.contains(name)) {
+                name = basename + "_" + i++;
+
+                if (name.length() > 9) {
+                    name = name.substring(name.length() - 9);
+                }
+            }
+
+            columnNames.add(name);
+        }
+    }
+
+
+    /**
+     * This method writes the header of the WST.
+     *
+     * @param writer The PrintWriter that creates the output.
+     */
+    protected void writeHeader(PrintWriter writer) {
+        logger.debug("WstWriter.writeHeader");
+
+        writer.println(cols);
+        writer.print("        ");
+
+        for (String columnName: columnNames) {
+            writer.printf(locale, "%9s", columnName);
+        }
+
+        writer.println();
+
+        writer.write("*   KM     ");
+        writer.write(DEFAULT_UNIT);
+        writer.println();
+    }
+
+
+    /**
+     * This method writes a line with W values and a certain kilometer.
+     *
+     * @param writer The PrintWriter that is used to create the output.
+     * @param line The WstLine that should be written to the output.
+     */
+    protected void writeWLine(PrintWriter writer, WstLine line) {
+        double   km  = line.getKm();
+        double[] qs  = line.getQs();
+        int      num = line.getSize();
+
+        if (dischargesChanged(qs)) {
+            writeQLine(writer, qs);
+        }
+
+        writer.printf(locale, "%8.3f", km);
+
+        for (int i = 0; i < num; i++) {
+            writer.printf(locale, "%9.2f", line.getW(i));
+        }
+
+        writer.println();
+    }
+
+
+    /**
+     * Writes a discharge line (Q values) into a WST.
+     *
+     * @param qs the Q values for the next range.
+     */
+    protected void writeQLine(PrintWriter writer, double[] qs) {
+        writer.write("*\u001f      ");
+
+        for (int i = 0; i < qs.length; i++) {
+            this.qs[i] = qs[i];
+
+            writer.printf(locale, "%9.2f", qs[i]);
+        }
+
+        writer.println();
+    }
+
+
+    /**
+     * This method determines if a Q has changed from the last line to the
+     * current one.
+     *
+     * @param newQs The Q values of the next line.
+     *
+     * @return true, if a Q value have changed, otherwise false.
+     */
+    protected boolean dischargesChanged(double[] newQs) {
+        // XXX maybe there is a way to do this faster
+        for (int i = 0; i < cols; i++) {
+            if (Math.abs(newQs[i] - qs[i]) >= 0.001) {
+                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/flys-artifacts/src/main/java/org/dive4elements/river/exports/XYChartGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,1074 @@
+package org.dive4elements.river.exports;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.text.NumberFormat;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.ImageIcon;
+
+import org.apache.log4j.Logger;
+import org.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.annotations.XYTextAnnotation;
+import org.jfree.chart.axis.NumberAxis;
+import org.jfree.chart.axis.ValueAxis;
+import org.jfree.chart.axis.LogarithmicAxis;
+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.json.JSONArray;
+import org.json.JSONException;
+import org.w3c.dom.Document;
+
+import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
+import org.dive4elements.river.jfree.Bounds;
+import org.dive4elements.river.jfree.CollisionFreeXYTextAnnotation;
+import org.dive4elements.river.jfree.DoubleBounds;
+import org.dive4elements.river.jfree.FLYSAnnotation;
+import org.dive4elements.river.jfree.StyledAreaSeriesCollection;
+import org.dive4elements.river.jfree.StyledXYSeries;
+
+
+/**
+ * An abstract base class for creating XY charts.
+ *
+ * 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>
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+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();
+
+    public static final int AXIS_SPACE = 5;
+
+    /** The logger that is used in this generator. */
+    private static Logger logger = Logger.getLogger(XYChartGenerator.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;
+
+    public XYChartGenerator() {
+        super();
+
+        xBounds  = new HashMap<Integer, Bounds>();
+        yBounds  = new HashMap<Integer, Bounds>();
+    }
+
+
+    /**
+     * Generate the chart anew (including localized axis and all).
+     */
+    @Override
+    public JFreeChart generateChart() {
+        logger.debug("XYChartGenerator.generateChart");
+
+        JFreeChart chart = ChartFactory.createXYLineChart(
+            getChartTitle(),
+            getXAxisLabel(),
+            getYAxisLabel(0),
+            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.
+        addAnnotationsToRenderer(plot);
+
+        // Add a logo (maybe).
+        addLogo(plot);
+
+        aggregateLegendEntries(plot);
+
+        return chart;
+    }
+
+
+    /**
+     * Return left most data points x value (on first axis).
+     * Shortcut, especially to be overridden in (LS) charts where
+     * axis could be inverted.
+     */
+    protected double getLeftX() {
+        return (Double)getXBounds(0).getLower();
+    }
+
+
+    /**
+     * Return right most data points x value (on first axis).
+     * Shortcut, especially to be overridden in (LS) charts where
+     * axis could be inverted.
+     */
+    protected double getRightX() {
+        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:
+         XYChartGenerator.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 = XYChartGenerator.class.getResource("/images/intevation.png");
+        }
+        else { // TODO else if ...
+            imageURL = XYChartGenerator.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 XYAxisDataset for index: " + idx);
+        return new XYAxisDataset(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 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;
+        }
+
+        XYAxisDataset axisDataset = (XYAxisDataset) 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  ('symbolic') index of the series and of its 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);
+
+        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);
+        }
+    }
+
+
+    /**
+     * 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.
+     *
+     * (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);
+    }
+
+
+    /**
+     * 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);
+    }
+
+
+    /**
+     * Do Points out.
+     */
+    protected void doPoints(
+        Object     o,
+        ArtifactAndFacet aandf,
+        Document   theme,
+        boolean    visible,
+        int        axisIndex
+    ) {
+        String seriesName = aandf.getFacetDescription();
+        XYSeries series = new StyledXYSeries(seriesName, theme);
+
+        // Add text annotations for single points.
+        List<XYTextAnnotation> xy = new ArrayList<XYTextAnnotation>();
+
+        try {
+            JSONArray points = new JSONArray((String) o);
+            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.");
+        }
+
+        FLYSAnnotation annotations = new FLYSAnnotation(null, null, null, theme);
+        annotations.setTextAnnotations(xy);
+
+        // Do not generate second legend entry. (null was passed for the aand before).
+        doAnnotations(annotations, null, theme, visible);
+        addAxisSeries(series, axisIndex, visible);
+    }
+
+
+    /**
+     * 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 has been inverted. */
+    public void setInverted(boolean inverted) {
+        this.inverted = inverted;
+    }
+
+
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/extreme/ExtremeWQCurveGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,194 @@
+package org.dive4elements.river.exports.extreme;
+
+import java.awt.Color;
+
+import org.apache.log4j.Logger;
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.plot.Marker;
+import org.jfree.chart.plot.ValueMarker;
+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;
+import org.dive4elements.river.artifacts.access.FixAnalysisAccess;
+import org.dive4elements.river.artifacts.model.DateRange;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.extreme.Curve;
+import org.dive4elements.river.artifacts.model.extreme.ExtremeCurveFacet;
+import org.dive4elements.river.artifacts.resources.Resources;
+import org.dive4elements.river.exports.fixings.FixWQCurveGenerator;
+import org.dive4elements.river.exports.StyledSeriesBuilder;
+import org.dive4elements.river.jfree.JFreeUtil;
+import org.dive4elements.river.jfree.StyledXYSeries;
+
+import org.dive4elements.river.utils.ThemeUtil;
+
+
+/**
+ * Generator for WQ fixing charts.
+ * @author <a href="mailto:christian.lins@intevation.de">Christian Lins</a>
+ */
+public class ExtremeWQCurveGenerator
+extends      FixWQCurveGenerator
+implements   FacetTypes
+{
+    /** Private logger. */
+    private static Logger logger =
+            Logger.getLogger(ExtremeWQCurveGenerator.class);
+
+    public static final String I18N_CHART_TITLE =
+            "chart.extreme.wq.title";
+
+    public static final String I18N_CHART_SUBTITLE =
+            "chart.extreme.wq.subtitle";
+
+    public static final String I18N_CHART_SUBTITLE1 =
+            "chart.extreme.wq.subtitle1";
+
+    public static final String I18N_XAXIS_LABEL =
+            "chart.extreme.wq.xaxis.label";
+
+    public static final String I18N_YAXIS_LABEL =
+            "chart.extreme.wq.yaxis.label";
+
+    public static final String I18N_CHART_TITLE_DEFAULT  =
+            "Extremkurvenanalyse";
+
+    public static final String I18N_XAXIS_LABEL_DEFAULT  =
+            "Q [m\u00B3/s]";
+
+    public static final String I18N_YAXIS_LABEL_DEFAULT  =
+            "W [NN + m]";
+
+
+    @Override
+    public boolean prepareChartData(ArtifactAndFacet aaf, Document theme, boolean visible) {
+        if (!super.prepareChartData(aaf, theme, visible)) {
+            String name = aaf.getFacetName();
+            if (name.equals(EXTREME_WQ_CURVE)) {
+                doExtremeCurveOut(aaf, theme, visible);
+                return true;
+            }
+            else if (name.equals(EXTREME_WQ_CURVE_BASE)) {
+                doExtremeCurveBaseOut(aaf, theme, visible);
+                return true;
+            }
+            return false;
+        }
+        return true;
+    }
+
+    /** Do Extreme Curve nonextrapolated points out. */
+    protected void doExtremeCurveBaseOut(ArtifactAndFacet aaf, Document theme, boolean visible) {
+        logger.debug("doExtremeCurveBaseOut");
+        ExtremeCurveFacet facet = (ExtremeCurveFacet) aaf.getFacet();
+        Curve curve = (Curve) facet.getData(aaf.getArtifact(), context);
+        if (curve == null) {
+            logger.warn("doExtremeCurveBaseOut: Facet does not contain Curve");
+            return;
+        }
+
+        XYSeries qwseries = new StyledXYSeries(aaf.getFacetDescription(), theme);
+        StyledSeriesBuilder.addPointsQW(qwseries, curve.getQs(), curve.getWs());
+
+        addAxisSeries(qwseries, YAXIS.W.idx, visible);
+    }
+
+
+    /** Do Extreme Curve out */
+    protected void doExtremeCurveOut(ArtifactAndFacet aaf, Document theme, boolean visible) {
+        logger.debug("doExtremeCurveOut");
+        ExtremeCurveFacet facet = (ExtremeCurveFacet) aaf.getFacet();
+        Curve curve = (Curve) facet.getData(aaf.getArtifact(), context);
+        if (curve == null) {
+            logger.warn("doExtremeCurveOut: Facet does not contain Curve");
+            return;
+        }
+
+        double maxQ = curve.getSuggestedMaxQ();
+        if (maxQ == Double.MAX_VALUE) {
+            maxQ = 8000;
+        }
+
+        StyledXYSeries series = JFreeUtil.sampleFunction2D(
+                curve,
+                theme,
+                aaf.getFacetDescription(),
+                500,   // number of samples
+                0.0 ,  // start
+                maxQ); // end
+
+        // Add marker from where on its extrapolated.
+        if (ThemeUtil.parseShowExtraMark(theme)) {
+            double[] qs = curve.getQs();
+            double extrapolateFrom = qs[qs.length-1];
+
+            Marker m = new ValueMarker(extrapolateFrom);
+            m.setPaint(Color.black);
+            addDomainMarker(m);
+        }
+
+        addAxisSeries(series, 0, visible);
+    }
+
+
+    @Override
+    protected String getChartTitle() {
+        return Resources.format(
+                context.getMeta(),
+                I18N_CHART_TITLE,
+                I18N_CHART_TITLE_DEFAULT,
+                context.getContextValue(CURRENT_KM));
+    }
+
+
+    @Override
+    protected String getDefaultChartTitle() {
+        return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT);
+    }
+
+    @Override
+    protected String getDefaultChartSubtitle() {
+        FixAnalysisAccess access = new FixAnalysisAccess(artifact, context);
+        DateRange dateRange = access.getDateRange();
+        DateRange refRange  = access.getReferencePeriod();
+
+        if (dateRange != null && refRange != null) {
+            return Resources.format(
+                    context.getMeta(),
+                    I18N_CHART_SUBTITLE,
+                    "",
+                    access.getRiver(),
+                    dateRange.getFrom(),
+                    dateRange.getTo(),
+                    refRange.getFrom(),
+                    refRange.getTo());
+        }
+
+        return null;
+    }
+
+    @Override
+    protected void addSubtitles(JFreeChart chart) {
+        String defaultSubtitle = getDefaultChartSubtitle();
+
+        if (defaultSubtitle == null || defaultSubtitle.length() == 0) {
+            return;
+        }
+
+        chart.addSubtitle(new TextTitle(defaultSubtitle));
+    }
+
+    @Override
+    protected String getDefaultXAxisLabel() {
+        return msg(I18N_XAXIS_LABEL, I18N_XAXIS_LABEL_DEFAULT);
+    }
+
+    @Override
+    protected String getDefaultYAxisLabel(int pos) {
+        return msg(I18N_YAXIS_LABEL, I18N_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/flys-artifacts/src/main/java/org/dive4elements/river/exports/extreme/ExtremeWQCurveInfoGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,15 @@
+package org.dive4elements.river.exports.extreme;
+
+import org.dive4elements.river.exports.ChartInfoGenerator;
+
+/**
+ * A ChartInfoGenerator that generates meta information for specific extreme
+ * analysis W/Q curves.
+ */
+public class ExtremeWQCurveInfoGenerator extends ChartInfoGenerator {
+
+    public ExtremeWQCurveInfoGenerator() {
+        super(new ExtremeWQCurveGenerator());
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/fixings/DeltaWtExporter.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,291 @@
+package org.dive4elements.river.exports.fixings;
+
+import au.com.bytecode.opencsv.CSVWriter;
+
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.river.artifacts.model.CalculationResult;
+
+import org.dive4elements.river.artifacts.model.fixings.AnalysisPeriod;
+import org.dive4elements.river.artifacts.model.fixings.FixAnalysisResult;
+import org.dive4elements.river.artifacts.model.fixings.QWD;
+
+import org.dive4elements.river.artifacts.resources.Resources;
+
+import org.dive4elements.river.exports.AbstractExporter;
+
+import org.dive4elements.river.utils.Formatter;
+import org.dive4elements.river.utils.KMIndex;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import java.text.DateFormat;
+import java.text.MessageFormat;
+import java.text.NumberFormat;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.TreeMap;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+
+/** Exports fixation analysis deltaw(t) computation results to csv. */
+public class DeltaWtExporter
+extends      AbstractExporter
+{
+    /** Private logger. */
+    private static Logger log = Logger.getLogger(DeltaWtExporter.class);
+
+    public static final String CSV_KM_HEADER =
+        "export.fixings.deltawt.csv.header.km";
+
+    public static final String CSV_DELTA_W_HEADER =
+        "export.fixings.deltawt.csv.header.deltaw";
+
+    public static final String CSV_Q_HEADER =
+        "export.fixings.deltawt.csv.header.q";
+
+    public static final String CSV_W_HEADER =
+        "export.fixings.deltawt.csv.header.w";
+
+    public static final String CSV_TRANGE_HEADER =
+        "export.fixings.deltawt.csv.header.time.range";
+
+    public static final String CSV_T_HEADER =
+        "export.fixings.deltawt.csv.header.t";
+
+    public static final String CSV_T_FORMAT =
+        "export.fixings.deltawt.csv.t.format";
+
+    public static final String DEFAULT_CSV_KM_HEADER = "km";
+
+    public static final String DEFAULT_CSV_DELTA_W_HEADER = "\u0394 W [cm]";
+
+    public static final String DEFAULT_CSV_W_HEADER = "Wasserstand [m]";
+
+    public static final String DEFAULT_CSV_Q_HEADER = "Abfluss [m\u00b3/s]";
+
+    public static final String DEFAULT_CSV_T_HEADER = "Datum";
+
+    public static final String DEFAULT_CSV_TRANGE_DESC_HEADER =
+        "Status";
+
+    public static final String CSV_REFERENCE =
+        "export.fixings.deltawt.csv.reference";
+
+    public static final String CSV_ANALYSIS =
+        "export.fixings.deltawt.csv.analysis";
+
+    public static final String DEFAULT_CSV_REFERENCE =
+        "B";
+
+    public static final String DEFAULT_CSV_ANALYSIS =
+        "A{0,number,integer}";
+
+    public static final String DEFAULT_CSV_T_FORMAT =
+        "dd.MM.yyyy";
+
+    protected List<KMIndex<AnalysisPeriod []>> analysisPeriods;
+
+    protected List<KMIndex<QWD[]>> referenceEvents;
+
+    @Override
+    public void init(Document request, OutputStream out, CallContext context) {
+        log.debug("DeltaWtExporter.init");
+        super.init(request, out, context);
+        analysisPeriods = new ArrayList<KMIndex<AnalysisPeriod []>>();
+        referenceEvents = new ArrayList<KMIndex<QWD[]>>();
+    }
+
+    @Override
+    protected void addData(Object d) {
+        log.debug("DeltaWtExporter.addData");
+        if (!(d instanceof CalculationResult)) {
+            log.warn("Invalid data type");
+            return;
+        }
+
+        Object data = ((CalculationResult)d).getData();
+        if (!(data instanceof FixAnalysisResult)) {
+            log.warn("Invalid data stored in result.");
+        }
+        FixAnalysisResult result = (FixAnalysisResult)data;
+        analysisPeriods.add(result.getAnalysisPeriods());
+        referenceEvents.add(result.getReferenced());
+    }
+
+    @Override
+    protected void writeCSVData(CSVWriter writer) throws IOException {
+
+        boolean debug = log.isDebugEnabled();
+
+        writeCSVHeader(writer);
+
+        NumberFormat kmF = getKMFormatter();
+        NumberFormat dwF = getDeltaWFormatter();
+        NumberFormat qF  = getQFormatter();
+        NumberFormat wF  = getWFormatter();
+
+        DateFormat dF = getDateFormatter();
+
+        TreeMap<Double, ArrayList<String []>> sorted =
+            new TreeMap<Double, ArrayList<String []>>();
+
+        String referenceS = getReference();
+
+        for (KMIndex<QWD[]> reference: referenceEvents) {
+
+            for (KMIndex.Entry<QWD[]> kmEntry: reference) {
+
+                Double km = kmEntry.getKm();
+
+                ArrayList<String []> list = sorted.get(km);
+
+                if (list == null) {
+                    list = new ArrayList<String []>();
+                    sorted.put(km, list);
+                }
+
+                String kmS = kmF.format(kmEntry.getKm());
+                for (QWD qwd: kmEntry.getValue()) {
+                    String deltaWS = dwF.format(qwd.getDeltaW());
+                    String qS      = qF.format(qwd.getQ());
+                    String wS      = wF.format(qwd.getW());
+                    String dateS   = dF.format(qwd.getDate());
+
+                    list.add(new String[] {
+                        kmS,
+                        dateS,
+                        qS,
+                        wS,
+                        referenceS,
+                        deltaWS
+                        });
+                }
+            }
+        }
+
+        if (debug) {
+            log.debug("AnalysisPeriods: " + analysisPeriods.size());
+        }
+
+        String analysisTemplate = getAnalysisTemplate();
+
+        for (KMIndex<AnalysisPeriod []> periods: analysisPeriods) {
+
+            for (KMIndex.Entry<AnalysisPeriod []> kmEntry: periods) {
+
+                Double km = kmEntry.getKm();
+
+                ArrayList<String []> list = sorted.get(km);
+
+                if (list == null) {
+                    list = new ArrayList<String []>();
+                    sorted.put(km, list);
+                }
+
+                String kmS = kmF.format(kmEntry.getKm());
+                int analysisCount = 1;
+
+                for (AnalysisPeriod period: kmEntry.getValue()) {
+                    // Typically resulting in A1,A2...
+                    String analyisS = MessageFormat.format(analysisTemplate,
+                        analysisCount);
+                    QWD [] qwds = period.getQWDs();
+
+                    if (qwds != null) {
+                        for (QWD qwd: qwds) {
+                            String deltaWS = dwF.format(qwd.getDeltaW());
+                            String qS      = qF.format(qwd.getQ());
+                            String wS      = wF.format(qwd.getW());
+                            String dateS   = dF.format(qwd.getDate());
+
+                            list.add(new String[] {
+                                kmS,
+                                dateS,
+                                qS,
+                                wS,
+                                analyisS,
+                                deltaWS });
+                        }
+                    }
+                    ++analysisCount;
+                }
+            }
+        }
+
+        for (ArrayList<String []> list: sorted.values()) {
+            for (String [] row: list) {
+                writer.writeNext(row);
+            }
+        }
+
+        writer.flush();
+    }
+
+    /** Template to create "State" strings like A1,A2... */
+    protected String getAnalysisTemplate() {
+        return Resources.getMsg(
+            context.getMeta(),
+            CSV_ANALYSIS, DEFAULT_CSV_ANALYSIS);
+    }
+
+    protected String getReference() {
+        return Resources.getMsg(
+            context.getMeta(),
+            CSV_REFERENCE, DEFAULT_CSV_REFERENCE);
+    }
+
+    protected NumberFormat getKMFormatter() {
+        return Formatter.getFixDeltaWKM(context);
+    }
+
+    protected NumberFormat getDeltaWFormatter() {
+        return Formatter.getFixDeltaWDeltaW(context);
+    }
+
+    protected NumberFormat getQFormatter() {
+        return Formatter.getFixDeltaWQ(context);
+    }
+
+    protected NumberFormat getWFormatter() {
+        return Formatter.getFixDeltaWW(context);
+    }
+
+    protected DateFormat getDateFormatter() {
+        CallMeta meta = context.getMeta();
+        return Formatter.getDateFormatter(
+            meta,
+            Resources.getMsg(
+                meta,
+                CSV_T_FORMAT,
+                DEFAULT_CSV_T_FORMAT));
+    }
+
+    protected void writeCSVHeader(CSVWriter writer) {
+        log.debug("DeltaWtExporter.writeCSVHeader");
+
+        /* issue825
+        km; Ereignis, Abfluss, GEMESSENER Wasserstand; Status (RECHTSBÜNDIG), del W
+        */
+
+        writer.writeNext(new String[] {
+            msg(CSV_KM_HEADER,      DEFAULT_CSV_KM_HEADER),
+            msg(CSV_T_HEADER,       DEFAULT_CSV_T_HEADER),
+            msg(CSV_Q_HEADER,       DEFAULT_CSV_Q_HEADER),
+            msg(CSV_W_HEADER,       DEFAULT_CSV_W_HEADER),
+            msg(CSV_TRANGE_HEADER,  DEFAULT_CSV_TRANGE_DESC_HEADER),
+            msg(CSV_DELTA_W_HEADER, DEFAULT_CSV_DELTA_W_HEADER)
+        });
+    }
+
+    @Override
+    protected void writePDF(OutputStream out) {
+        // TODO: Implement me!
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixATExport.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,100 @@
+package org.dive4elements.river.exports.fixings;
+
+import au.com.bytecode.opencsv.CSVWriter;
+
+import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+import org.dive4elements.river.artifacts.access.FixAccess;
+
+import org.dive4elements.river.artifacts.math.fitting.Function;
+import org.dive4elements.river.artifacts.math.fitting.FunctionFactory;
+
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.Parameters;
+
+import org.dive4elements.river.artifacts.model.fixings.FixResult;
+
+import org.dive4elements.river.exports.AbstractExporter;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.NodeList;
+
+/** Export result of fixation analysis. */
+public class FixATExport extends AbstractExporter {
+
+    /** Private logger. */
+    private static Logger logger =
+        Logger.getLogger(FixATExport.class);
+
+    protected Function function;
+    protected Parameters parameters;
+
+
+    @Override
+    public void doOut(ArtifactAndFacet bundle, Document attr, boolean visible) {
+        logger.debug("AT Export doOut().");
+        Object data = bundle.getData(context);
+        if (data instanceof CalculationResult) {
+            CalculationResult cr = (CalculationResult)data;
+            Object resData = cr.getData();
+            if (resData instanceof FixResult) {
+                this.parameters = ((FixResult)resData).getParameters();
+            }
+        }
+        else {
+            logger.debug("No CalculationResult found for AT export.");
+            return;
+        }
+        FixAccess access = new FixAccess((FLYSArtifact)this.master, context);
+        String f = access.getFunction();
+        if (f == null || f.length() == 0) {
+            logger.debug("No function found for AT export.");
+            return;
+        }
+        this.function = FunctionFactory.getInstance().getFunction(f);
+    }
+
+    @Override
+    public void generate() throws IOException {
+        if (this.function == null || this.parameters == null) {
+            logger.debug("No function or paramters for AT export.");
+            return;
+        }
+
+        Writer writer = new OutputStreamWriter(out, DEFAULT_CSV_CHARSET);
+
+        FixAccess access = new FixAccess((FLYSArtifact)this.master, context);
+        FixATWriter atWriter = new FixATWriter(this.function, this.parameters);
+        NodeList nodes = request.getElementsByTagName("km");
+        String km = nodes.item(0).getTextContent();
+        double dkm = Double.parseDouble(km);
+        String river = access.getRiver();
+        atWriter.write(writer, context.getMeta(), river, dkm);
+        writer.close();
+    }
+
+    @Override
+    protected void writeCSVData(CSVWriter writer) throws IOException {
+        // The concrete writer is used to write csv data.
+    }
+
+    @Override
+    protected void writePDF(OutputStream out) {
+        // Implement me!
+    }
+
+    @Override
+    protected void addData(Object data) {
+        // Nothing to do here.
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixATWriter.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,187 @@
+package org.dive4elements.river.exports.fixings;
+
+import org.dive4elements.artifacts.CallMeta;
+
+import org.dive4elements.river.artifacts.math.fitting.Function;
+
+import org.dive4elements.river.artifacts.model.Parameters;
+
+import org.dive4elements.river.artifacts.resources.Resources;
+
+import org.dive4elements.river.exports.ATWriter;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Writer;
+
+import java.util.Locale;
+
+import org.apache.log4j.Logger;
+
+/** Export Fixation Analysis Results to AT. */
+public class FixATWriter
+{
+    /** Private logger. */
+    private static Logger log = Logger.getLogger(FixATWriter.class);
+
+    public static final String I18N_HEADER_KEY =
+        "fix.export.at.header";
+
+    public static final String I18N_HEADER_DEFAULT =
+        "Exported fixings discharge curve for {0} {0}-km: {1}";
+
+    public static final String [] Q_MAX_COLUMN = new String [] { "max_q" };
+
+    private static final int    MAX_ITERATIONS = 10000;
+    private static final double EPSILON        = 1e-8;
+    private static final double MIN_Q          = 1e-4;
+
+    protected Function   function;
+    protected Parameters parameters;
+
+    public FixATWriter() {
+    }
+
+    public FixATWriter(Function function, Parameters parameters) {
+        this.function   = function;
+        this.parameters = parameters;
+    }
+
+    public void write(
+        Writer   writer,
+        CallMeta meta,
+        String   river,
+        double   km
+    )
+    throws IOException {
+        PrintWriter out = new PrintWriter(writer);
+        printHeader(out, meta, river, km);
+
+        double [] coeffs = parameters.interpolate(
+            "km", km, function.getParameterNames());
+
+        double [] qMax = parameters.interpolate(
+            "km", km, Q_MAX_COLUMN);
+
+        if (coeffs == null || qMax == null) {
+            log.debug("No data found at km " + km + ".");
+            return;
+        }
+
+        org.dive4elements.river.artifacts.math.Function funcInst =
+            function.instantiate(coeffs);
+
+        // Increase Q max about 5%.
+        qMax[0] += Math.abs(qMax[0])*0.05;
+
+        double wMax = funcInst.value(qMax[0]);
+
+        if (Double.isNaN(wMax) || wMax < 0d) {
+            log.debug("function '" + function.getName() +
+                "' eval failed at " + wMax);
+            return;
+        }
+
+        Function inverse = function.getInverse();
+
+        org.dive4elements.river.artifacts.math.Function invInst =
+            inverse.instantiate(coeffs);
+
+        double wMin = minW(invInst, wMax, qMax[0]);
+
+        double wMinCM = wMin * 100d;
+        double wMaxCM = wMax * 100d;
+
+        int wRow = ((int)wMinCM / 10) * 10;
+
+        if ((wMinCM - (int)wMinCM) > 0d) {
+            wMinCM = (int)wMinCM + 1d;
+        }
+
+        double w = wMinCM / 100.0;
+
+        int wcm = ((int)wMinCM) % 10;
+
+        if (log.isDebugEnabled()) {
+            log.debug("wMinCM: " + wMinCM);
+            log.debug("wMaxCM: " + wMaxCM);
+            log.debug("wcm: " + wcm);
+        }
+
+        out.printf(Locale.US, "%8d", wRow);
+
+        if (wcm > 0) {
+            int rest = 10 - wcm;
+            while (rest-- > 0) {
+                out.print(ATWriter.EMPTY);
+            }
+        }
+
+        for (;;) {
+            while (wcm++ < 10) {
+                if (w > wMax) {
+                    break;
+                }
+                double q = invInst.value(w);
+                if (Double.isNaN(w)) {
+                    out.print(ATWriter.EMPTY);
+                }
+                else {
+                    ATWriter.printQ(out, q);
+                }
+                w += 0.01d;
+            }
+            out.println();
+            if (w > wMax) {
+                break;
+            }
+            out.printf(Locale.US, "%8d", wRow += 10);
+            wcm = 0;
+        }
+
+        out.flush();
+    }
+
+    protected void printHeader(
+        PrintWriter out,
+        CallMeta    meta,
+        String      river,
+        double      km
+    ) {
+        out.println(Resources.format(
+            meta,
+            I18N_HEADER_KEY,
+            I18N_HEADER_DEFAULT,
+            river, km));
+    }
+
+    private static double minW(
+        org.dive4elements.river.artifacts.math.Function function,
+        double maxW,
+        double maxQ
+    ) {
+        double stepWidth = 10d;
+
+        double lastW = maxW;
+        double lastQ = maxQ;
+
+        for (int i = 0; i < MAX_ITERATIONS; ++i) {
+            double w = lastW - stepWidth;
+            double q = function.value(w);
+
+            if (Double.isNaN(q) || q > lastQ || q < MIN_Q) {
+                if (stepWidth < EPSILON) {
+                    break;
+                }
+                stepWidth *= 0.5d;
+                continue;
+            }
+
+            lastW = w;
+            lastQ = q;
+        }
+
+        return lastW;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixChartGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,61 @@
+package org.dive4elements.river.exports.fixings;
+
+import org.dive4elements.artifacts.ArtifactNamespaceContext;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+import org.dive4elements.river.exports.XYChartGenerator;
+
+import java.io.OutputStream;
+
+import org.apache.log4j.Logger;
+import org.w3c.dom.Document;
+
+/**
+ * Base class for FixChartGenerator.
+ */
+public abstract class FixChartGenerator
+extends XYChartGenerator
+{
+    /** Private logger. */
+    private static Logger logger = Logger.getLogger(FixChartGenerator.class);
+
+    public static final Double INVALID_KM = Double.valueOf(-1d);
+    public static final String CURRENT_KM = "currentKm";
+    public static final String XPATH_CHART_CURRENTKM =
+        "/art:action/art:attributes/art:currentKm/@art:km";
+
+    @Override
+    public void init(Document request, OutputStream out, CallContext context) {
+        super.init(request, out, context);
+
+        Double currentKm = getCurrentKmFromRequest(request);
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("currentKm = " + currentKm);
+        }
+
+        if (currentKm != INVALID_KM) {
+            context.putContextValue(CURRENT_KM, currentKm);
+        }
+    }
+
+    public static final Double getCurrentKmFromRequest(Document request) {
+
+        String km = XMLUtils.xpathString(
+            request,
+            XPATH_CHART_CURRENTKM,
+            ArtifactNamespaceContext.INSTANCE);
+
+        if (km == null) {
+            return INVALID_KM;
+        }
+
+        try {
+            return Double.valueOf(km);
+        }
+        catch (NumberFormatException nfe) {
+            return INVALID_KM;
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixDeltaWtGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,460 @@
+package org.dive4elements.river.exports.fixings;
+
+import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.access.FixAnalysisAccess;
+import org.dive4elements.river.artifacts.model.DateRange;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.QWDDateRange;
+import org.dive4elements.river.artifacts.model.fixings.QWD;
+import org.dive4elements.river.artifacts.resources.Resources;
+import org.dive4elements.river.exports.TimeseriesChartGenerator;
+import org.dive4elements.river.jfree.CollisionFreeXYTextAnnotation;
+import org.dive4elements.river.jfree.FLYSAnnotation;
+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 java.io.OutputStream;
+import java.text.NumberFormat;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+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.TimeSeries;
+import org.jfree.data.time.TimeSeriesCollection;
+import org.w3c.dom.Document;
+
+
+/**
+ * Generator for Delta W(t) charts.
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class FixDeltaWtGenerator
+extends      TimeseriesChartGenerator
+implements   FacetTypes
+{
+    /** Private logger. */
+    private static Logger logger =
+            Logger.getLogger(FixDeltaWtGenerator.class);
+
+    public static final String I18N_CHART_TITLE =
+            "chart.fix.deltawt.title";
+
+    public static final String I18N_CHART_SUBTITLE =
+            "chart.fix.deltawt.subtitle";
+
+    public static final String I18N_XAXIS_LABEL =
+            "chart.fix.deltawt.xaxis.label";
+
+    public static final String I18N_YAXIS_LABEL =
+            "chart.fix.deltawt.yaxis.label";
+
+    public static final String I18N_YAXIS_SECOND_LABEL =
+            "chart.fix.deltawt.yaxis.second.label";
+
+
+    public static enum YAXIS {
+        dW(0);
+        protected int idx;
+        private YAXIS(int c) {
+            idx = c;
+        }
+    }
+
+
+    private FLYSArtifact artifact = null;
+
+
+    @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
+    protected String getDefaultChartTitle() {
+        return msg(I18N_CHART_TITLE, I18N_CHART_TITLE);
+    }
+
+
+    @Override
+    protected String getChartTitle() {
+        return Resources.format(
+                context.getMeta(),
+                I18N_CHART_TITLE,
+                "",
+                FixChartGenerator
+                .getCurrentKmFromRequest(request).doubleValue());
+    }
+
+
+    @Override
+    protected String getDefaultChartSubtitle() {
+        FixAnalysisAccess access = new FixAnalysisAccess(artifact, context);
+        DateRange dateRange = access.getDateRange();
+        DateRange refRange  = access.getReferencePeriod();
+        return Resources.format(
+                context.getMeta(),
+                I18N_CHART_SUBTITLE,
+                "",
+                access.getRiver(),
+                dateRange.getFrom(),
+                dateRange.getTo(),
+                refRange.getFrom(),
+                refRange.getTo());
+    }
+
+
+    @Override
+    protected String getDefaultXAxisLabel() {
+        return msg(I18N_XAXIS_LABEL, I18N_XAXIS_LABEL);
+    }
+
+    @Override
+    protected String getDefaultYAxisLabel(int pos) {
+        if (pos == 0) {
+            return msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL);
+        }
+        else if (pos == 1) {
+            return msg(I18N_YAXIS_SECOND_LABEL, I18N_YAXIS_SECOND_LABEL);
+        }
+        else {
+            return "NO TITLE FOR Y AXIS: " + pos;
+        }
+    }
+
+
+    @Override
+    public void doOut(
+            ArtifactAndFacet artifactFacet,
+            Document         theme,
+            boolean          visible
+            ) {
+        String name = artifactFacet.getFacetName();
+        logger.debug("FixDeltaWtGenerator.doOut: " + name);
+        logger.debug("Theme description is: " + artifactFacet.getFacetDescription());
+
+        this.artifact = (FLYSArtifact)artifactFacet.getArtifact();
+
+        if (name.contains(FIX_SECTOR_AVERAGE_DWT)) {
+            doSectorAverageOut(
+                    (FLYSArtifact) artifactFacet.getArtifact(),
+                    artifactFacet.getData(context),
+                    artifactFacet.getFacetDescription(),
+                    theme,
+                    visible);
+        }
+        else if (name.equals(FIX_REFERENCE_EVENTS_DWT)) {
+            doReferenceEventsOut(
+                    (FLYSArtifact) artifactFacet.getArtifact(),
+                    artifactFacet.getData(context),
+                    artifactFacet.getFacetDescription(),
+                    theme,
+                    visible);
+        }
+        else if (name.equals(FIX_ANALYSIS_EVENTS_DWT)) {
+            doAnalysisEventsOut(
+                    (FLYSArtifact) artifactFacet.getArtifact(),
+                    artifactFacet.getData(context),
+                    artifactFacet.getFacetDescription(),
+                    theme,
+                    visible);
+        }
+        else if (name.equals(FIX_DEVIATION_DWT)) {
+            doDeviationOut(
+                    (FLYSArtifact) artifactFacet.getArtifact(),
+                    artifactFacet.getData(context),
+                    artifactFacet.getFacetDescription(),
+                    theme,
+                    visible);
+        }
+        else if (name.equals(FIX_ANALYSIS_PERIODS_DWT)) {
+            doAnalysisPeriodsOut(
+                    (FLYSArtifact) artifactFacet.getArtifact(),
+                    artifactFacet.getData(context),
+                    artifactFacet.getFacetDescription(),
+                    theme,
+                    visible);
+        }
+        else if (name.equals(FIX_REFERENCE_PERIOD_DWT)) {
+            doReferencePeriodsOut(
+                    (FLYSArtifact) artifactFacet.getArtifact(),
+                    artifactFacet.getData(context),
+                    artifactFacet.getFacetDescription(),
+                    theme,
+                    visible);
+        }
+        else if (FacetTypes.IS.MANUALPOINTS(name)) {
+            doPoints (artifactFacet.getData(context),
+                    artifactFacet,
+                    theme, visible, YAXIS.dW.idx);
+        }
+        else {
+            logger.warn("doOut(): unknown facet name: " + name);
+            return;
+        }
+    }
+
+
+    protected void doReferencePeriodsOut(
+            FLYSArtifact artifact,
+            Object       data,
+            String       desc,
+            Document     theme,
+            boolean      visible)
+    {
+        logger.debug("doReferencePeriodsOut()");
+
+        if (visible) {
+            FixAnalysisAccess access = new FixAnalysisAccess(artifact, context);
+            DateRange refRange  = access.getReferencePeriod();
+
+            RegularTimePeriod start = new Day(refRange.getFrom());
+            RegularTimePeriod end = new Day(refRange.getTo());
+            StyledDomainMarker marker = new StyledDomainMarker(
+                    start.getMiddleMillisecond(),
+                    end.getMiddleMillisecond(),
+                    theme);
+            domainMarker.add(marker);
+        }
+    }
+
+
+    protected void doSectorAverageOut(
+            FLYSArtifact artifact,
+            Object       data,
+            String       desc,
+            Document     theme,
+            boolean      visible)
+    {
+        logger.debug("doSectorAverageOut(): description = " + desc);
+
+        QWDDateRange qwd = (QWDDateRange) data;
+        TimeSeriesCollection tsc = new TimeSeriesCollection();
+        TimeSeries        series = new StyledTimeSeries(desc, theme);
+
+        if (qwd == null || qwd.qwd == null || qwd.dateRange == null) {
+            return;
+        }
+        RegularTimePeriod rtp = new Day(qwd.qwd.getDate());
+        double value = qwd.qwd.getDeltaW();
+
+        // Draw a line spanning the analysis time.
+        series.add(rtp, value);
+        rtp = new Day(qwd.dateRange.getFrom());
+        series.addOrUpdate(rtp, value);
+        rtp = new Day(qwd.dateRange.getTo());
+        series.addOrUpdate(rtp, value);
+
+        tsc.addSeries(series);
+
+        addAxisDataset(tsc, 0, visible);
+
+        if (visible && ThemeUtil.parseShowLineLabel(theme)) {
+            List<XYTextAnnotation> textAnnos = new ArrayList<XYTextAnnotation>();
+            XYTextAnnotation anno = new CollisionFreeXYTextAnnotation(
+                    "\u0394 W(t) [cm] " + (float)Math.round(qwd.qwd.getDeltaW() * 10000) / 10000,
+                    tsc.getXValue(0, 0),
+                    qwd.qwd.getDeltaW());
+            textAnnos.add(anno);
+
+            FLYSAnnotation flysAnno = new FLYSAnnotation(null, null, null, theme);
+            flysAnno.setTextAnnotations(textAnnos);
+            addAnnotations(flysAnno);
+        }
+    }
+
+
+    protected void doAnalysisEventsOut(
+            FLYSArtifact artifact,
+            Object       data,
+            String       desc,
+            Document     theme,
+            boolean      visible
+            ) {
+        logger.debug("doAnalysisEventsOut: desc = " + desc);
+
+        QWD qwd = (QWD) data;
+        doQWDEventsOut(qwd, desc, theme, visible);
+    }
+
+
+    protected void doQWDEventsOut(QWD qwd, String desc, Document theme, boolean visible)
+    {
+        TimeSeriesCollection tsc = new TimeSeriesCollection();
+
+        TimeSeries   series = new StyledTimeSeries(desc, theme);
+        TimeSeries interpol = new StyledTimeSeries(desc + "interpol", theme);
+
+        if (qwd == null) {
+            logger.debug("doQWDEventsOut: qwd == null");
+            return;
+        }
+
+        Map<Integer, int[]> annoIdxMap = new HashMap<Integer, int[]>();
+
+        int idxInterpol = 0;
+        int idxRegular = 0;
+        RegularTimePeriod rtp = new Day(qwd.getDate());
+        double value =  qwd.getDeltaW();
+        boolean interpolate = qwd.getInterpolated();
+        if (interpolate) {
+            if(interpol.addOrUpdate(rtp, value) == null) {
+                annoIdxMap.put(
+                        0,
+                        new int[]{1, idxInterpol});
+                idxInterpol++;
+            }
+        }
+        else {
+            if(series.addOrUpdate(rtp, value) == null) {
+                annoIdxMap.put(
+                        0,
+                        new int[]{0, idxRegular});
+                idxRegular++;
+            }
+        }
+
+        tsc.addSeries(series);
+        tsc.addSeries(interpol);
+        addAxisDataset(tsc, 0, visible);
+        addAttribute(desc + "interpol", "interpolate");
+        addAttribute(desc, "outline");
+
+        doQWDTextAnnotations(annoIdxMap, tsc, qwd, theme, visible);
+    }
+
+
+    /**
+     * @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,
+            boolean visible) {
+        logger.debug("doQWDTextAnnotation()");
+
+        if (!visible || !ThemeUtil.parseShowPointLabel(theme)) {
+            logger.debug("doQWDTextAnnotation: annotation not visible");
+            return;
+        }
+
+        Locale locale = Resources.getLocale(context.getMeta());
+        NumberFormat nf = NumberFormat.getInstance(locale);
+
+        List<XYTextAnnotation> textAnnos = new ArrayList<XYTextAnnotation>();
+
+        for (int[] idxs: annoIdxMap.values()) {
+
+            double x = tsc.getXValue(idxs[0], idxs[1]);
+
+            XYTextAnnotation anno = new CollisionFreeXYTextAnnotation(
+                    nf.format(qwd.getQ()) + " m\u00B3/s",
+                    x,
+                    qwd.getDeltaW());
+            textAnnos.add(anno);
+        }
+
+        FLYSAnnotation flysAnno = new FLYSAnnotation(null, null, null, theme);
+        flysAnno.setTextAnnotations(textAnnos);
+        addAnnotations(flysAnno);
+    }
+
+
+    protected void doReferenceEventsOut(
+            FLYSArtifact artifact,
+            Object       data,
+            String       desc,
+            Document     theme,
+            boolean      visible
+            ) {
+        logger.debug("doReferenceEventsOut: desc = " + desc);
+
+        QWD qwd = (QWD) data;
+        doQWDEventsOut(qwd, desc, theme, visible);
+    }
+
+
+    protected void doDeviationOut(
+            FLYSArtifact artifact,
+            Object       data,
+            String       desc,
+            Document     theme,
+            boolean      visible
+            ) {
+        logger.debug("doDeviationOut: desc = " + desc);
+
+        if (data == null || !visible) {
+            logger.debug("no standard deviation");
+            return;
+        }
+        double[] value = (double[]) data;
+        StyledDomainMarker lower = new StyledDomainMarker((value[0] * -1), 0, theme);
+        StyledDomainMarker upper = new StyledDomainMarker(0, value[0], theme);
+
+        valueMarker.add(lower);
+        valueMarker.add(upper);
+    }
+
+
+    protected void doAnalysisPeriodsOut(
+            FLYSArtifact artifact,
+            Object       data,
+            String       desc,
+            Document     theme,
+            boolean      visible)
+    {
+        DateRange[] ranges = (DateRange[]) data;
+        if (ranges == null || !visible) {
+            return;
+        }
+        for (int i = 0; i < ranges.length; i++) {
+            logger.debug("creating domain marker");
+            RegularTimePeriod start = new Day(ranges[i].getFrom());
+            RegularTimePeriod end = new Day(ranges[i].getTo());
+            StyledDomainMarker marker =
+                    new StyledDomainMarker(start.getMiddleMillisecond(),
+                            end.getMiddleMillisecond(), theme);
+            marker.useSecondColor(i % 2 == 0);
+            domainMarker.add(marker);
+        }
+        logger.debug("domainmarkers: " + domainMarker.size());
+    }
+
+
+    @Override
+    public void init(Document request, OutputStream out, CallContext context) {
+        super.init(request, out, context);
+
+        Double currentKm = FixChartGenerator.getCurrentKmFromRequest(request);
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("currentKm = " + currentKm);
+        }
+
+        context.putContextValue("currentKm", currentKm);
+
+        StyledValueMarker marker = new StyledValueMarker(0, request);
+        valueMarker.add(marker);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixDeltaWtInfoGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,11 @@
+package org.dive4elements.river.exports.fixings;
+
+import org.dive4elements.river.exports.ChartInfoGenerator;
+
+public class FixDeltaWtInfoGenerator extends ChartInfoGenerator {
+
+    public FixDeltaWtInfoGenerator() {
+        super(new FixDeltaWtGenerator());
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixDerivedCurveGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,138 @@
+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;
+import org.dive4elements.river.artifacts.model.fixings.FixDerivateFacet;
+import org.dive4elements.river.artifacts.model.fixings.FixFunction;
+import org.dive4elements.river.artifacts.resources.Resources;
+import org.dive4elements.river.exports.ChartGenerator;
+import org.dive4elements.river.jfree.JFreeUtil;
+import org.dive4elements.river.jfree.StyledXYSeries;
+
+/**
+ * Generator for fixation derived function curve.
+ *
+ * @author <a href="mailto:christian.lins@intevation.de">Christian Lins</a>
+ */
+public class FixDerivedCurveGenerator
+extends FixChartGenerator
+implements FacetTypes
+{
+    /** Private logger. */
+    private static Logger logger =
+            Logger.getLogger(FixDerivedCurveGenerator.class);
+
+    public static final String I18N_CHART_TITLE =
+            "chart.fixings.derivedcurve.title";
+
+    public static final String I18N_CHART_SUBTITLE =
+            "chart.fixings.derivedcurve.subtitle";
+
+    public static final String I18N_XAXIS_LABEL =
+            "chart.fixings.derivedcurve.xaxis.label";
+
+    public static final String I18N_YAXIS_LABEL =
+            "chart.fixings.derivedcurve.yaxis.label";
+
+    public static final String I18N_CHART_TITLE_DEFAULT =
+            "Ableitungskurve";
+
+    public static final String I18N_XAXIS_LABEL_DEFAULT =
+            "Q [m\u00B3/s]";
+
+    public static final String I18N_YAXIS_LABEL_DEFAULT =
+            "W [NN + m]";
+
+    public static enum YAXIS {
+        W(0),
+        Q(1);
+        public int idx;
+        private YAXIS(int c) {
+            idx = c;
+        }
+    }
+
+
+    @Override
+    public void doOut(ArtifactAndFacet aaf, Document doc, boolean visible) {
+        logger.debug("doOut");
+
+        if (FacetTypes.IS.MANUALPOINTS(aaf.getFacetName())) {
+            doPoints(aaf.getData(context),
+                    aaf,
+                    doc, visible, YAXIS.W.idx);
+        }
+        else {
+            FixDerivateFacet facet = (FixDerivateFacet)aaf.getFacet();
+            FixFunction func = (FixFunction)facet.getData(
+                    aaf.getArtifact(), context);
+
+            if (func == null) {
+                logger.warn("doOut: Facet does not contain FixFunction");
+                return;
+            }
+
+            double maxQ = func.getMaxQ();
+
+            if (maxQ > 0) {
+                StyledXYSeries series = JFreeUtil.sampleFunction2D(
+                        func.getFunction(),
+                        doc,
+                        aaf.getFacetDescription(),
+                        500,   // number of samples
+                        0.0 ,  // start
+                        maxQ); // end
+                addAxisSeries(series, 0, visible);
+            }
+        }
+    }
+
+
+    @Override
+    protected String getDefaultChartTitle() {
+        return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT);
+    }
+
+
+    @Override
+    protected String getChartTitle() {
+        return Resources.format(
+                context.getMeta(),
+                I18N_CHART_TITLE,
+                I18N_CHART_TITLE_DEFAULT,
+                context.getContextValue(CURRENT_KM));
+    }
+
+
+    @Override
+    protected String getDefaultXAxisLabel() {
+        return msg(I18N_XAXIS_LABEL, I18N_XAXIS_LABEL_DEFAULT);
+    }
+
+
+    @Override
+    protected String getDefaultYAxisLabel(int pos) {
+        return msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL_DEFAULT);
+    }
+
+
+    @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 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixDerivedCurveInfoGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,11 @@
+package org.dive4elements.river.exports.fixings;
+
+import org.dive4elements.river.exports.ChartInfoGenerator;
+
+public class FixDerivedCurveInfoGenerator extends ChartInfoGenerator {
+
+    public FixDerivedCurveInfoGenerator() {
+        super(new FixDerivedCurveGenerator());
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixLongitudinalSectionGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,213 @@
+package org.dive4elements.river.exports.fixings;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+
+import org.apache.log4j.Logger;
+import org.jfree.chart.plot.Marker;
+import org.jfree.chart.plot.ValueMarker;
+import org.jfree.data.xy.XYSeries;
+import org.w3c.dom.Document;
+
+import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
+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.ChartGenerator;
+import org.dive4elements.river.exports.process.KMIndexProcessor;
+import org.dive4elements.river.exports.process.Processor;
+import org.dive4elements.river.jfree.FLYSAnnotation;
+import org.dive4elements.river.jfree.StyledAreaSeriesCollection;
+import org.dive4elements.river.jfree.StyledXYSeries;
+import org.dive4elements.river.utils.KMIndex;
+
+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 enum YAXIS {
+        dW(0);
+        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();
+        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 (name.equals(FIX_DEVIATION_LS)) {
+            doReferenceDeviationOut(aaf, doc, visible);
+        }
+        else if (name.equals(LONGITUDINAL_ANNOTATION)) {
+            doAnnotations(
+                    (FLYSAnnotation) 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);
+        }
+    }
+
+    @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) {
+        return msg(I18N_DW_YAXIS_LABEL, I18N_DW_YAXIS_LABEL_DEFAULT);
+    }
+
+    @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 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixLongitudinalSectionInfoGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,11 @@
+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 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixWQCurveGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,620 @@
+package org.dive4elements.river.exports.fixings;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.text.DateFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.annotations.XYTextAnnotation;
+import org.jfree.chart.plot.Marker;
+import org.jfree.chart.plot.ValueMarker;
+import org.jfree.chart.title.TextTitle;
+import org.jfree.data.xy.XYSeries;
+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;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.StaticWKmsArtifact;
+import org.dive4elements.river.artifacts.WINFOArtifact;
+import org.dive4elements.river.artifacts.access.FixAnalysisAccess;
+import org.dive4elements.river.artifacts.model.DateRange;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.NamedDouble;
+import org.dive4elements.river.artifacts.model.QWDDateRange;
+import org.dive4elements.river.artifacts.model.WKms;
+import org.dive4elements.river.artifacts.model.WQKms;
+import org.dive4elements.river.artifacts.model.fixings.FixFunction;
+import org.dive4elements.river.artifacts.model.fixings.FixWQCurveFacet;
+import org.dive4elements.river.artifacts.model.fixings.QWD;
+import org.dive4elements.river.artifacts.model.fixings.QWI;
+import org.dive4elements.river.artifacts.resources.Resources;
+import org.dive4elements.river.exports.ChartGenerator;
+import org.dive4elements.river.exports.StyledSeriesBuilder;
+import org.dive4elements.river.jfree.CollisionFreeXYTextAnnotation;
+import org.dive4elements.river.jfree.FLYSAnnotation;
+import org.dive4elements.river.jfree.JFreeUtil;
+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.utils.FLYSUtils;
+import org.dive4elements.river.utils.ThemeUtil;
+
+/**
+ * Generator for WQ fixing charts.
+ * @author <a href="mailto:christian.lins@intevation.de">Christian Lins</a>
+ */
+public class FixWQCurveGenerator
+extends      FixChartGenerator
+implements   FacetTypes
+{
+    /** Private logger. */
+    private static Logger logger =
+            Logger.getLogger(FixWQCurveGenerator.class);
+
+    public static final String I18N_CHART_TITLE =
+            "chart.fixings.wq.title";
+
+    public static final String I18N_CHART_SUBTITLE =
+            "chart.fixings.wq.subtitle";
+
+    public static final String I18N_CHART_SUBTITLE1 =
+            "chart.fixings.wq.subtitle1";
+
+    public static final String I18N_XAXIS_LABEL =
+            "chart.fixings.wq.xaxis.label";
+
+    public static final String I18N_YAXIS_LABEL =
+            "chart.fixings.wq.yaxis.label";
+
+    public static final String I18N_CHART_TITLE_DEFAULT  =
+            "Fixierungsanalyse";
+
+    public static final String I18N_XAXIS_LABEL_DEFAULT  =
+            "Q [m\u00B3/s]";
+
+    public static final String I18N_YAXIS_LABEL_DEFAULT  =
+            "W [NN + m]";
+
+    public static final double EPSILON = 0.001d;
+
+    public static enum YAXIS {
+        W(0),
+        Q(1);
+        public int idx;
+        private YAXIS(int c) {
+            idx = c;
+        }
+    }
+
+
+    /** Needed to access data to create subtitle. */
+    protected FLYSArtifact artifact;
+
+
+    @Override
+    public void doOut(ArtifactAndFacet aaf, Document doc, boolean visible) {
+        logger.debug("doOut: " + aaf.getFacetName());
+        if (!prepareChartData(aaf, doc, visible)) {
+            logger.warn("Unknown facet, name " + aaf.getFacetName());
+        }
+    }
+
+
+    /** Return true if data could be handled. */
+    public boolean prepareChartData(ArtifactAndFacet aaf, Document doc, boolean visible) {
+        String name = aaf.getFacetName();
+
+        this.artifact = (FLYSArtifact)aaf.getArtifact();
+
+        if(name.startsWith(FIX_SECTOR_AVERAGE_WQ)) {
+            doSectorAverageOut(aaf, doc, visible);
+        }
+        else if(FIX_ANALYSIS_EVENTS_WQ.equals(name)) {
+            doAnalysisEventsOut(aaf, doc, visible);
+        }
+        else if(FIX_REFERENCE_EVENTS_WQ.equals(name)) {
+            doReferenceEventsOut(aaf, doc, visible);
+        }
+        else if(FIX_WQ_CURVE.equals(name)) {
+            doWQCurveOut(aaf, doc, visible);
+        }
+        else if(FIX_OUTLIER.equals(name)) {
+            doOutlierOut(aaf, doc, visible);
+        }
+        else if(QSECTOR.equals(name)) {
+            doQSectorOut(aaf, doc, visible);
+        }
+        else if(FIX_EVENTS.equals(name)) {
+            doEventsOut(aaf, doc, visible);
+        }
+        else if(/*STATIC_WKMS_INTERPOL.equals(name) ||*/
+                STATIC_WKMS_MARKS.equals(name) ||
+                STATIC_WKMS.equals(name) ||
+                HEIGHTMARKS_POINTS.equals(name) ) {
+            doWAnnotations(
+                    aaf.getData(context),
+                    aaf,
+                    doc,
+                    visible);
+        }
+        else if (LONGITUDINAL_W.equals(name) || STATIC_WQ.equals(name)
+                        || STATIC_WKMS_INTERPOL.equals(name)) {
+            doWQOut(aaf.getData(context), aaf, doc, visible);
+        }
+        else if (name.equals(DISCHARGE_CURVE)) {
+            doDischargeOut(
+                    (WINFOArtifact) aaf.getArtifact(),
+                    aaf.getData(context),
+                    aaf.getFacetDescription(),
+                    doc,
+                    visible);
+        }
+        else if (FacetTypes.IS.MANUALPOINTS(aaf.getFacetName())) {
+            doPoints(aaf.getData(context),
+                    aaf,
+                    doc, visible, YAXIS.W.idx);
+        }
+        else {
+            return false;
+        }
+        return true;
+    }
+
+
+    /** Add sector average points to chart */
+    protected void doSectorAverageOut(ArtifactAndFacet aaf, Document doc, boolean visible) {
+        logger.debug("doSectorAverageOut");
+
+        QWDDateRange qwdd = (QWDDateRange) aaf.getData(context);
+        QWD qwd = qwdd != null ? qwdd.getQWD() : null;
+
+        if(qwd != null) {
+            addQWSeries(new QWD[] { qwd }, aaf, doc, visible);
+        }
+        else {
+            logger.debug("doSectorAverageOut: qwd == null");
+        }
+    }
+
+    /** Add analysis event points to chart */
+    protected void doAnalysisEventsOut(ArtifactAndFacet aaf, Document doc, boolean visible) {
+        logger.debug("doAnalysisEventsOut");
+
+        QWD qwd = (QWD)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());
+
+            XYTextAnnotation anno = new CollisionFreeXYTextAnnotation(
+                    dateFormat.format(qwd.getDate()),
+                    qwd.getQ(),
+                    qwd.getW());
+            textAnnos.add(anno);
+
+            addAxisSeries(series, 0, visible);
+            if(visible && ThemeUtil.parseShowPointLabel(doc)) {
+                FLYSAnnotation flysAnno = new FLYSAnnotation(null, null, null, doc);
+                flysAnno.setTextAnnotations(textAnnos);
+                addAnnotations(flysAnno);
+            }
+        }
+        else {
+            logger.debug("doAnalysisEventsOut: qwds == null");
+        }
+    }
+
+
+    /** Add reference event points to chart */
+    protected void doReferenceEventsOut(ArtifactAndFacet aaf, Document 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());
+
+            XYTextAnnotation anno = new CollisionFreeXYTextAnnotation(
+                    dateFormat.format(qwd.getDate()),
+                    qwd.getQ(),
+                    qwd.getW());
+            textAnnos.add(anno);
+
+            addAxisSeries(series, 0, visible);
+            if(visible && ThemeUtil.parseShowPointLabel(doc)) {
+                FLYSAnnotation flysAnno = new FLYSAnnotation(null, null, null, doc);
+                flysAnno.setTextAnnotations(textAnnos);
+                addAnnotations(flysAnno);
+            }
+        }
+        else {
+            logger.debug("doReferenceEventsOut: qwds == null");
+        }
+    }
+
+
+    private void addPointFromWQKms(WQKms wqkms,
+        String title,
+        Document theme,
+        boolean visible
+    ) {
+        XYSeries series = new StyledXYSeries(title, theme);
+        Double ckm = (Double) context.getContextValue(CURRENT_KM);
+        if (wqkms == null || wqkms.getKms().length == 0 || ckm == null) {
+            logger.info("addPointFromWQKms: No event data to show.");
+            return;
+        }
+        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));
+                addAxisSeries(series, YAXIS.W.idx, visible);
+                return;
+            }
+        }
+    }
+
+    protected void doEventsOut(ArtifactAndFacet aaf, Document doc, boolean visible) {
+        logger.debug("doEventsOut");
+        // Find W/Q at km.
+        addPointFromWQKms((WQKms) aaf.getData(context),
+            aaf.getFacetDescription(), doc, visible);
+    }
+
+
+    protected void doWQCurveOut(ArtifactAndFacet aaf, Document doc, boolean visible) {
+        logger.debug("doWQCurveOut");
+
+        FixWQCurveFacet facet = (FixWQCurveFacet)aaf.getFacet();
+        FixFunction func = (FixFunction)facet.getData(
+                aaf.getArtifact(), context);
+
+        if (func == null) {
+            logger.warn("doWQCurveOut: Facet does not contain FixFunction");
+            return;
+        }
+
+        double maxQ = func.getMaxQ();
+
+        if (maxQ > 0) {
+            StyledXYSeries series = JFreeUtil.sampleFunction2D(
+                    func.getFunction(),
+                    doc,
+                    aaf.getFacetDescription(),
+                    500,   // number of samples
+                    0.0 ,  // start
+                    maxQ); // end
+
+            addAxisSeries(series, 0, visible);
+        }
+        else {
+            logger.warn("doWQCurveOut: maxQ <= 0");
+        }
+    }
+
+    protected void doOutlierOut(ArtifactAndFacet aaf, Document doc, boolean visible) {
+        logger.debug("doOutlierOut");
+
+        QWI[] qws = (QWI[])aaf.getData(context);
+        addQWSeries(qws, aaf, doc, visible);
+    }
+
+
+    /** Add markers for q sectors. */
+    protected void doQSectorOut(ArtifactAndFacet aaf, Document theme, boolean visible) {
+        logger.debug("doQSectorOut");
+        if (!visible) {
+            return;
+        }
+
+        Object qsectorsObj = aaf.getData(context);
+        if (qsectorsObj == null || !(qsectorsObj instanceof List)) {
+            logger.warn("No QSectors coming from data.");
+            return;
+        }
+
+        List<?> qsectorsList = (List<?>) qsectorsObj;
+        if (qsectorsList.size() == 0 || !(qsectorsList.get(0) instanceof NamedDouble)) {
+            logger.warn("No QSectors coming from data.");
+            return;
+        }
+
+        @SuppressWarnings("unchecked")
+        List<NamedDouble> qsectors = (List<NamedDouble>) qsectorsList;
+
+        for (NamedDouble qsector : qsectors) {
+            if (Double.isNaN(qsector.getValue())) {
+                continue;
+            }
+            Marker m = new ValueMarker(qsector.getValue());
+            m.setPaint(Color.black);
+
+            float[] dashes = ThemeUtil.parseLineStyle(theme);
+            int size       = ThemeUtil.parseLineWidth(theme);
+            BasicStroke stroke;
+            if (dashes.length <= 1) {
+                stroke = new BasicStroke(size);
+            }
+            else {
+                stroke = new BasicStroke(size,
+                        BasicStroke.CAP_BUTT,
+                        BasicStroke.JOIN_ROUND,
+                        1.0f,
+                        dashes,
+                        0.0f);
+            }
+            m.setStroke(stroke);
+
+            if (ThemeUtil.parseShowLineLabel(theme)) {
+                m.setLabel(qsector.getName());
+                m.setPaint(ThemeUtil.parseTextColor(theme));
+                m.setLabelFont(ThemeUtil.parseTextFont(theme));
+            }
+            Color paint = ThemeUtil.parseLineColorField(theme);
+            if (paint != null) {
+                m.setPaint(paint);
+            }
+            m.setLabelAnchor(RectangleAnchor.TOP_LEFT);
+            m.setLabelTextAnchor(TextAnchor.TOP_LEFT);
+            m.setLabelOffset(new RectangleInsets(5, 5, 10, 10));
+            addDomainMarker(m);
+        }
+    }
+
+
+    /**
+     * Add W-Annotations to plot.
+     * @param wqkms actual data (double[][]).
+     * @param theme theme to use.
+     */
+    protected void doWAnnotations(
+            Object   wqkms,
+            ArtifactAndFacet aandf,
+            Document theme,
+            boolean  visible
+            ) {
+        Facet facet = aandf.getFacet();
+
+        List<StickyAxisAnnotation> xy = new ArrayList<StickyAxisAnnotation>();
+        if (wqkms instanceof double[][]) {
+            logger.debug("Got double[][]");
+            double [][] data = (double [][]) wqkms;
+            for (int i = 0; i< data[0].length; i++) {
+                xy.add(new StickyAxisAnnotation(aandf.getFacetDescription(),
+                        (float) data[1][i], StickyAxisAnnotation.SimpleAxis.Y_AXIS));
+            }
+
+            doAnnotations(new FLYSAnnotation(facet.getDescription(), xy),
+                    aandf, theme, visible);
+        }
+        else {
+            // Assume its WKms.
+            logger.debug("Got WKms");
+            WKms data = (WKms) wqkms;
+
+            Double ckm = (Double) context.getContextValue(CURRENT_KM);
+            double location = (ckm != null)
+                    ? ckm.doubleValue()
+                    : getRange()[0];
+            double w = StaticWKmsArtifact.getWAtKmLin(data, location);
+            xy.add(new StickyAxisAnnotation(aandf.getFacetDescription(),
+                    (float) w, StickyAxisAnnotation.SimpleAxis.Y_AXIS));
+
+            doAnnotations(new FLYSAnnotation(facet.getDescription(), xy),
+                    aandf, theme, visible);
+        }
+    }
+
+
+    /**
+     * Add series with discharge curve to diagram.
+     */
+    protected void doDischargeOut(
+            WINFOArtifact artifact,
+            Object        o,
+            String        description,
+            Document      theme,
+            boolean       visible)
+    {
+        WQKms wqkms = (WQKms) o;
+
+        String gaugeName = wqkms.getName();
+
+        River river = FLYSUtils.getRiver(artifact);
+
+        if (river == null) {
+            logger.debug("no river found");
+            return;
+        }
+
+        Gauge gauge = river.determineGaugeByName(gaugeName);
+
+        if (gauge == null) {
+            logger.debug("no gauge found");
+            return;
+        }
+
+        XYSeries series = new StyledXYSeries(description, theme);
+        StyledSeriesBuilder.addPointsQW(series, wqkms);
+        addAxisSeries(series, YAXIS.W.idx, visible);
+    }
+
+
+    /**
+     * Add WQ Data to plot.
+     * @param wqkms data as double[][]
+     */
+    protected void doWQOut(
+            Object           wqkms,
+            ArtifactAndFacet aaf,
+            Document         theme,
+            boolean          visible
+            ) {
+        logger.debug("FixWQCurveGenerator: doWQOut");
+        if (wqkms instanceof WQKms) {
+            // TODO As in doEventsOut, the value-searching should
+            // be delivered by the facet already (instead of in the Generator).
+            logger.debug("FixWQCurveGenerator: doWQOut: WQKms");
+
+            addPointFromWQKms((WQKms) aaf.getData(context), aaf.getFacetDescription(), theme, visible);
+        }
+        else {
+            logger.debug("FixWQCurveGenerator: doWQOut: double[][]");
+            double [][] data = (double [][]) wqkms;
+
+            XYSeries series = new StyledXYSeries(aaf.getFacetDescription(), theme);
+            StyledSeriesBuilder.addPoints(series, data, true);
+
+            addAxisSeries(series, YAXIS.W.idx, visible);
+        }
+    }
+
+
+    protected void addQWSeries(
+            QWI []           qws,
+            ArtifactAndFacet aaf,
+            Document         theme,
+            boolean          visible
+            ) {
+        if (qws == null) {
+            return;
+        }
+
+        XYSeries series = new StyledXYSeries(aaf.getFacetDescription(), theme);
+        List<XYTextAnnotation> textAnnos =
+                new ArrayList<XYTextAnnotation>(qws.length);
+
+        DateFormat dateFormat = DateFormat.getDateInstance(
+                DateFormat.SHORT);
+
+        for (QWI qw: qws) {
+            series.add(qw.getQ(), qw.getW());
+
+            XYTextAnnotation anno = new CollisionFreeXYTextAnnotation(
+                    dateFormat.format(qw.getDate()),
+                    qw.getQ(),
+                    qw.getW());
+            textAnnos.add(anno);
+        }
+
+        addAxisSeries(series, 0, visible);
+        if (visible && ThemeUtil.parseShowPointLabel(theme)) {
+            FLYSAnnotation flysAnno =
+                    new FLYSAnnotation(null, null, null, theme);
+            flysAnno.setTextAnnotations(textAnnos);
+            addAnnotations(flysAnno);
+        }
+    }
+
+    @Override
+    protected String getChartTitle() {
+        return Resources.format(
+                context.getMeta(),
+                I18N_CHART_TITLE,
+                I18N_CHART_TITLE_DEFAULT,
+                context.getContextValue(CURRENT_KM));
+    }
+
+    @Override
+    protected String getDefaultChartTitle() {
+        return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT);
+    }
+
+    @Override
+    protected String getDefaultChartSubtitle() {
+        FixAnalysisAccess access = new FixAnalysisAccess(artifact, context);
+        DateRange dateRange = access.getDateRange();
+        DateRange refRange  = access.getReferencePeriod();
+
+        if (dateRange != null && refRange != null) {
+            return Resources.format(
+                    context.getMeta(),
+                    I18N_CHART_SUBTITLE,
+                    "",
+                    access.getRiver(),
+                    dateRange.getFrom(),
+                    dateRange.getTo(),
+                    refRange.getFrom(),
+                    refRange.getTo());
+        }
+
+        return null;
+    }
+
+    @Override
+    protected void addSubtitles(JFreeChart chart) {
+        String defaultSubtitle = getDefaultChartSubtitle();
+
+        if (defaultSubtitle == null || defaultSubtitle.length() == 0) {
+            return;
+        }
+
+        chart.addSubtitle(new TextTitle(defaultSubtitle));
+
+        StringBuilder buf = new StringBuilder();
+
+        // Add analysis periods as additional subtitle
+        FixAnalysisAccess access = new FixAnalysisAccess(artifact, context);
+        DateRange[] aperiods = access.getAnalysisPeriods();
+        buf.append(msg("fix.analysis.periods"));
+        buf.append(": ");
+        for(int n = 0; n < aperiods.length; n++) {
+            buf.append(
+                    Resources.format(
+                            context.getMeta(),
+                            I18N_CHART_SUBTITLE1,
+                            "",
+                            aperiods[n].getFrom(),
+                            aperiods[n].getTo()));
+            if(n + 1 < aperiods.length) {
+                buf.append("; ");
+            }
+        }
+
+        chart.addSubtitle(new TextTitle(buf.toString()));
+    }
+
+    @Override
+    protected String getDefaultXAxisLabel() {
+        return msg(I18N_XAXIS_LABEL, I18N_XAXIS_LABEL_DEFAULT);
+    }
+
+    @Override
+    protected String getDefaultYAxisLabel(int pos) {
+        return msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL_DEFAULT);
+    }
+
+    @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 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixWQCurveInfoGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,17 @@
+package org.dive4elements.river.exports.fixings;
+
+import org.dive4elements.river.exports.ChartInfoGenerator;
+
+/**
+ * A ChartInfoGenerator that generates meta information for specific fixing
+ * analysis W/Q curves.
+ *
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class FixWQCurveInfoGenerator extends ChartInfoGenerator {
+
+    public FixWQCurveInfoGenerator() {
+        super(new FixWQCurveGenerator());
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/fixings/ParametersExporter.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,115 @@
+package org.dive4elements.river.exports.fixings;
+
+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;
+
+import org.dive4elements.river.artifacts.model.fixings.FixAnalysisResult;
+
+import org.dive4elements.river.artifacts.resources.Resources;
+
+import org.dive4elements.river.exports.AbstractExporter;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import java.text.NumberFormat;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+
+public class ParametersExporter
+extends      AbstractExporter
+{
+    private static Logger log = Logger.getLogger(ParametersExporter.class);
+
+    protected List<Parameters> parametersList;
+
+    @Override
+    public void init(Document request, OutputStream out, CallContext context) {
+        log.debug("ParametersExporter.init");
+        super.init(request, out, context);
+        parametersList = new ArrayList<Parameters>();
+    }
+
+    @Override
+    protected void addData(Object d) {
+        log.debug("ParametersExporter.addData");
+        if (!(d instanceof CalculationResult)) {
+            log.warn("Invalid data type");
+            return;
+        }
+
+        Object data = ((CalculationResult)d).getData();
+        if (!(data instanceof FixAnalysisResult)) {
+            log.warn("Invalid data stored in result.");
+            return;
+        }
+
+        FixAnalysisResult result = (FixAnalysisResult)data;
+        parametersList.add(result.getParameters());
+    }
+
+    @Override
+    public void generate()
+    throws IOException
+    {
+        log.debug("ParametersExporter.generate");
+
+        if (facet == null) {
+            throw new IOException("invalid (null) facet for exporter");
+        }
+
+        if (facet.equals(FIX_PARAMETERS)) {
+            generateCSV();
+        }
+        else {
+            throw new IOException(
+                "invalid facet for exporter: '" + facet + "'");
+        }
+    }
+
+    @Override
+    protected void writeCSVData(final CSVWriter writer) throws IOException {
+
+        if (parametersList.isEmpty()) {
+            return;
+        }
+
+        Parameters parameters = parametersList.get(0);
+        writer.writeNext(parameters.getColumnNames());
+
+        final int numColumns = parameters.getNumberColumns();
+
+        parameters.visit(new Parameters.Visitor() {
+
+            String [] row = new String[numColumns];
+
+            NumberFormat format = NumberFormat.getInstance(
+                Resources.getLocale(context.getMeta()));
+
+            @Override
+            public void visit(double [] data) {
+                for (int i = 0; i < data.length; ++i) {
+                    row[i] = format.format(data[i]);
+                }
+                writer.writeNext(row);
+            }
+        }, new double[numColumns]);
+
+        writer.flush();
+    }
+
+    @Override
+    protected void writePDF(OutputStream out) {
+        // TODO: Implement me!
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedDiffEpochInfoGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,12 @@
+package org.dive4elements.river.exports.minfo;
+
+import org.dive4elements.river.exports.ChartInfoGenerator;
+
+
+public class BedDiffEpochInfoGenerator
+extends ChartInfoGenerator
+{
+   public BedDiffEpochInfoGenerator() {
+        super(new BedDifferenceEpochGenerator());
+   }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedDiffHeightYearGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,163 @@
+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.BedDiffYearResult;
+import org.dive4elements.river.artifacts.model.minfo.BedDifferencesResult;
+import org.dive4elements.river.exports.StyledSeriesBuilder;
+import org.dive4elements.river.exports.fixings.FixChartGenerator;
+import org.dive4elements.river.exports.process.KMIndexProcessor;
+import org.dive4elements.river.exports.process.Processor;
+import org.dive4elements.river.jfree.Bounds;
+import org.dive4elements.river.jfree.DoubleBounds;
+import org.dive4elements.river.jfree.FLYSAnnotation;
+import org.dive4elements.river.jfree.StyledXYSeries;
+
+
+public class BedDiffHeightYearGenerator
+extends FixChartGenerator
+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 (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) {
+            BedDifferencesResult data = (BedDifferencesResult)bundle.getData(context);
+            context.putContextValue("startkm", data.getKms().min());
+            context.putContextValue("endkm", data.getKms().max());
+        }
+        else if (getXBounds(0) == null && getDomainAxisRange() != null){
+            BedDifferencesResult data = (BedDifferencesResult)bundle.getData(context);
+            Bounds b = new DoubleBounds(data.getKms().min(), data.getKms().max());
+            Bounds bounds =
+                calculateZoom(b, getDomainAxisRange());
+            context.putContextValue("startkm", bounds.getLower());
+            context.putContextValue("endkm", bounds.getUpper());
+        }
+        Processor processor = new KMIndexProcessor();
+        if (name.equals(BED_DIFFERENCE_HEIGHT_YEAR)) {
+            doBedDifferenceYearOut(
+                (BedDiffYearResult) bundle.getData(context),
+                bundle, attr, visible);
+        }
+        else if (name.equals(BED_DIFFERENCE_HEIGHT_YEAR_FILTERED)) {
+            doBedDifferenceYearOut(
+                (BedDiffYearResult) bundle.getData(context),
+                bundle, attr, visible);
+        }
+        else if (name.equals(LONGITUDINAL_ANNOTATION)) {
+            doAnnotations(
+                (FLYSAnnotation) bundle.getData(context),
+                 bundle,
+                 attr,
+                 visible);
+        }
+        else if (processor.canHandle(name)) {
+            processor.doOut(this, bundle, attr, visible, YAXIS.dW.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);
+    }
+
+    @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";
+    }
+
+    protected void doBedDifferenceYearOut(BedDiffYearResult data,
+        ArtifactAndFacet aandf, Document theme, boolean visible) {
+
+        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
+        StyledSeriesBuilder.addPoints(series, data.getHeightPerYearData(), true);
+
+        addAxisSeries(series, YAXIS.D.idx, visible);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedDiffHeightYearInfoGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,12 @@
+package org.dive4elements.river.exports.minfo;
+
+import org.dive4elements.river.exports.ChartInfoGenerator;
+
+
+public class BedDiffHeightYearInfoGenerator
+extends ChartInfoGenerator
+{
+    public BedDiffHeightYearInfoGenerator() {
+        super (new BedDiffHeightYearGenerator());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedDiffYearInfoGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,12 @@
+package org.dive4elements.river.exports.minfo;
+
+import org.dive4elements.river.exports.ChartInfoGenerator;
+
+
+public class BedDiffYearInfoGenerator
+extends ChartInfoGenerator
+{
+    public BedDiffYearInfoGenerator() {
+        super(new BedDifferenceYearGenerator());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedDifferenceEpochGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,249 @@
+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.FLYSArtifact;
+import org.dive4elements.river.artifacts.access.FlowVelocityAccess;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.WKms;
+import org.dive4elements.river.artifacts.model.minfo.BedDiffEpochResult;
+import org.dive4elements.river.exports.StyledSeriesBuilder;
+import org.dive4elements.river.exports.fixings.FixChartGenerator;
+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.Bounds;
+import org.dive4elements.river.jfree.DoubleBounds;
+import org.dive4elements.river.jfree.FLYSAnnotation;
+import org.dive4elements.river.jfree.StyledXYSeries;
+import org.dive4elements.river.utils.DataUtil;
+
+
+public class BedDifferenceEpochGenerator
+extends FixChartGenerator
+implements FacetTypes
+{
+    public enum YAXIS {
+        D(0), H(1), dW(2), W(3);
+
+        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.beddifference.epoch.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.height";
+
+    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 = "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 (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) {
+            FLYSArtifact artifact = (FLYSArtifact)bundle.getArtifact();
+            FlowVelocityAccess access = new FlowVelocityAccess(artifact, context);
+            context.putContextValue("startkm", access.getLowerKM());
+            context.putContextValue("endkm", access.getUpperKM());
+        }
+        else if (getXBounds(0) == null && getDomainAxisRange() != null){
+            FLYSArtifact artifact = (FLYSArtifact)bundle.getArtifact();
+            FlowVelocityAccess access = new FlowVelocityAccess(artifact, context);
+            Bounds b = new DoubleBounds(access.getLowerKM(), access.getUpperKM());
+            Bounds bounds =
+                calculateZoom(b, getDomainAxisRange());
+            context.putContextValue("startkm", bounds.getLower());
+            context.putContextValue("endkm", bounds.getUpper());
+        }
+        Processor processor = new KMIndexProcessor();
+        Processor woutp = new WOutProcessor();
+        if (name.equals(BED_DIFFERENCE_EPOCH)) {
+            doBedDifferenceEpochOut(
+                (BedDiffEpochResult) bundle.getData(context),
+                bundle, attr, visible);
+        }
+        else if (name.equals(BED_DIFFERENCE_EPOCH_HEIGHT1)) {
+            doBedDifferenceHeightsOut((BedDiffEpochResult)bundle.getData(context),
+                bundle, attr, visible, 0);
+        }
+        else if (name.equals(BED_DIFFERENCE_EPOCH_HEIGHT2)) {
+            doBedDifferenceHeightsOut((BedDiffEpochResult)bundle.getData(context),
+                bundle, attr, visible, 1);
+        }
+        else if (name.equals(BED_DIFFERENCE_EPOCH_FILTERED)) {
+            doBedDifferenceEpochOut(
+                (BedDiffEpochResult) bundle.getData(context),
+                bundle, attr, visible);
+        }
+        else if (name.equals(BED_DIFFERENCE_EPOCH_HEIGHT1_FILTERED)) {
+            doBedDifferenceHeightsOut((BedDiffEpochResult)bundle.getData(context),
+                bundle, attr, visible, 0);
+        }
+        else if (name.equals(BED_DIFFERENCE_EPOCH_HEIGHT2_FILTERED)) {
+            doBedDifferenceHeightsOut((BedDiffEpochResult)bundle.getData(context),
+                bundle, attr, visible, 1);
+        }
+        else if (processor.canHandle(name)) {
+            processor.doOut(this, bundle, attr, visible, YAXIS.dW.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 if (name.equals(LONGITUDINAL_ANNOTATION)) {
+            doAnnotations(
+                (FLYSAnnotation) bundle.getData(context),
+                 bundle,
+                 attr,
+                 visible);
+        }
+        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);
+    }
+
+    @Override
+    protected String getDefaultYAxisLabel(int pos) {
+        String label = "default";
+        if (pos == YAXIS.D.idx) {
+            label = msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL_DEFAULT);
+        }
+        else if (pos == YAXIS.H.idx) {
+            label = msg(I18N_SECOND_YAXIS_LABEL, I18N_SECOND_YAXIS_LABEL_DEFAULT);
+        }
+        else if (pos == YAXIS.dW.idx) {
+            return 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);
+        }
+        return label;
+    }
+
+    protected void doBedDifferenceEpochOut(BedDiffEpochResult data,
+        ArtifactAndFacet aandf, Document theme, boolean visible) {
+
+        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
+        StyledSeriesBuilder.addPoints(series, data.getDifferencesData(), true);
+
+        addAxisSeries(series, YAXIS.D.idx, visible);
+    }
+
+    private void doBedDifferenceHeightsOut(
+        BedDiffEpochResult data,
+        ArtifactAndFacet bundle,
+        Document attr,
+        boolean visible,
+        int idx) {
+         logger.debug("doBedDifferenceHeightOut()");
+
+        XYSeries series = new StyledXYSeries(bundle.getFacetDescription(), attr);
+        if (idx == 0) {
+            StyledSeriesBuilder.addPoints(series, data.getHeights1Data(), true);
+        }
+        else {
+            StyledSeriesBuilder.addPoints(series, data.getHeights2Data(), true);
+        }
+
+        addAxisSeries(series, YAXIS.H.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);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedDifferenceYearGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,291 @@
+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.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.fixings.FixChartGenerator;
+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.Bounds;
+import org.dive4elements.river.jfree.DoubleBounds;
+import org.dive4elements.river.jfree.FLYSAnnotation;
+import org.dive4elements.river.jfree.StyledXYSeries;
+import org.dive4elements.river.utils.DataUtil;
+
+
+public class BedDifferenceYearGenerator
+extends FixChartGenerator
+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 (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) {
+            BedDifferencesResult data = (BedDifferencesResult)bundle.getData(context);
+            context.putContextValue("startkm", data.getKms().min());
+            context.putContextValue("endkm", data.getKms().max());
+        }
+        else if (getXBounds(0) == null && getDomainAxisRange() != null){
+            BedDifferencesResult data = (BedDifferencesResult)bundle.getData(context);
+            Bounds b = new DoubleBounds(data.getKms().min(), data.getKms().max());
+            Bounds bounds =
+                calculateZoom(b, getDomainAxisRange());
+            context.putContextValue("startkm", bounds.getLower());
+            context.putContextValue("endkm", bounds.getUpper());
+        }
+        Processor processor = new KMIndexProcessor();
+        Processor woutp = new WOutProcessor();
+        if (name.equals(BED_DIFFERENCE_YEAR)) {
+            doBedDifferenceYearOut(
+                (BedDiffYearResult) bundle.getData(context),
+                bundle, attr, visible);
+        }
+        else if (name.equals(BED_DIFFERENCE_MORPH_WIDTH)) {
+            doBedDifferenceMorphWidthOut(
+                (BedDiffYearResult) bundle.getData(context),
+                bundle, attr, visible);
+        }
+        else if (name.equals(BED_DIFFERENCE_YEAR_HEIGHT1)) {
+            doBedDifferenceHeightsOut(
+                (BedDiffYearResult)bundle.getData(context),
+                bundle, attr, visible, 0);
+        }
+        else if (name.equals(BED_DIFFERENCE_YEAR_HEIGHT2)) {
+            doBedDifferenceHeightsOut(
+                (BedDiffYearResult)bundle.getData(context),
+                bundle, attr, visible, 1);
+        }
+        else if (name.equals(BED_DIFFERENCE_YEAR_FILTERED)) {
+            doBedDifferenceYearOut(
+                (BedDiffYearResult) bundle.getData(context),
+                bundle, attr, visible);
+        }
+        else if (name.equals(BED_DIFFERENCE_YEAR_HEIGHT1_FILTERED)) {
+            doBedDifferenceHeightsOut(
+                (BedDiffYearResult)bundle.getData(context),
+                bundle, attr, visible, 0);
+        }
+        else if (name.equals(BED_DIFFERENCE_YEAR_HEIGHT2_FILTERED)) {
+            doBedDifferenceHeightsOut(
+                (BedDiffYearResult)bundle.getData(context),
+                bundle, attr, visible, 1);
+        }
+        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(
+                (FLYSAnnotation) bundle.getData(context),
+                 bundle,
+                 attr,
+                 visible);
+        }
+        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);
+    }
+
+    private void doBedDifferenceHeightsOut(
+        BedDiffYearResult data,
+        ArtifactAndFacet bundle,
+        Document attr,
+        boolean visible,
+        int idx) {
+         logger.debug("doBedDifferenceYearOut()");
+
+        XYSeries series = new StyledXYSeries(bundle.getFacetDescription(), attr);
+        if (idx == 0) {
+            StyledSeriesBuilder.addPoints(series, data.getHeights1Data(), true);
+        }
+        else {
+            StyledSeriesBuilder.addPoints(series, data.getHeights2Data(), true);
+        }
+
+        addAxisSeries(series, YAXIS.H.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);
+    }
+
+    @Override
+    protected String getDefaultYAxisLabel(int pos) {
+        String label = "default";
+        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);
+        }
+        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);
+        }
+
+        return label;
+    }
+
+    protected void doBedDifferenceYearOut(BedDiffYearResult data,
+        ArtifactAndFacet aandf, Document theme, boolean visible) {
+        logger.debug("doBedDifferenceYearOut()");
+
+        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
+        StyledSeriesBuilder.addPoints(series, data.getDifferencesData(), true);
+
+        addAxisSeries(series, YAXIS.D.idx, visible);
+    }
+
+    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);
+        }
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedQualityExporter.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,216 @@
+package org.dive4elements.river.exports.minfo;
+
+import gnu.trove.TDoubleArrayList;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.text.DateFormat;
+import java.text.NumberFormat;
+import java.util.Arrays;
+import java.util.LinkedList;
+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;
+import org.dive4elements.river.artifacts.model.minfo.BedQualityResult;
+import org.dive4elements.river.artifacts.model.minfo.BedloadDiameterResult;
+import org.dive4elements.river.exports.AbstractExporter;
+import org.dive4elements.river.utils.Formatter;
+
+
+public class BedQualityExporter
+extends AbstractExporter
+{
+    /** Private logger. */
+    private static Logger logger = Logger.getLogger(BedQualityExporter.class);
+
+    private static final String CSV_HEADER_KM = "export.minfo.bedquality.km";
+    private static final String CSV_HEADER_DENSITY_CAP =
+        "export.minfo.bedquality.density_cap";
+    private static final String CSV_HEADER_DENSITY_SUB =
+        "export.minfo.bedquality.density_sub";
+    private static final String CSV_HEADER_POROSITY_CAP =
+        "export.minfo.bedquality.porosity_cap";
+    private static final String CSV_HEADER_POROSITY_SUB =
+        "export.minfo.bedquality.porosity_sub";
+    private static final String CSV_HEADER_BEDLOAD =
+        "export.minfo.bedquality.bedload";
+    private static final String CSV_HEADER_BED_CAP =
+        "export.minfo.bedquality.bed_cap";
+    private static final String CSV_HEADER_BED_SUB =
+        "export.minfo.bedquality.bed_sub";
+
+    private BedQualityResult[] results;
+
+    @Override
+    public void init(Document request, OutputStream out, CallContext context) {
+        logger.debug("BedQualityExporter.init");
+        super.init(request, out, context);
+        results = new BedQualityResult[0];
+    }
+
+    @Override
+    protected void writeCSVData(CSVWriter writer) throws IOException {
+        // TODO Auto-generated method stub
+        writeCSVHeader(writer);
+
+        NumberFormat kmf = Formatter.getCalculationKm(context.getMeta());
+
+        TDoubleArrayList kms = new TDoubleArrayList();
+        int cols = 1;
+        for (int i = 0; i < results.length; i++) {
+            BedDiameterResult[] beds = results[i].getBedResults();
+            for (int j = 0; j < beds.length; j++) {
+                TDoubleArrayList bkms = beds[j].getKms();
+                for (int k = 0; k < bkms.size(); k++) {
+                    if (!kms.contains(bkms.get(k))) {
+                        kms.add(bkms.get(k));
+                    }
+                }
+            }
+            BedloadDiameterResult[] loads = results[i].getBedloadResults();
+            for (int j = 0; j < loads.length; j++) {
+                TDoubleArrayList lkms = loads[i].getKms();
+                for (int k = 0; k < lkms.size(); k++) {
+                    if (!kms.contains(lkms.get(k))) {
+                        kms.add(lkms.get(k));
+                    }
+                }
+            }
+            cols += beds.length * 2;
+            cols += loads.length;
+            if (beds.length > 0) {
+                cols += 4;
+            }
+        }
+
+        kms.sort();
+        List<double[]> rows = new LinkedList<double[]>();
+        for (int i = 0; i < kms.size(); i++) {
+            double[] row = new double[cols];
+            double km = kms.get(i);
+            row[0] = km;
+            for (int j = 0; j < results.length; j++) {
+                BedloadDiameterResult[] loads = results[j].getBedloadResults();
+
+                for(int k = 0; k < loads.length; k++) {
+                    // k + 1: shift km column.
+                    // j* loads.length: shift periods.
+                    row[(k + 1) + (j * loads.length)] =
+                        loads[k].getDiameter(km);
+                }
+                BedDiameterResult[] beds = results[j].getBedResults();
+                for (int k = 0; k < beds.length; k++) {
+                    // k + 1: shift km column.
+                    // j * beds.length: shift periods.
+                    // loads.length * results.length: shift bed load columns.
+                    int ndx = (k + 1) + (j * beds.length) + (loads.length * results.length);
+                    row[ndx] = beds[k].getDiameterCap(km);
+                    row[ndx + 1] = beds[k].getDiameterSub(km);
+                }
+                BedParametersResult[] params = results[j].getParameters();
+                for(int k = 0; k < params.length; k++) {
+                    // loads.length + (beds.lenght * 2) * (j + 1): shift bed and bedload columns.
+                    int ndx = 1 + (loads.length + (beds.length * 2) * (j + 1));
+                    row[ndx] = params[k].getLoadDensityCap(km);
+                    row[ndx + 1] = params[k].getLoadDensitySub(km);
+                    row[ndx + 2] = params[k].getPorosityCap(km);
+                    row[ndx + 3] = params[k].getPorositySub(km);
+                }
+            }
+            rows.add(row);
+        }
+        for (double[] d : rows) {
+            logger.debug(Arrays.toString(d));
+            List<String> cells = new LinkedList<String>();
+            for (int i = 0; i < d.length; i++) {
+                if (!Double.isNaN(d[i])) {
+                    NumberFormat nf = Formatter.getFormatter(context, 1, 3);
+                    cells.add(nf.format(d[i]));
+                }
+                else {
+                    cells.add("");
+                }
+            }
+            writer.writeNext(cells.toArray(new String[cells.size()]));
+        }
+    }
+
+    @Override
+    protected void writePDF(OutputStream out) {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    protected void addData(Object data) {
+        // TODO Auto-generated method stub
+        logger.debug("addData()");
+        if (!(data instanceof CalculationResult)) {
+            logger.warn("Invalid data type.");
+            return;
+        }
+        Object[] d = (Object[])((CalculationResult)data).getData();
+
+        if (!(d instanceof BedQualityResult[])) {
+            logger.warn("Invalid result object.");
+            return;
+        }
+        results = (BedQualityResult[])d;
+    }
+
+    protected void writeCSVHeader(CSVWriter writer) {
+        logger.debug("writeCSVHeader()");
+
+        List<String> header = new LinkedList<String>();
+        if (results != null)  {
+            header.add(msg(CSV_HEADER_KM, "km"));
+            for (int i = 0; i < results.length; i++) {
+                DateFormat df = Formatter.getDateFormatter(context.getMeta(), "dd.MM.yyyy");
+                String d1 = df.format(results[i].getDateRange().getFrom());
+                String d2 = df.format(results[i].getDateRange().getTo());
+                BedloadDiameterResult[] loads = results[i].getBedloadResults();
+                BedDiameterResult[] beds = results[i].getBedResults();
+                BedParametersResult[] params = results[i].getParameters();
+                for (int j = 0; j < loads.length; j++) {
+                    header.add(msg(CSV_HEADER_BEDLOAD, CSV_HEADER_BEDLOAD) +
+                        " - " +
+                        msg(loads[j].getType().toString(),
+                            loads[j].getType().toString()) + " - " +
+                        d1 + "-" + d2);
+                }
+                for (int j = 0; j < beds.length; j++) {
+                    header.add(msg(CSV_HEADER_BED_CAP, CSV_HEADER_BED_CAP) + " - " +
+                        msg(beds[j].getType().toString(),
+                            beds[j].getType().toString()) + " - " +
+                        d1 + "-" + d2);
+                    header.add(msg(CSV_HEADER_BED_SUB, CSV_HEADER_BED_SUB) + " - " +
+                        msg(beds[j].getType().toString(),
+                            beds[j].getType().toString()) + " - " +
+                        d1 + "-" + d2);
+                }
+                if (params.length > 0) {
+                    header.add(
+                        msg(CSV_HEADER_DENSITY_CAP, CSV_HEADER_DENSITY_CAP) +
+                        " - " + d1 + "-" + d2);
+                    header.add(
+                        msg(CSV_HEADER_DENSITY_SUB, CSV_HEADER_DENSITY_SUB) +
+                        " - " + d1 + "-" + d2);
+                    header.add(
+                        msg(CSV_HEADER_POROSITY_CAP, CSV_HEADER_POROSITY_CAP) +
+                        " - " + d1 + "-" + d2);
+                    header.add(
+                        msg(CSV_HEADER_POROSITY_SUB, CSV_HEADER_POROSITY_SUB) +
+                        " - " + d1 + "-" + d2);
+                }
+            }
+        }
+        writer.writeNext(header.toArray(new String[header.size()]));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedQualityGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,281 @@
+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.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.FLYSAnnotation;
+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);
+    }
+
+    @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(LONGITUDINAL_ANNOTATION)) {
+            doAnnotations(
+                (FLYSAnnotation) 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;
+        }
+    }
+
+    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 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedQualityInfoGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,18 @@
+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 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/minfo/SedimentLoadLSGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,325 @@
+package org.dive4elements.river.exports.minfo;
+
+import java.util.Arrays;
+
+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.FLYSArtifact;
+import org.dive4elements.river.artifacts.access.RangeAccess;
+import org.dive4elements.river.artifacts.access.SedimentLoadAccess;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.FlowVelocityData;
+import org.dive4elements.river.artifacts.model.WKms;
+import org.dive4elements.river.artifacts.model.minfo.BedDiffEpochResult;
+import org.dive4elements.river.artifacts.model.minfo.BedDiffYearResult;
+import org.dive4elements.river.exports.StyledSeriesBuilder;
+import org.dive4elements.river.exports.XYChartGenerator;
+import org.dive4elements.river.jfree.Bounds;
+import org.dive4elements.river.jfree.DoubleBounds;
+import org.dive4elements.river.jfree.FLYSAnnotation;
+import org.dive4elements.river.jfree.StyledXYSeries;
+import org.dive4elements.river.utils.DataUtil;
+
+
+public class SedimentLoadLSGenerator
+extends XYChartGenerator
+implements FacetTypes
+{
+    public enum YAXIS {
+        L(0),
+        D(1),
+        DW(2),
+        V(3);
+
+        protected int idx;
+
+        private YAXIS(int c) {
+            idx = c;
+        }
+    }
+    /** The logger that is used in this generator. */
+    private static Logger logger = Logger.getLogger(SedimentLoadLSGenerator.class);
+
+    public static final String I18N_CHART_TITLE = "chart.sedimentload.ls.title";
+    public static final String I18N_XAXIS_LABEL = "chart.sedimentload.ls.xaxis.label";
+    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_D_LABEL = "chart.beddifference.yaxis.label.diff";
+    public static final String I18N_YAXIS_V_LABEL =
+        "chart.flow_velocity.section.yaxis.label";
+    public final static String I18N_WDIFF_YAXIS_LABEL =
+        "chart.w_differences.yaxis.label";
+
+    public final static String I18N_WDIFF_YAXIS_LABEL_DEFAULT = "m";
+    public static final String I18N_CHART_TITLE_DEFAULT = "Sedimentfracht";
+    public static final String I18N_XAXIS_LABEL_DEFAULT = "Fluss-Km";
+    public static final String I18N_YAXIS_LABEL_DEFAULT_1 = "[t/a]";
+    public static final String I18N_YAXIS_LABEL_DEFAULT_2 = "[m\u00b3/a]";
+    public static final String I18N_YAXIS_D_LABEL_DEFAULT = "delta S [m]";
+    public static final String I18N_YAXIS_V_LABEL_DEFAULT = "Geschwindigkeit v [m/s]";
+
+    private FLYSArtifact artifact;
+
+    @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();
+        artifact = (FLYSArtifact)bundle.getArtifact();
+
+        if (facet == null) {
+            return;
+        }
+        if (getXBounds(0) != null && getDomainAxisRange() != null) {
+            logger.debug(Arrays.toString(getDomainAxisRangeFromRequest()));
+            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) {
+            FLYSArtifact artifact = (FLYSArtifact)bundle.getArtifact();
+            RangeAccess access = new RangeAccess(artifact, context);
+            context.putContextValue("startkm", access.getFrom());
+            context.putContextValue("endkm", access.getTo());
+        }
+        else if (getXBounds(0) == null && getDomainAxisRange() != null){
+            FLYSArtifact artifact = (FLYSArtifact)bundle.getArtifact();
+            RangeAccess access = new RangeAccess(artifact, context);
+            Bounds b = new DoubleBounds(access.getFrom(), access.getTo());
+            Bounds bounds =
+                calculateZoom(b, getDomainAxisRange());
+            context.putContextValue("startkm", bounds.getLower());
+            context.putContextValue("endkm", bounds.getUpper());
+        }
+        if (FacetTypes.IS.SEDIMENT_LOAD(SEDIMENT_LOAD_COARSE)) {
+            doSedimentLoadOut(
+                (double[][]) bundle.getData(context),
+                bundle,
+                attr,
+                visible);
+        }
+        else if (name.equals(FLOW_VELOCITY_TOTALCHANNEL)) {
+            doFlowVelocityTotalOut(
+                (FlowVelocityData) bundle.getData(context),
+                bundle,
+                attr,
+                visible);
+        }
+        else if (name.equals(FLOW_VELOCITY_TOTALCHANNEL_FILTERED)) {
+            doFlowVelocityTotalOut(
+                (FlowVelocityData) bundle.getData(context),
+                bundle,
+                attr,
+                visible);
+        }
+        else if (name.equals(FLOW_VELOCITY_MAINCHANNEL)) {
+            doFlowVelocityMainOut(
+                (FlowVelocityData) bundle.getData(context),
+                bundle,
+                attr,
+                visible);
+        }
+        else if (name.equals(FLOW_VELOCITY_MAINCHANNEL_FILTERED)) {
+            doFlowVelocityMainOut(
+                (FlowVelocityData) bundle.getData(context),
+                bundle,
+                attr,
+                visible);
+        }
+        else if (name.equals(BED_DIFFERENCE_YEAR)) {
+            doBedDifferenceYearOut(
+                (BedDiffYearResult) bundle.getData(context),
+                bundle,
+                attr,
+                visible);
+        }
+        else if (name.equals(BED_DIFFERENCE_YEAR_FILTERED)) {
+            doBedDifferenceYearOut(
+                (BedDiffYearResult) bundle.getData(context),
+                bundle,
+                attr,
+                visible);
+        }
+        else if (name.equals(BED_DIFFERENCE_EPOCH)) {
+            doBedDifferenceEpochOut(
+                (BedDiffEpochResult) bundle.getData(context),
+                bundle,
+                attr,
+                visible);
+        }
+        else if (name.equals(W_DIFFERENCES)) {
+            doWDifferencesOut(
+                (WKms) bundle.getData(context),
+                bundle,
+                attr,
+                visible);
+        }
+        else if (name.equals(LONGITUDINAL_ANNOTATION)) {
+            doAnnotations(
+                (FLYSAnnotation) bundle.getData(context),
+                 bundle,
+                 attr,
+                 visible);
+        }
+        else if (FacetTypes.IS.MANUALPOINTS(name)) {
+            doPoints(
+                bundle.getData(context),
+                bundle,
+                attr,
+                visible,
+                YAXIS.L.idx);
+        }
+    }
+
+    @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) {
+        String label = "default";
+        if (pos == YAXIS.L.idx) {
+            SedimentLoadAccess access = new SedimentLoadAccess(artifact, context);
+            if (access.getUnit().equals("m3_per_a")) {
+                label = msg(I18N_YAXIS_LABEL_2, I18N_YAXIS_LABEL_DEFAULT_2);
+            }
+            else {
+                label = msg(I18N_YAXIS_LABEL_1, I18N_YAXIS_LABEL_DEFAULT_1);
+            }
+        }
+        else if (pos == YAXIS.V.idx) {
+            label = msg(I18N_YAXIS_V_LABEL, I18N_YAXIS_V_LABEL_DEFAULT);
+        }
+        else if (pos == YAXIS.D.idx) {
+            label = msg(I18N_YAXIS_D_LABEL, I18N_YAXIS_D_LABEL_DEFAULT);
+        }
+        else if (pos == YAXIS.DW.idx) {
+            label = msg(I18N_WDIFF_YAXIS_LABEL, I18N_WDIFF_YAXIS_LABEL_DEFAULT);
+        }
+
+        return label;
+    }
+
+    protected void doSedimentLoadOut(double[][] data,
+        ArtifactAndFacet aandf, Document theme, boolean visible) {
+
+        // Allow for gaps (NaNs).
+        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), false, theme);
+        StyledSeriesBuilder.addPoints(series, data, false);
+
+        addAxisSeries(series, YAXIS.L.idx, visible);
+    }
+
+    protected void doFlowVelocityMainOut(
+        FlowVelocityData data,
+        ArtifactAndFacet aandf,
+        Document         theme,
+        boolean          visible
+    ) {
+        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
+        StyledSeriesBuilder.addPoints(series, data.getMainChannelPoints(), true);
+
+        addAxisSeries(series, YAXIS.V.idx, visible);
+    }
+
+    protected void doFlowVelocityTotalOut(
+        FlowVelocityData data,
+        ArtifactAndFacet aandf,
+        Document         theme,
+        boolean          visible
+    ) {
+        if (data == null) {
+            logger.warn("No data to add to FlowVelocity chart.");
+            return;
+         }
+
+        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
+        StyledSeriesBuilder.addPoints(series, data.getTotalChannelPoints(), true);
+
+        addAxisSeries(series, YAXIS.V.idx, visible);
+    }
+
+    protected void doBedDifferenceYearOut(
+        BedDiffYearResult data,
+        ArtifactAndFacet aandf,
+        Document theme,
+        boolean visible
+    ) {
+        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
+        StyledSeriesBuilder.addPoints(series, data.getDifferencesData(), true);
+
+        addAxisSeries(series, YAXIS.D.idx, visible);
+    }
+
+    protected void doBedDifferenceEpochOut(
+        BedDiffEpochResult data,
+        ArtifactAndFacet aandf,
+        Document theme,
+        boolean visible
+    ) {
+        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
+        StyledSeriesBuilder.addPoints(series, data.getDifferencesData(), true);
+
+        addAxisSeries(series, YAXIS.D.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);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/minfo/SedimentLoadLSInfoGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,12 @@
+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/flys-artifacts/src/main/java/org/dive4elements/river/exports/process/BedheightProcessor.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,79 @@
+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/flys-artifacts/src/main/java/org/dive4elements/river/exports/process/KMIndexProcessor.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,142 @@
+package org.dive4elements.river.exports.process;
+
+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;
+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.XYChartGenerator;
+import org.dive4elements.river.jfree.StyledXYSeries;
+import org.dive4elements.river.utils.KMIndex;
+
+public class KMIndexProcessor implements Processor {
+
+    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();
+        if (facettype.contains(FacetTypes.FIX_SECTOR_AVERAGE_LS)) {
+            doSectorAverageOut(generator, aandf, theme, visible, index);
+        }
+        else if (facettype.equals(FacetTypes.FIX_REFERENCE_EVENTS_LS)) {
+            doReferenceEventsOut(generator, aandf, theme, visible, index);
+        }
+        else if (facettype.equals(FacetTypes.FIX_ANALYSIS_EVENTS_LS)) {
+            doAnalysisEventsOut(generator, aandf, theme, visible, index);
+        }
+
+    }
+
+    @Override
+    public boolean canHandle(String facettype) {
+        if (facettype == null) {
+            return false;
+        }
+
+        if (facettype.contains(FacetTypes.FIX_SECTOR_AVERAGE_LS)
+                || facettype.equals(FacetTypes.FIX_REFERENCE_EVENTS_LS)
+                || facettype.equals(FacetTypes.FIX_ANALYSIS_EVENTS_LS))
+        {
+            return true;
+        }
+        return false;
+    }
+
+    private void doSectorAverageOut(XYChartGenerator generator, ArtifactAndFacet aaf,
+            Document doc, boolean visible, int idx) {
+        logger.debug("doSectorAverageOut" + aaf.getFacet().getIndex());
+
+        CallContext context = generator.getCallContext();
+        int index = aaf.getFacet().getIndex();
+        int sectorNdx = index & 3;
+
+        @SuppressWarnings("unchecked")
+        KMIndex<AnalysisPeriod> kms =
+                (KMIndex<AnalysisPeriod>)aaf.getData(context);
+
+        if(kms == null) {
+            return;
+        }
+
+        XYSeries series = new StyledXYSeries(aaf.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, idx, visible);
+    }
+
+    private void doReferenceEventsOut(XYChartGenerator generator,
+            ArtifactAndFacet aaf, Document doc, boolean visible, int idx) {
+        logger.debug("doReferenceEventOut");
+
+        CallContext context = generator.getCallContext();
+
+        @SuppressWarnings("unchecked")
+        KMIndex<QWD> kms =
+                (KMIndex<QWD>)aaf.getData(context);
+
+        if(kms == null) {
+            return;
+        }
+
+        XYSeriesCollection col = new XYSeriesCollection();
+
+        StyledXYSeries series = new StyledXYSeries(aaf.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, idx, visible);
+    }
+
+    private void doAnalysisEventsOut(XYChartGenerator generator,
+            ArtifactAndFacet aaf, Document doc, boolean visible, int idx) {
+        logger.debug("doAnalysisEventsOut");
+
+        CallContext context = generator.getCallContext();
+
+        @SuppressWarnings("unchecked")
+        KMIndex<QWD> kms =
+                (KMIndex<QWD>)aaf.getData(context);
+
+        if(kms == null) {
+            return;
+        }
+
+        XYSeriesCollection col = new XYSeriesCollection();
+
+        StyledXYSeries series = new StyledXYSeries(aaf.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, idx, visible);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/process/Processor.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,39 @@
+package org.dive4elements.river.exports.process;
+
+import org.w3c.dom.Document;
+
+import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
+import org.dive4elements.river.exports.XYChartGenerator;
+
+/**
+ * A processor is intended to generate an output e.g. curve in a chart diagramm from
+ * arbitrary data input which can be reused in several generators.
+ *
+ * @author <a href="mailto:bjoern.ricks@intevation.de">Björn Ricks</a>
+ */
+public interface Processor {
+
+    /**
+     * 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
+     */
+    public void doOut(
+            XYChartGenerator generator,
+            ArtifactAndFacet    aandf,
+            Document             theme,
+            boolean               visible,
+            int                         index);
+
+    /**
+     * 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
+     */
+    public boolean canHandle(String facettype);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/process/WOutProcessor.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,116 @@
+package org.dive4elements.river.exports.process;
+
+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.WKms;
+import org.dive4elements.river.exports.StyledSeriesBuilder;
+import org.dive4elements.river.exports.XYChartGenerator;
+import org.dive4elements.river.jfree.StyledAreaSeriesCollection;
+import org.dive4elements.river.jfree.StyledXYSeries;
+import org.dive4elements.river.utils.DataUtil;
+import org.dive4elements.river.utils.ThemeUtil;
+
+/**
+ * Add data to chart/generator.
+ *
+ * @author <a href="mailto:bjoern.ricks@intevation.de">Björn Ricks</a>
+ */
+public class WOutProcessor implements Processor {
+
+    /** Private logger. */
+    private static final Logger logger =
+            Logger.getLogger(WOutProcessor.class);
+
+    @Override
+    public void doOut(
+            XYChartGenerator generator,
+            ArtifactAndFacet aaf,
+            Document         theme,
+            boolean          visible,
+            int              index)
+    {
+        CallContext context = generator.getCallContext();
+        WKms wkms = (WKms) aaf.getData(context);
+
+        logger.debug("doOut");
+
+        XYSeries series = new StyledXYSeries(aaf.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);
+        if (bandWidth > 0 ) {
+            XYSeries seriesDown = new StyledXYSeries(
+                "band " + aaf.getFacetDescription(), false, theme);
+            XYSeries seriesUp = new StyledXYSeries(
+                aaf.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, index, visible);
+        }
+
+        invertAxis(generator, wkms);
+    }
+
+    /**
+     * Returns true if facettype is longitutinal_section.w .
+     */
+    @Override
+    public boolean canHandle(String facetType) {
+        if (facetType == null) {
+            return false;
+        }
+
+        if (facetType.equals(FacetTypes.LONGITUDINAL_W)
+                || facetType.equals(FacetTypes.STATIC_WKMS)
+                || facetType.equals(FacetTypes.HEIGHTMARKS_POINTS)
+                || facetType.equals(FacetTypes.STATIC_WQKMS)
+                || facetType.equals(FacetTypes.STATIC_WQKMS_W)
+                || facetType.equals(FacetTypes.DISCHARGE_LONGITUDINAL_W))
+        {
+            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());
+        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);
+        }
+        generator.setInverted(inv);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/sq/SQOverviewGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,238 @@
+package org.dive4elements.river.exports.sq;
+
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.imageio.ImageIO;
+import javax.xml.xpath.XPathConstants;
+
+import org.apache.log4j.Logger;
+import org.jfree.chart.ChartRenderingInfo;
+import org.jfree.chart.JFreeChart;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
+import org.dive4elements.artifactdatabase.state.Settings;
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.ArtifactDatabaseException;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.common.ArtifactNamespaceContext;
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+import org.dive4elements.river.artifacts.context.FLYSContext;
+import org.dive4elements.river.collections.FLYSArtifactCollection;
+import org.dive4elements.river.exports.ChartGenerator;
+import org.dive4elements.river.exports.OutGenerator;
+import org.dive4elements.river.exports.OutputHelper;
+
+public class SQOverviewGenerator
+implements OutGenerator
+{
+    private static Logger logger = Logger.getLogger(SQOverviewGenerator.class);
+
+    public static final String XPATH_CHART_SIZE =
+        "/art:action/art:attributes/art:size";
+
+    protected FLYSArtifactCollection collection;
+
+    protected Artifact master;
+
+    protected Settings settings;
+
+    protected Document request;
+
+    protected OutputStream out;
+
+    protected CallContext context;
+
+    protected List<JFreeChart> charts;
+
+    /**
+     * Produce output.
+     * @param artifactAndFacet current facet and artifact.
+     * @param attr  theme for facet
+     */
+    @Override
+    public void doOut(
+        ArtifactAndFacet artifactAndFacet,
+        Document         attr,
+        boolean          visible
+    ) {
+        logger.debug("doOut()");
+
+        String name = artifactAndFacet.getData(context).toString();
+        if(name != null) {
+            logger.debug("name: " + name);
+            ChartGenerator g =
+                (ChartGenerator)FLYSContext.getOutGenerator(
+                    context,
+                    name,
+                    null);
+
+            if (g == null) {
+                logger.debug("generator is null.");
+                return;
+            }
+
+            OutputHelper helper = new OutputHelper(master.identifier());
+            Document collectionAttribute = collection.getAttribute();
+
+            try {
+                Document cAttr = getAttribute(context, collectionAttribute, name);
+                g.init(request, out, context);
+
+                helper.doOut(g, name, name, cAttr, context);
+                JFreeChart chart = g.generateChart();
+                chart.removeLegend();
+                charts.add(chart);
+            }
+            catch (IOException e) {
+                logger.warn(e);
+            }
+            catch (ArtifactDatabaseException e) {
+                logger.warn(e);
+            }
+        }
+    }
+
+    @Override
+    public void init(Document request, OutputStream out, CallContext context) {
+        this.request = request;
+        this.out = out;
+        this.context = context;
+        charts = new ArrayList<JFreeChart>();
+    }
+
+    @Override
+    public void setMasterArtifact(Artifact master) {
+        this.master = master;
+    }
+
+    @Override
+    public void setCollection(FLYSArtifactCollection collection) {
+        this.collection = collection;
+    }
+
+    @Override
+    public void generate() throws IOException {
+        logger.debug("SQOverviewGenerator.generate");
+
+        int[] size = getSize();
+
+        if (size == null) {
+            size = new int[] {400, 600};
+        }
+        BufferedImage result =
+            new BufferedImage(size[0], size[1], BufferedImage.TYPE_INT_RGB);
+        for (int i = 0; i < charts.size(); i++) {
+            logger.debug("index: " + i);
+            JFreeChart chart = charts.get(i);
+            ChartRenderingInfo info = new ChartRenderingInfo();
+            BufferedImage img =
+                chart.createBufferedImage(size[0]/2, size[1]/3, info);
+            int horPos = 0;
+            int vertPos = 0;
+            if (i % 2 == 1) {
+                horPos = size[0]/2;
+            }
+            if (i > 1) {
+                vertPos = (size[1] / 3) * (i / 2);
+            }
+            result.createGraphics().drawImage(img, horPos, vertPos, null);
+        }
+        ImageIO.write(result, "png", out);
+    }
+
+    @Override
+    public void setSettings(Settings settings) {
+        this.settings = settings;
+    }
+
+    @Override
+    public Settings getSettings() {
+        return this.settings;
+    }
+
+
+    /**
+     * Returns the "attribute" (part of description document) for a specific
+     * output type.
+     *
+     * @param context The CallContext object.
+     * @param attr The xml attribute saved at the collection.
+     * @param output The name of the desired output type.
+     *
+     * @return the attribute for the desired output type.
+     */
+    protected Document getAttribute(
+        CallContext context,
+        Document    attr,
+        String      output)
+    throws    ArtifactDatabaseException
+    {
+        logger.debug("find specific XML node for Output: " + output);
+
+        Map<String, String> vars = new HashMap<String, String>();
+        vars.put("output", output);
+
+        Node out = (Node) XMLUtils.xpath(
+            attr,
+            "art:attribute/art:outputs/art:output[@name=$output]",
+            XPathConstants.NODE,
+            ArtifactNamespaceContext.INSTANCE,
+            vars);
+
+        if (out != null) {
+            Document o = XMLUtils.newDocument();
+            o.appendChild(o.importNode(out, true));
+
+            return o;
+        }
+
+        return null;
+    }
+
+
+    /**
+     * 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;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/sq/SQRelationExporter.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,162 @@
+package org.dive4elements.river.exports.sq;
+
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+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.sq.SQFractionResult;
+import org.dive4elements.river.artifacts.model.sq.SQResult;
+import org.dive4elements.river.artifacts.model.Parameters;
+
+import org.dive4elements.river.exports.AbstractExporter;
+
+import org.apache.log4j.Logger;
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class SQRelationExporter extends AbstractExporter {
+
+    /** Private logger. */
+    private static final Logger logger =
+        Logger.getLogger(SQRelationExporter.class);
+
+
+    public static final String CSV_PARAMETER =
+        "export.sqrelation.csv.header.parameter";
+
+    public static final String CSV_STATION =
+        "export.sqrelation.csv.header.station";
+
+    public static final String CSV_KM =
+        "export.sqrelation.csv.header.km";
+
+    public static final String CSV_FUNCTION =
+        "export.sqrelation.csv.header.function";
+
+    public static final String CSV_GAUGE =
+        "export.sqrelation.csv.header.gauge";
+
+    public static final String CSV_COEFF_A =
+        "export.sqrelation.csv.header.coeff.a";
+
+    public static final String CSV_COEFF_B =
+        "export.sqrelation.csv.header.coeff.b";
+
+    public static final String CSV_COEFF_Q =
+        "export.sqrelation.csv.header.coeff.q";
+
+    public static final String CSV_COEFF_R =
+        "export.sqrelation.csv.header.coeff.r";
+
+    public static final String CSV_N_TOTAL =
+        "export.sqrelation.csv.header.n.total";
+
+    public static final String CSV_N_OUTLIERS =
+        "export.sqrelation.csv.header.n.outliers";
+
+    public static final String CSV_C_DUAN =
+        "export.sqrelation.csv.header.c.duan";
+
+    public static final String CSV_C_FERGUSON =
+        "export.sqrelation.csv.header.c.ferguson";
+
+    public static final String CSV_VARIANCE =
+        "export.sqrelation.csv.header.variance";
+
+
+    protected List<SQResult []> data;
+
+
+    public void init(Document request, OutputStream out, CallContext cc) {
+        super.init(request, out, cc);
+        data = new ArrayList<SQResult []>();
+    }
+
+
+    @Override
+    protected void addData(Object d) {
+        if (d instanceof CalculationResult) {
+            d = ((CalculationResult)d).getData();
+            if (d instanceof SQResult []) {
+                data.add((SQResult [])d);
+            }
+        }
+    }
+
+    protected void writeCSVHeader(CSVWriter writer) {
+        writer.writeNext(new String[] {
+            msg(CSV_KM        , CSV_KM),
+            msg(CSV_PARAMETER,  CSV_PARAMETER),
+            msg(CSV_COEFF_A   , CSV_COEFF_A),
+            msg(CSV_COEFF_B   , CSV_COEFF_B),
+            msg(CSV_N_TOTAL   , CSV_N_TOTAL),
+            msg(CSV_N_OUTLIERS, CSV_N_OUTLIERS),
+            msg(CSV_VARIANCE  , CSV_VARIANCE)
+        });
+    }
+
+    @Override
+    protected void writeCSVData(CSVWriter writer) {
+        logger.debug("writeCSVData");
+
+        writeCSVHeader(writer);
+
+        for (SQResult [] results: data) {
+            for (SQResult result: results) {
+                data2CSV(writer, result);
+            }
+        }
+    }
+
+    protected void data2CSV(CSVWriter writer, SQResult result) {
+        logger.debug("data2CSV");
+
+        // TODO: i18n
+        String km = String.valueOf(result.getKm());
+
+        for (int i = 0; i < SQResult.NUMBER_FRACTIONS; ++i) {
+            SQFractionResult fraction = result.getFraction(i);
+
+            String name = result.getFractionName(i);
+
+            Parameters parameters = fraction.getParameters();
+
+            if (parameters == null) {
+                continue;
+            }
+
+            double a  = parameters.getValue(0, "a");
+            double b  = parameters.getValue(0, "b");
+            double sd = Math.sqrt(parameters.getValue(0, "std_dev"));
+            int    o  = fraction.totalNumOutliers();
+            int    t  = fraction.numMeasurements() + o;
+
+            writer.writeNext(new String[] {
+                km,
+                name,
+                String.valueOf(a),
+                String.valueOf(b),
+                String.valueOf(t),
+                String.valueOf(o),
+                String.valueOf(sd)
+            });
+        }
+    }
+
+
+    @Override
+    protected void writePDF(OutputStream out) {
+        logger.debug("writePDF");
+        logger.error("NOT IMPLEMENTED: writePDF");
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/sq/SQRelationGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,209 @@
+package org.dive4elements.river.exports.sq;
+
+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.sq.SQ;
+import org.dive4elements.river.artifacts.model.sq.SQFunction;
+
+import org.dive4elements.river.exports.XYChartGenerator;
+
+import org.dive4elements.river.jfree.JFreeUtil;
+import org.dive4elements.river.jfree.StyledXYSeries;
+
+import org.apache.log4j.Logger;
+
+import org.jfree.chart.axis.LogarithmicAxis;
+import org.jfree.chart.axis.NumberAxis;
+
+import org.jfree.data.xy.XYSeries;
+
+import org.w3c.dom.Document;
+
+/**
+ * An OutGenerator that generates charts for MINFO sq relation.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class SQRelationGenerator
+extends      XYChartGenerator
+implements   FacetTypes
+{
+    public enum YAXIS {
+        S(0);
+        protected int idx;
+        private YAXIS(int c) {
+           idx = c;
+        }
+    }
+
+
+    public static final String I18N_XAXIS_LABEL =
+        "chart.sq_relation.xaxis.label";
+
+    public static final String I18N_YAXIS_LABEL =
+        "chart.sq_relation.yaxis.label";
+
+
+    /** The logger that is used in this generator. */
+    private static Logger logger = Logger.getLogger(SQRelationGenerator.class);
+
+
+    @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 String getDefaultChartTitle() {
+        return "TODO: CHART TITLE";
+    }
+
+
+    @Override
+    protected String getDefaultXAxisLabel() {
+        return msg(I18N_XAXIS_LABEL, I18N_XAXIS_LABEL);
+    }
+
+
+    @Override
+    protected String getDefaultYAxisLabel(int index) {
+        return msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL);
+    }
+
+
+    @Override
+    protected NumberAxis createXAxis(String label) {
+        return new LogarithmicAxis(label);
+    }
+
+
+    @Override
+    protected NumberAxis createYAxis(int index) {
+        return new LogarithmicAxis(getDefaultYAxisLabel(index));
+    }
+
+
+    @Override
+    public void doOut(
+        ArtifactAndFacet artifactAndFacet,
+        Document         attr,
+        boolean          visible
+    ) {
+        logger.debug("doOut");
+
+        Facet  facet = artifactAndFacet.getFacet();
+        String name  = facet != null ? facet.getName() : null;
+
+        if (name == null || name.length() == 0) {
+            logger.warn("Invalid facet with no name given!");
+            return;
+        }
+
+        if (IS.SQ_CURVE(name)) {
+            doSQCurveOut(artifactAndFacet, attr, visible);
+        }
+        else if (IS.SQ_MEASUREMENT(name)) {
+            doSQOut(artifactAndFacet, attr, visible);
+        }
+        else if (IS.SQ_OUTLIER(name)) {
+            doSQOut(artifactAndFacet, attr, visible);
+        }
+        else if (IS.MANUALPOINTS(name)) {
+            doPoints(
+                artifactAndFacet.getData(context),
+                artifactAndFacet,
+                attr,
+                visible,
+                YAXIS.S.idx);
+        }
+    }
+
+
+    protected void doSQCurveOut(
+        ArtifactAndFacet artifactAndFacet,
+        Document         attr,
+        boolean          visible
+    ) {
+        String desc = artifactAndFacet.getFacetDescription();
+        logger.debug("doSQCurveOut: " + desc);
+
+        SQFunction func = (SQFunction) artifactAndFacet.getData(context);
+
+        if (func == null) {
+            return;
+        }
+
+        XYSeries series = JFreeUtil.sampleFunction2DPositive(
+            func.getFunction(),
+            attr,
+            desc,
+            500,
+            Math.max(func.getMinQ(), 0.01),
+            Math.max(func.getMaxQ(), 0.02));
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("Series '" + desc + "' has "
+                + series.getItemCount() + " items.");
+
+            logger.debug("   -> min x = " + series.getMinX());
+            logger.debug("   -> max x = " + series.getMaxX());
+            logger.debug("   -> min y = " + series.getMinY());
+            logger.debug("   -> max y = " + series.getMaxY());
+        }
+
+        addAxisSeries(series, YAXIS.S.idx, visible);
+    }
+
+
+    protected void doSQOut(
+        ArtifactAndFacet artifactAndFacet,
+        Document         attr,
+        boolean          visible
+    ) {
+        String desc = artifactAndFacet.getFacetDescription();
+        logger.debug("doSQOut: " + desc);
+
+        SQ[]     sqs    = (SQ[]) artifactAndFacet.getData(context);
+        if (sqs == null) {
+            logger.debug("No SQs found for facet");
+            return;
+        }
+        XYSeries series = new StyledXYSeries(desc, attr);
+
+        for (SQ sq: sqs) {
+            double q = sq.getQ();
+            double s = sq.getS();
+            if (s > 0d && q > 0d) {
+                series.add(q, s, false);
+            }
+        }
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("Series '" + desc + "' has "
+                + series.getItemCount() + " items.");
+
+            logger.debug("   -> min x = " + series.getMinX());
+            logger.debug("   -> max x = " + series.getMaxX());
+            logger.debug("   -> min y = " + series.getMinY());
+            logger.debug("   -> max y = " + series.getMaxY());
+        }
+
+        addAxisSeries(series, YAXIS.S.idx, 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/flys-artifacts/src/main/java/org/dive4elements/river/exports/sq/SQRelationGeneratorA.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,15 @@
+package org.dive4elements.river.exports.sq;
+
+
+public class SQRelationGeneratorA extends SQRelationGenerator {
+
+    public static final String I18N_CHART_TITLE =
+        "chart.sq_relation_a.title";
+
+
+    @Override
+    public String getDefaultChartTitle() {
+        return msg(I18N_CHART_TITLE, I18N_CHART_TITLE);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/sq/SQRelationGeneratorB.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,15 @@
+package org.dive4elements.river.exports.sq;
+
+
+public class SQRelationGeneratorB extends SQRelationGenerator {
+
+    public static final String I18N_CHART_TITLE =
+        "chart.sq_relation_b.title";
+
+
+    @Override
+    public String getDefaultChartTitle() {
+        return msg(I18N_CHART_TITLE, I18N_CHART_TITLE);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/sq/SQRelationGeneratorC.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,15 @@
+package org.dive4elements.river.exports.sq;
+
+
+public class SQRelationGeneratorC extends SQRelationGenerator {
+
+    public static final String I18N_CHART_TITLE =
+        "chart.sq_relation_c.title";
+
+
+    @Override
+    public String getDefaultChartTitle() {
+        return msg(I18N_CHART_TITLE, I18N_CHART_TITLE);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/sq/SQRelationGeneratorD.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,15 @@
+package org.dive4elements.river.exports.sq;
+
+
+public class SQRelationGeneratorD extends SQRelationGenerator {
+
+    public static final String I18N_CHART_TITLE =
+        "chart.sq_relation_d.title";
+
+
+    @Override
+    public String getDefaultChartTitle() {
+        return msg(I18N_CHART_TITLE, I18N_CHART_TITLE);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/sq/SQRelationGeneratorE.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,15 @@
+package org.dive4elements.river.exports.sq;
+
+
+public class SQRelationGeneratorE extends SQRelationGenerator {
+
+    public static final String I18N_CHART_TITLE =
+        "chart.sq_relation_e.title";
+
+
+    @Override
+    public String getDefaultChartTitle() {
+        return msg(I18N_CHART_TITLE, I18N_CHART_TITLE);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/sq/SQRelationGeneratorF.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,15 @@
+package org.dive4elements.river.exports.sq;
+
+
+public class SQRelationGeneratorF extends SQRelationGenerator {
+
+    public static final String I18N_CHART_TITLE =
+        "chart.sq_relation_f.title";
+
+
+    @Override
+    public String getDefaultChartTitle() {
+        return msg(I18N_CHART_TITLE, I18N_CHART_TITLE);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/exports/sq/SQRelationInfoGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,19 @@
+package org.dive4elements.river.exports.sq;
+
+import org.dive4elements.river.exports.ChartInfoGenerator;
+
+
+/**
+ * A ChartInfoGenerator that generates meta information for specific
+ * sq relation charts.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class SQRelationInfoGenerator
+extends      ChartInfoGenerator
+{
+    public SQRelationInfoGenerator() {
+        super(new SQRelationGenerator());
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/java2d/NOPGraphics2D.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,528 @@
+package org.dive4elements.river.java2d;
+
+import java.util.Map;
+
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Shape;
+import java.awt.Image;
+import java.awt.Color;
+import java.awt.Composite;
+import java.awt.Paint;
+import java.awt.GraphicsConfiguration;
+import java.awt.Stroke;
+import java.awt.Rectangle;
+import java.awt.Font;
+import java.awt.FontMetrics;
+
+import java.awt.image.RenderedImage;
+
+import java.awt.image.BufferedImage;
+import java.awt.image.BufferedImageOp;
+import java.awt.image.ImageObserver;
+
+import java.awt.image.renderable.RenderableImage;
+
+import java.awt.geom.AffineTransform;
+
+import java.text.AttributedCharacterIterator;
+
+import java.awt.font.GlyphVector;
+import java.awt.font.FontRenderContext;
+
+import java.awt.RenderingHints;
+
+public final class NOPGraphics2D
+extends            Graphics2D
+{
+    private Graphics2D parent;
+
+    public NOPGraphics2D(Graphics2D parent) {
+        this.parent = parent;
+    }
+
+    @Override
+    public final void addRenderingHints(Map<?,?> hints) {
+        parent.addRenderingHints(hints);
+    }
+
+    @Override
+    public final void clip(Shape s) {
+    }
+
+    @Override
+    public final void draw(Shape s) {
+    }
+
+    @Override
+    public final void drawGlyphVector(GlyphVector g, float x, float y) {
+    }
+
+    @Override
+    public final void drawImage(
+        BufferedImage   img,
+        BufferedImageOp op,
+        int x,
+        int y
+    ) {
+    }
+
+    @Override
+    public final boolean drawImage(
+        Image           img,
+        AffineTransform xform,
+        ImageObserver   obs
+    ) {
+        return true;
+    }
+
+    @Override
+    public final void drawRenderableImage(
+        RenderableImage img,
+        AffineTransform xform
+    ) {
+    }
+
+    @Override
+    public final void drawRenderedImage(
+        RenderedImage   img,
+        AffineTransform xform
+    ) {
+    }
+
+    @Override
+    public final void drawString(
+        AttributedCharacterIterator iterator,
+        float x,
+        float y
+    ) {
+    }
+
+    @Override
+    public final void drawString(
+        AttributedCharacterIterator iterator,
+        int x,
+        int y
+    ) {
+    }
+
+    @Override
+    public final void drawString(String str, float x, float y) {
+    }
+
+    @Override
+    public final void drawString(String str, int x, int y) {
+    }
+
+    @Override
+    public final void fill(Shape s) {
+    }
+
+    @Override
+    public final Color getBackground() {
+        return parent.getBackground();
+    }
+
+    @Override
+    public final Composite getComposite() {
+        return parent.getComposite();
+    }
+
+    @Override
+    public final GraphicsConfiguration getDeviceConfiguration() {
+        return parent.getDeviceConfiguration();
+    }
+
+    @Override
+    public final FontRenderContext getFontRenderContext() {
+        return parent.getFontRenderContext();
+    }
+
+    @Override
+    public final Paint getPaint() {
+        return parent.getPaint();
+    }
+
+    @Override
+    public final Object getRenderingHint(RenderingHints.Key hintKey) {
+        return parent.getRenderingHint(hintKey);
+    }
+
+    @Override
+    public final RenderingHints getRenderingHints() {
+        return parent.getRenderingHints();
+    }
+
+    @Override
+    public final Stroke getStroke() {
+        return parent.getStroke();
+    }
+
+    @Override
+    public final AffineTransform getTransform() {
+        return parent.getTransform();
+    }
+
+    @Override
+    public final boolean hit(Rectangle rect, Shape s, boolean onStroke) {
+        return parent.hit(rect, s, onStroke);
+    }
+
+    @Override
+    public final void rotate(double theta) {
+        parent.rotate(theta);
+    }
+
+    @Override
+    public final void rotate(double theta, double x, double y) {
+        parent.rotate(theta);
+    }
+
+    @Override
+    public final void scale(double sx, double sy) {
+        parent.scale(sx, sy);
+    }
+
+    @Override
+    public final void setBackground(Color color) {
+        parent.setBackground(color);
+    }
+
+    @Override
+    public final void setComposite(Composite comp) {
+        parent.setComposite(comp);
+    }
+
+    @Override
+    public final void setPaint(Paint paint) {
+        parent.setPaint(paint);
+    }
+
+    @Override
+    public final void setRenderingHint(
+        RenderingHints.Key hintKey,
+        Object             hintValue
+    ) {
+        parent.setRenderingHint(hintKey, hintValue);
+    }
+
+    @Override
+    public final void setRenderingHints(Map<?,?> hints) {
+        parent.setRenderingHints(hints);
+    }
+
+    @Override
+    public final void setStroke(Stroke s) {
+        parent.setStroke(s);
+    }
+
+    @Override
+    public final void setTransform(AffineTransform Tx) {
+        parent.setTransform(Tx);
+    }
+
+
+    @Override
+    public final void shear(double shx, double shy) {
+        parent.shear(shx, shy);
+    }
+
+    @Override
+    public final void transform(AffineTransform Tx) {
+        parent.transform(Tx);
+    }
+
+    @Override
+    public final void translate(double tx, double ty) {
+        parent.translate(tx, ty);
+    }
+
+    @Override
+    public final void translate(int tx, int ty) {
+        parent.translate(tx, ty);
+    }
+
+    @Override
+    public final void dispose() {
+        parent.dispose();
+    }
+
+    @Override
+    public final boolean drawImage(
+        Image img,
+        int x,
+        int y,
+        int width,
+        int height,
+        Color bgcolor,
+        ImageObserver observer
+    ) {
+        return true;
+    }
+
+    @Override
+    public final boolean drawImage(
+        Image img,
+        int dx1,
+        int dy1,
+        int dx2,
+        int dy2,
+        int sx1,
+        int sy1,
+        int sx2,
+        int sy2,
+        Color bgcolor,
+        ImageObserver observer
+    ) {
+        return true;
+    }
+
+    @Override
+    public final boolean drawImage(
+        Image img,
+        int dx1,
+        int dy1,
+        int dx2,
+        int dy2,
+        int sx1,
+        int sy1,
+        int sx2,
+        int sy2,
+        ImageObserver observer
+    ) {
+        return true;
+    }
+
+    @Override
+    public final boolean drawImage(
+        Image img,
+        int x,
+        int y,
+        Color bgcolor,
+        ImageObserver observer
+    ) {
+        return true;
+    }
+
+    @Override
+    public final boolean drawImage(
+        Image img,
+        int x,
+        int y,
+        int width,
+        int height,
+        ImageObserver observer
+    ) {
+        return true;
+    }
+
+    @Override
+    public final boolean drawImage(
+        Image img,
+        int x,
+        int y,
+        ImageObserver observer
+    ) {
+        return true;
+    }
+
+    @Override
+    public final void fillPolygon(
+        int [] xPoints,
+        int [] yPoints,
+        int    nPoints
+    ) {
+    }
+
+    @Override
+    public final void drawPolygon(
+        int [] xPoints,
+        int [] yPoints,
+        int    nPoints
+    ) {
+    }
+
+    @Override
+    public final void drawPolyline(
+        int [] xPoints,
+        int [] yPoints,
+        int    nPoints
+    ) {
+    }
+
+    @Override
+    public final void fillArc(
+        int x,
+        int y,
+        int width,
+        int height,
+        int startAngle,
+        int arcAngle
+    ) {
+    }
+
+    @Override
+    public final void drawArc(
+        int x,
+        int y,
+        int width,
+        int height,
+        int startAngle,
+        int arcAngle
+    ) {
+    }
+
+    @Override
+    public final void fillOval(
+        int x,
+        int y,
+        int width,
+        int height
+    ) {
+    }
+
+    @Override
+    public final void drawOval(
+        int x,
+        int y,
+        int width,
+        int height
+    ) {
+    }
+
+    @Override
+    public final void fillRoundRect(
+        int x,
+        int y,
+        int width,
+        int height,
+        int arcWidth,
+        int arcHeight
+    ) {
+    }
+
+    @Override
+    public final void drawRoundRect(
+        int x,
+        int y,
+        int width,
+        int height,
+        int arcWidth,
+        int arcHeight
+    ) {
+    }
+
+    @Override
+    public final void clearRect(
+        int x,
+        int y,
+        int width,
+        int height
+    ) {
+    }
+
+    @Override
+    public final void fillRect(
+        int x,
+        int y,
+        int width,
+        int height
+    ) {
+    }
+
+    @Override
+    public final void drawLine(
+        int x1,
+        int y1,
+        int x2,
+        int y2
+    ) {
+    }
+
+    @Override
+    public final void copyArea(
+        int x,
+        int y,
+        int width,
+        int height,
+        int dx,
+        int dy
+    ) {
+    }
+
+    @Override
+    public final void setClip(
+        int x,
+        int y,
+        int width,
+        int height
+    ) {
+        parent.setClip(x, y, width, height);
+    }
+
+    @Override
+    public final void setClip(Shape shape) {
+        parent.setClip(shape);
+    }
+
+    @Override
+    public final Shape getClip() {
+        return parent.getClip();
+    }
+
+    @Override
+    public final void clipRect(
+        int x,
+        int y,
+        int width,
+        int height
+    ) {
+        parent.clipRect(x, y, width, height);
+    }
+
+    @Override
+    public final Rectangle getClipBounds() {
+        return parent.getClipBounds();
+    }
+
+    @Override
+    public final FontMetrics getFontMetrics(Font f) {
+        return parent.getFontMetrics(f);
+    }
+
+    @Override
+    public final void setFont(Font font) {
+        parent.setFont(font);
+    }
+
+    @Override
+    public final Font getFont() {
+        return parent.getFont();
+    }
+
+    @Override
+    public final void setXORMode(Color c1) {
+        parent.setXORMode(c1);
+    }
+
+    @Override
+    public final void setPaintMode() {
+        parent.setPaintMode();
+    }
+
+    @Override
+    public final void setColor(Color c) {
+        parent.setColor(c);
+    }
+
+    @Override
+    public final Color getColor() {
+        return parent.getColor();
+    }
+
+    @Override
+    public final Graphics create() {
+        return new NOPGraphics2D((Graphics2D)parent.create());
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/java2d/ShapeUtils.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,83 @@
+package org.dive4elements.river.java2d;
+
+import java.awt.Shape;
+
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.GeneralPath;
+import java.awt.geom.Rectangle2D;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class ShapeUtils
+{
+    // TODO: Use enum
+    public static final int MEASURED     = 0;
+    public static final int DIGITIZED    = 1;
+    public static final int INTERPOLATED = 2;
+
+    public static final boolean DIGITIZED_FILL    = false;
+    public static final boolean MEASURED_FILL     = true;
+    public static final boolean INTERPOLATED_FILL = false;
+
+   public static final Shape DIGITIZED_SHAPE =
+        createCross(4f);
+
+    public static final Shape MEASURED_SHAPE =
+        new Rectangle2D.Double(-2, -2, 4, 4);
+
+    public static final Shape INTERPOLATED_SHAPE =
+        new Ellipse2D.Double(-2, -2, 4, 4);
+
+    protected static Map<Long, Shape> scaledShapesCache =
+        new HashMap<Long, Shape>();
+
+    public static final Shape createCross(float size) {
+        float half = size * 0.5f;
+        GeneralPath p = new GeneralPath();
+        p.moveTo(-half, -half);
+        p.lineTo(half, half);
+        p.closePath();
+        p.moveTo(-half, half);
+        p.lineTo(half, -half);
+        p.closePath();
+        return p;
+    }
+
+    public static Shape scale(Shape shape, float factor) {
+        if (factor == 1f) {
+            return shape;
+        }
+        AffineTransform xform =
+            AffineTransform.getScaleInstance(factor, factor);
+
+        GeneralPath gp = new GeneralPath(shape);
+        return gp.createTransformedShape(xform);
+    }
+
+    public static synchronized Shape getScaledShape(int type, float size) {
+
+        Long hash = Long.valueOf(
+            (((long)type) << 32) | Float.floatToIntBits(size));
+
+        Shape shape = scaledShapesCache.get(hash);
+
+        if (shape == null) {
+            switch (type) {
+                case MEASURED:
+                    shape = MEASURED_SHAPE;
+                    break;
+                case DIGITIZED:
+                    shape = DIGITIZED_SHAPE;
+                    break;
+                default:
+                    shape = INTERPOLATED_SHAPE;
+            }
+            scaledShapesCache.put(hash, shape = scale(shape, size));
+        }
+
+        return shape;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/jfree/Bounds.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,24 @@
+package org.dive4elements.river.jfree;
+
+import java.io.Serializable;
+
+import org.jfree.chart.axis.ValueAxis;
+
+
+/**
+ * Somewhat better Ranges.
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface Bounds extends Serializable {
+
+    Number getLower();
+
+    Number getUpper();
+
+    void applyBounds(ValueAxis axis);
+
+    void applyBounds(ValueAxis axis, int percent);
+
+    Bounds combine(Bounds bounds);
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/jfree/CollisionFreeLineLabelEntity.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,21 @@
+package org.dive4elements.river.jfree;
+
+import java.awt.Shape;
+
+import org.jfree.chart.entity.XYAnnotationEntity;
+
+/**
+ * Chart Entity for Line Labels that should not collide.
+ */
+public class CollisionFreeLineLabelEntity
+extends XYAnnotationEntity {
+    public CollisionFreeLineLabelEntity(
+        Shape hotspot,
+        int rendererIndex,
+        String toolTip,
+        String url
+    ) {
+        super(hotspot, rendererIndex, toolTip, url);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/jfree/CollisionFreeXYTextAnnotation.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,140 @@
+package org.dive4elements.river.jfree;
+
+import java.awt.Shape;
+import java.awt.geom.Rectangle2D;
+
+import org.jfree.chart.entity.EntityCollection;
+import org.jfree.chart.annotations.XYTextAnnotation;
+import org.jfree.chart.axis.ValueAxis;
+import org.jfree.chart.plot.PlotOrientation;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.chart.entity.XYAnnotationEntity;
+import org.jfree.chart.plot.PlotRenderingInfo;
+import org.jfree.chart.plot.Plot;
+
+import org.jfree.text.TextUtilities;
+
+import org.jfree.ui.RectangleEdge;
+
+/**
+ * Custom Annotations class that is drawn only if no collisions with other
+ * already drawn CustomAnnotations in current plot are found.
+ */
+public class CollisionFreeXYTextAnnotation extends XYTextAnnotation {
+
+    public CollisionFreeXYTextAnnotation(String text, double x, double y) {
+        super(text, x, y);
+    }
+
+
+    /**
+     * Draw the Annotation only if it does not collide with other
+     * already drawn Annotations- texts.
+     *
+     * @param g2            the graphics device.
+     * @param plot          the plot.
+     * @param dataArea      the data area.
+     * @param domainAxis    the domain axis.
+     * @param rangeAxis     the range axis.
+     * @param rendererIndex the render index.
+     * @param info          state information, escpecially collects info about
+     *                      already drawn shapes (and thus annotations), used
+     *                      for collision detection.
+     */
+    @Override
+    public void draw(
+        java.awt.Graphics2D g2,
+        XYPlot plot,
+        java.awt.geom.Rectangle2D dataArea,
+        ValueAxis domainAxis,
+        ValueAxis rangeAxis,
+        int rendererIndex,
+        PlotRenderingInfo info
+    ) {
+        // From superclass, adjusted access only.
+        PlotOrientation orientation = plot.getOrientation();
+        RectangleEdge domainEdge = Plot.resolveDomainAxisLocation(
+                plot.getDomainAxisLocation(), orientation);
+        RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation(
+                plot.getRangeAxisLocation(), orientation);
+
+        float anchorX = (float) domainAxis.valueToJava2D(
+                this.getX(), dataArea, domainEdge);
+        float anchorY = (float) rangeAxis.valueToJava2D(
+                this.getY(), dataArea, rangeEdge);
+
+        if (orientation == PlotOrientation.HORIZONTAL) {
+            float tempAnchor = anchorX;
+            anchorX = anchorY;
+            anchorY = tempAnchor;
+        }
+
+        g2.setFont(getFont());
+        Shape hotspot = TextUtilities.calculateRotatedStringBounds(
+                getText(), g2, anchorX, anchorY, getTextAnchor(),
+                getRotationAngle(), getRotationAnchor());
+
+        // Deviation from superclass: prevent collision.
+        if (JFreeUtil.collides(hotspot, info.getOwner().getEntityCollection(),
+            XYAnnotationEntity.class)) {
+            return;
+        }
+
+        if (this.getBackgroundPaint() != null) {
+            g2.setPaint(this.getBackgroundPaint());
+            g2.fill(hotspot);
+        }
+        g2.setPaint(getPaint());
+        TextUtilities.drawRotatedString(getText(), g2, anchorX, anchorY,
+                getTextAnchor(), getRotationAngle(), getRotationAnchor());
+        if (this.isOutlineVisible()) {
+            g2.setStroke(this.getOutlineStroke());
+            g2.setPaint(this.getOutlinePaint());
+            g2.draw(hotspot);
+        }
+
+        //String toolTip = getToolTipText();
+        //String url = getURL();
+        String toolTip = "CollisionFreeXYTextAnnotation";
+        String url     = toolTip;
+
+        if (toolTip != null || url != null) {
+            addEntity(info, hotspot, rendererIndex, toolTip, url);
+        }
+        // XXX: DEAD CODE (as long as a hard value is assigned to toolTip
+        /*
+        else {
+            addEntity(info, hotspot, rendererIndex,
+                "CollisionFreeXYTextAnnotation",
+                "CollisionFreeXYTextAnnotation");
+        }
+        */
+    }
+
+    /**
+     * A utility method for adding an {@link CollisionFreeXYAnnotationEntity} to
+     * a {@link PlotRenderingInfo} instance.
+     *
+     * @param info  the plot rendering info (<code>null</code> permitted).
+     * @param hotspot  the hotspot area.
+     * @param rendererIndex  the renderer index.
+     * @param toolTipText  the tool tip text.
+     * @param urlText  the URL text.
+     */
+    protected void addEntity(PlotRenderingInfo info,
+                             Shape hotspot, int rendererIndex,
+                             String toolTipText, String urlText) {
+        if (info == null) {
+            return;
+        }
+        EntityCollection entities = info.getOwner().getEntityCollection();
+        if (entities == null) {
+            return;
+        }
+        CollisionFreeXYTextAnnotationEntity entity =
+            new CollisionFreeXYTextAnnotationEntity(hotspot,
+                rendererIndex, toolTipText, urlText);
+        entities.add(entity);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/jfree/CollisionFreeXYTextAnnotationEntity.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,21 @@
+package org.dive4elements.river.jfree;
+
+import java.awt.Shape;
+
+import org.jfree.chart.entity.XYAnnotationEntity;
+
+/**
+ * Chart Entity for XYTextAnnotations that should not collide.
+ */
+public class CollisionFreeXYTextAnnotationEntity
+extends XYAnnotationEntity {
+    public CollisionFreeXYTextAnnotationEntity(
+        Shape hotspot,
+        int rendererIndex,
+        String toolTip,
+        String url
+    ) {
+        super(hotspot, rendererIndex, toolTip, url);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/jfree/DoubleBounds.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,85 @@
+package org.dive4elements.river.jfree;
+
+
+import org.jfree.chart.axis.ValueAxis;
+import org.jfree.chart.axis.LogarithmicAxis;
+import org.jfree.data.Range;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DoubleBounds implements Bounds {
+
+    protected double lower;
+    protected double upper;
+
+
+    /**
+     * Default constructor. <b>A DoubleBounds has always set lower &lt;
+     * upper!</b>
+     */
+    public DoubleBounds(double lower, double upper) {
+        this.lower = Math.min(lower, upper);
+        this.upper = Math.max(lower, upper);
+    }
+
+
+    @Override
+    public Number getLower() {
+        return Double.valueOf(lower);
+    }
+
+
+    @Override
+    public Number getUpper() {
+        return Double.valueOf(upper);
+    }
+
+
+    @Override
+    public void applyBounds(ValueAxis axis) {
+        axis.setRange(new Range(lower, upper));
+    }
+
+
+    /**
+     * Set extended range to ValueAxis.
+     * @param percent how many percent to extend (in each direction,
+     *        thus 10 percent on [0,100] -> [-10,110].
+     */
+    @Override
+    public void applyBounds(ValueAxis axis, int percent) {
+        double space = (upper - lower) / 100 * percent;
+        if (axis instanceof LogarithmicAxis) {
+            axis.setRange(new Range(Math.max(lower-space, 0.0001),
+                        Math.max(upper+space, 0.0002)));
+        } else {
+            axis.setRange(new Range(lower-space, upper+space));
+        }
+    }
+
+
+    @Override
+    public Bounds combine(Bounds bounds) {
+        if (bounds == null) {
+            return this;
+        }
+
+        DoubleBounds other = (DoubleBounds) bounds;
+
+        double otherLower = other.getLower().doubleValue();
+        double otherUpper = other.getUpper().doubleValue();
+
+        return new DoubleBounds(
+            otherLower < lower ? otherLower : lower,
+            otherUpper > upper ? otherUpper : upper);
+    }
+
+
+    @Override
+    public String toString() {
+        return "DoubleBounds=[" + lower + " ; " + upper + "]";
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/jfree/EnhancedLineAndShapeRenderer.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,576 @@
+package org.dive4elements.river.jfree;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Graphics2D;
+import java.awt.Paint;
+import java.awt.Shape;
+import java.awt.geom.Rectangle2D;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+import org.jfree.chart.axis.ValueAxis;
+import org.jfree.chart.entity.EntityCollection;
+import org.jfree.chart.plot.CrosshairState;
+import org.jfree.chart.plot.PlotOrientation;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
+import org.jfree.data.xy.XYDataset;
+import org.jfree.data.xy.XYSeries;
+import org.jfree.data.xy.XYSeriesCollection;
+import org.jfree.text.TextUtilities;
+import org.jfree.ui.RectangleEdge;
+import org.jfree.ui.TextAnchor;
+import org.jfree.util.BooleanList;
+import org.jfree.util.ShapeUtilities;
+
+/**
+ * Renderer with additional the additional functionality of renderering minima
+ * and/or maxima of dataseries contained in datasets.
+ */
+public class EnhancedLineAndShapeRenderer extends XYLineAndShapeRenderer {
+
+    /**
+     *
+     */
+    private static final long serialVersionUID = 1L;
+
+    /** Own logger. */
+    private static final Logger logger =
+        Logger.getLogger(EnhancedLineAndShapeRenderer.class);
+
+    protected BooleanList isMinimumShapeVisible;
+    protected BooleanList isMaximumShapeVisible;
+    protected BooleanList showLineLabel;
+
+    protected Map<Integer, Double> seriesMinimum;
+    protected Map<Integer, Double> seriesMinimumX;
+    protected Map<Integer, Double> seriesMaximum;
+
+    protected Map<Integer, Font> lineLabelFonts;
+    protected Map<Integer, Color> lineLabelTextColors;
+    protected BooleanList showLineLabelBG;
+    protected Map<Integer, Color> lineLabelBGColors;
+
+
+    public EnhancedLineAndShapeRenderer(boolean lines, boolean shapes) {
+        super(lines, shapes);
+        this.isMinimumShapeVisible = new BooleanList();
+        this.isMaximumShapeVisible = new BooleanList();
+        this.showLineLabel         = new BooleanList();
+        this.showLineLabelBG       = new BooleanList();
+        this.seriesMinimum         = new HashMap<Integer, Double>();
+        this.seriesMaximum         = new HashMap<Integer, Double>();
+        this.seriesMinimumX        = new HashMap<Integer, Double>();
+        this.lineLabelFonts        = new HashMap<Integer, Font>();
+        this.lineLabelTextColors   = new HashMap<Integer, Color>();
+        this.lineLabelBGColors     = new HashMap<Integer, Color>();
+    }
+
+
+    /**
+     * Draw a background-box of a text to render.
+     * @param g2 graphics device to use
+     * @param text text to draw
+     * @param textX x-position for text
+     * @param textY y-position for text
+     * @param bgColor color to fill box with.
+     */
+    public static void drawTextBox(Graphics2D g2,
+        String text, float textX, float textY, Color bgColor
+    ) {
+        Rectangle2D hotspotBox = g2.getFontMetrics().getStringBounds(text, g2);
+        float w = (float) hotspotBox.getWidth(), h = (float) hotspotBox.getHeight();
+        hotspotBox.setRect(textX, textY-h, w, h);
+        Color oldColor = g2.getColor();
+        g2.setColor(bgColor);
+        g2.fill(hotspotBox);
+        g2.setColor(oldColor);
+    }
+
+
+    /**
+     * Whether or not a specific item in a series (maybe the maxima) should
+     * be rendered with shape.
+     */
+    public boolean getItemShapeVisible(XYDataset dataset, int series, int item){
+        if (super.getItemShapeVisible(series, item)) {
+            return true;
+        }
+
+        if (isMinimumShapeVisible(series) && isMinimum(dataset, series, item)) {
+            return true;
+        }
+
+        if (isMaximumShapeVisible(series) && isMaximum(dataset, series, item)) {
+            return true;
+        }
+
+        return false;
+    }
+
+
+    /**
+     * Rectangle used to draw maximums shape.
+     */
+    public Shape getMaximumShape(int series, int column) {
+        return new Rectangle2D.Double(-5d, -5d, 10d, 10d);
+    }
+
+
+    /**
+     * Rectangle used to draw minimums shape.
+     */
+    public Shape getMinimumShape(int series, int column) {
+        return new Rectangle2D.Double(-5d, -5d, 10d, 10d);
+    }
+
+
+    /** Get fill paint for the maximum indicators. */
+    public Paint getMaximumFillPaint(int series, int column) {
+        Paint p = getItemPaint(series, column);
+
+        if (p instanceof Color) {
+            Color c = (Color) p;
+            Color b = c;
+
+            for (int i = 0; i < 2; i++) {
+                b = b.darker();
+            }
+
+            return b;
+        }
+
+        logger.warn("Item paint is no instance of Color!");
+        return p;
+    }
+
+
+    /** Get fill paint for the minimum indicators. */
+    public Paint getMinimumFillPaint(int series, int column) {
+        Paint p = getItemPaint(series, column);
+
+        if (p instanceof Color) {
+            Color c = (Color) p;
+            Color b = c;
+
+            for (int i = 0; i < 2; i++) {
+                b = b.darker();
+            }
+
+            return b;
+        }
+
+        logger.warn("Item paint is no instance of Color!");
+        return p;
+    }
+
+
+    /**
+     * Overrides XYLineAndShapeRenderer.drawSecondaryPass() to call an adapted
+     * method getItemShapeVisible() which now takes an XYDataset. So, 99% of
+     * code equal the code in XYLineAndShapeRenderer.
+     */
+    @Override
+    protected void drawSecondaryPass(
+        Graphics2D       g2,
+        XYPlot           plot,
+        XYDataset        dataset,
+        int              pass,
+        int              series,
+        int              item,
+        ValueAxis        domainAxis,
+        Rectangle2D      dataArea,
+        ValueAxis        rangeAxis,
+        CrosshairState   crosshairState,
+        EntityCollection entities
+    ) {
+        Shape entityArea = null;
+
+        // get the data point...
+        double x1 = dataset.getXValue(series, item);
+        double y1 = dataset.getYValue(series, item);
+        if (Double.isNaN(y1) || Double.isNaN(x1)) {
+            return;
+        }
+
+        PlotOrientation orientation = plot.getOrientation();
+        RectangleEdge xAxisLocation = plot.getDomainAxisEdge();
+        RectangleEdge yAxisLocation = plot.getRangeAxisEdge();
+        double transX1 = domainAxis.valueToJava2D(x1, dataArea, xAxisLocation);
+        double transY1 = rangeAxis.valueToJava2D(y1, dataArea, yAxisLocation);
+
+        if (getItemShapeVisible(dataset, series, item)) {
+            Shape shape = null;
+
+            // OPTIMIZE: instead of calculating minimum and maximum for every
+            //           point, calculate it just once (assume that dataset
+            //           content does not change during rendering).
+            // NOTE:     Above OPTIMIZE might already be fulfilled to most extend.
+            boolean isMinimum = isMinimumShapeVisible(series)
+                && isMinimum(dataset, series, item);
+
+            boolean isMaximum = isMaximumShapeVisible(series)
+                && isMaximum(dataset, series, item);
+
+            if (isMinimum) {
+                logger.debug("Create a Minimum shape.");
+                shape = getMinimumShape(series, item);
+            }
+            else if (isMaximum) {
+                logger.debug("Create a Maximum shape.");
+                shape = getMaximumShape(series, item);
+            }
+            else {
+                shape = getItemShape(series, item);
+            }
+
+            if (orientation == PlotOrientation.HORIZONTAL) {
+                shape = ShapeUtilities.createTranslatedShape(shape, transY1,
+                        transX1);
+            }
+            else if (orientation == PlotOrientation.VERTICAL) {
+                shape = ShapeUtilities.createTranslatedShape(shape, transX1,
+                        transY1);
+            }
+            entityArea = shape;
+            if (shape.intersects(dataArea)) {
+                if (getItemShapeFilled(series, item)) {
+                    if (getUseFillPaint()) {
+                        g2.setPaint(getItemFillPaint(series, item));
+                    }
+                    else {
+                        g2.setPaint(getItemPaint(series, item));
+                    }
+                    g2.fill(shape);
+                }
+                if (getDrawOutlines()) {
+                    if (getUseOutlinePaint()) {
+                        g2.setPaint(getItemOutlinePaint(series, item));
+                    }
+                    else {
+                        g2.setPaint(getItemPaint(series, item));
+                    }
+                    g2.setStroke(getItemOutlineStroke(series, item));
+                    g2.draw(shape);
+                }
+
+                if (isMinimum) {
+                    g2.setPaint(getMinimumFillPaint(series, item));
+                    g2.fill(shape);
+                    g2.setPaint(getItemOutlinePaint(series, item));
+                    g2.setStroke(getItemOutlineStroke(series, item));
+                    g2.draw(shape);
+                }
+                else if (isMaximum) {
+                    g2.setPaint(getMaximumFillPaint(series, item));
+                    g2.fill(shape);
+                    g2.setPaint(getItemOutlinePaint(series, item));
+                    g2.setStroke(getItemOutlineStroke(series, item));
+                    g2.draw(shape);
+                }
+            }
+        } // if (getItemShapeVisible(dataset, series, item))
+
+        double xx = transX1;
+        double yy = transY1;
+        if (orientation == PlotOrientation.HORIZONTAL) {
+            xx = transY1;
+            yy = transX1;
+        }
+
+        // Draw the item label if there is one...
+        if (isItemLabelVisible(series, item)) {
+            drawItemLabel(g2, orientation, dataset, series, item, xx, yy,
+                    (y1 < 0.0));
+        }
+
+        // Draw label of line.
+        if (dataset instanceof XYSeriesCollection
+            && isShowLineLabel(series)
+            && isMinimumX (dataset, series, item)
+            ) {
+            XYSeries xYSeries = ((XYSeriesCollection) dataset).getSeries(series);
+            String waterlevelLabel = (xYSeries instanceof HasLabel)
+                ? ((HasLabel)xYSeries).getLabel()
+                : xYSeries.getKey().toString();
+            // TODO Force water of some German rivers to flow direction mountains.
+
+            Font oldFont = g2.getFont();
+
+            Color oldColor = g2.getColor();
+            g2.setFont(this.getLineLabelFont(series));
+            g2.setColor(this.getLineLabelTextColor(series));
+            g2.setBackground(Color.black);
+
+            // Try to always display label if the data is visible.
+            if (!isPointInRect(dataArea, xx, yy)) {
+                // Move into the data area.
+                xx = Math.max(xx, dataArea.getMinX());
+                xx = Math.min(xx, dataArea.getMaxX());
+                yy = Math.max(yy, dataArea.getMinY());
+                yy = Math.min(yy, dataArea.getMaxY());
+            }
+
+            // Move to right until no collisions exist anymore
+            Shape hotspot = TextUtilities.calculateRotatedStringBounds(
+                waterlevelLabel, g2, (float)xx, (float)yy-3f,
+                TextAnchor.CENTER_LEFT,
+                0f, TextAnchor.CENTER_LEFT);
+            while (JFreeUtil.collides(hotspot, entities,
+                                      CollisionFreeLineLabelEntity.class)) {
+                xx += 5f;
+                hotspot = TextUtilities.calculateRotatedStringBounds(
+                    waterlevelLabel, g2, (float)xx, (float)yy-3f, TextAnchor.CENTER_LEFT,
+                    0f, TextAnchor.CENTER_LEFT);
+            }
+
+            // Register to avoid collissions.
+            entities.add(new CollisionFreeLineLabelEntity(hotspot,
+                1, "", ""));
+
+            // Fill background.
+            if (isShowLineLabelBG(series)) {
+                drawTextBox(g2, waterlevelLabel, (float)xx, (float)yy-3f,
+                    getLineLabelBGColor(series));
+            }
+
+            g2.drawString(waterlevelLabel, (float)xx, (float)yy-3f);
+
+            g2.setFont(oldFont);
+            g2.setColor(oldColor);
+        }
+
+        int domainAxisIndex = plot.getDomainAxisIndex(domainAxis);
+        int rangeAxisIndex = plot.getRangeAxisIndex(rangeAxis);
+        updateCrosshairValues(crosshairState, x1, y1, domainAxisIndex,
+                rangeAxisIndex, transX1, transY1, orientation);
+
+        // Add an entity for the item, but only if it falls within the data
+        // area...
+        if (entities != null && isPointInRect(dataArea, xx, yy)) {
+            addEntity(entities, entityArea, dataset, series, item, xx, yy);
+        }
+    }
+
+
+    /**
+     * Sets whether or not the minimum should be rendered with shape.
+     */
+    public void setIsMinimumShapeVisisble(int series, boolean isVisible) {
+        this.isMinimumShapeVisible.setBoolean(series, isVisible);
+    }
+
+
+    /**
+     * Whether or not the minimum should be rendered with shape.
+     */
+    public boolean isMinimumShapeVisible(int series) {
+        if (this.isMinimumShapeVisible.size() <= series) {
+            return false;
+        }
+
+        return isMinimumShapeVisible.getBoolean(series);
+    }
+
+
+    /**
+     * Sets whether or not the maximum should be rendered with shape.
+     */
+    public void setIsMaximumShapeVisible(int series, boolean isVisible) {
+        this.isMaximumShapeVisible.setBoolean(series, isVisible);
+    }
+
+
+    /**
+     * Whether or not the maximum should be rendered with shape.
+     */
+    public boolean isMaximumShapeVisible(int series) {
+        if (this.isMaximumShapeVisible.size() <= series) {
+            return false;
+        }
+
+        return isMaximumShapeVisible.getBoolean(series);
+    }
+
+    /** Whether or not a label should be shown for series. */
+    public boolean isShowLineLabel(int series) {
+        if (this.showLineLabel.size() <= series) {
+            return false;
+        }
+
+        return showLineLabel.getBoolean(series);
+    }
+
+
+    /** Sets whether or not a label should be shown for series. */
+    public void setShowLineLabel(boolean showLineLabel, int series) {
+        this.showLineLabel.setBoolean(series, showLineLabel);
+    }
+
+
+    /** Whether or not a label should be shown for series. */
+    public boolean isShowLineLabelBG(int series) {
+        if (this.showLineLabelBG.size() <= series) {
+            return false;
+        }
+
+        return showLineLabelBG.getBoolean(series);
+    }
+
+
+    public void setShowLineLabelBG(int series, boolean doShow) {
+        this.showLineLabelBG.setBoolean(series, doShow);
+    }
+
+    public Color getLineLabelBGColor(int series) {
+        if (this.lineLabelBGColors.size() <= series) {
+            return null;
+        }
+
+        return this.lineLabelBGColors.get(series);
+    }
+
+    public void setLineLabelBGColor(int series, Color color) {
+        this.lineLabelBGColors.put(series, color);
+    }
+
+    public Color getLineLabelTextColor(int series) {
+        if (this.lineLabelTextColors.size() <= series) {
+            return null;
+        }
+
+        return this.lineLabelTextColors.get(series);
+    }
+
+    public void setLineLabelTextColor(int series, Color color) {
+        this.lineLabelTextColors.put(series, color);
+    }
+
+    public void setLineLabelFont(Font font, int series) {
+        this.lineLabelFonts.put(series, font);
+    }
+
+    public Font getLineLabelFont(int series) {
+        return this.lineLabelFonts.get(series);
+    }
+
+
+    /**
+     * True if the given item of given dataset has the smallest
+     * X value within this set.
+     */
+    public boolean isMinimumX(XYDataset dataset, int series, int item) {
+        return dataset.getXValue(series, item) == getMinimumX(dataset, series);
+    }
+
+
+    /**
+     * Get Minimum X Value of a given series in a dataset.
+     * The value is stored for later use if queried the first time.
+     */
+    public double getMinimumX(XYDataset dataset, int series) {
+        Integer key = Integer.valueOf(series);
+        Double  old = seriesMinimumX.get(key);
+
+        if (old != null) {
+            return old.doubleValue();
+        }
+
+        logger.debug("Compute minimum of Series: " + series);
+
+        double min = Double.MAX_VALUE;
+
+        for (int i = 0, n = dataset.getItemCount(series); i < n; i++) {
+            double tmpValue = dataset.getXValue(series, i);
+
+            if (tmpValue < min) {
+                min = tmpValue;
+            }
+        }
+
+        seriesMinimumX.put(key, Double.valueOf(min));
+
+        return min;
+    }
+
+
+    /**
+     * True if the given item of given dataset has the smallest
+     * Y value within this set.
+     */
+    public boolean isMinimum(XYDataset dataset, int series, int item) {
+        return dataset.getYValue(series, item) == getMinimum(dataset, series);
+    }
+
+
+    /**
+     * Get Minimum Y Value of a given series in a dataset.
+     * The value is stored for later use if queried the first time.
+     */
+    public double getMinimum(XYDataset dataset, int series) {
+        Integer key = Integer.valueOf(series);
+        Double  old = seriesMinimum.get(key);
+
+        if (old != null) {
+            return old.doubleValue();
+        }
+
+        logger.debug("Compute minimum of Series: " + series);
+
+        double min = Double.MAX_VALUE;
+
+        for (int i = 0, n = dataset.getItemCount(series); i < n; i++) {
+            double tmpValue = dataset.getYValue(series, i);
+
+            if (tmpValue < min) {
+                min = tmpValue;
+            }
+        }
+
+        seriesMinimum.put(key, Double.valueOf(min));
+
+        return min;
+    }
+
+
+    /**
+     * True if the given item of given dataset has the biggest
+     * Y value within this set.
+     */
+    public boolean isMaximum(XYDataset dataset, int series, int item) {
+        return dataset.getYValue(series, item) == getMaximum(dataset, series);
+    }
+
+
+    /**
+     * Get maximum Y Value of a given series in a dataset.
+     * The value is stored for later use if queried the first time.
+     */
+    public double getMaximum(XYDataset dataset, int series) {
+        Integer key = Integer.valueOf(series);
+        Double  old = seriesMaximum.get(key);
+
+        if (old != null) {
+            return old.doubleValue();
+        }
+
+        logger.debug("Compute maximum of Series: " + series);
+
+        double max = -Double.MAX_VALUE;
+
+        for (int i = 0, n = dataset.getItemCount(series); i < n; i++) {
+            double tmpValue = dataset.getYValue(series, i);
+
+            if (tmpValue > max) {
+                max = tmpValue;
+            }
+        }
+
+        seriesMaximum.put(key, Double.valueOf(max));
+
+        return max;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/jfree/FLYSAnnotation.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,107 @@
+package org.dive4elements.river.jfree;
+
+import org.dive4elements.river.artifacts.model.HYKFactory;
+
+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)
+ * and 'HYK'-Annotations (rectangles/areas) with name and theme.
+ */
+public class FLYSAnnotation {
+
+    /** 'Other' Text Annotations. */
+    protected List<XYTextAnnotation> textAnnotations;
+
+    /** Annotations at axis. */
+    protected List<StickyAxisAnnotation> axisTextAnnotations;
+
+    /** Areas at axis. */
+    protected List<HYKFactory.Zone> boxes;
+
+    /** Styling information. */
+    protected Document theme;
+
+    /** Chart-legend information. */
+    protected String label;
+
+
+    public FLYSAnnotation(String label, List<StickyAxisAnnotation> annotations) {
+        this(label, annotations, null, null);
+    }
+
+
+    /** Create annotations, parameter might be null. */
+    public FLYSAnnotation(String label, List<StickyAxisAnnotation> annotations,
+        List<HYKFactory.Zone> bAnnotations
+    ) {
+        this(label, annotations, bAnnotations, null);
+    }
+
+
+    /** Create annotations, parameter might be null. */
+    public FLYSAnnotation(String label, List<StickyAxisAnnotation> annotations,
+        List<HYKFactory.Zone> bAnnotations, Document theme
+    ) {
+        this.label = label;
+        this.axisTextAnnotations = (annotations != null)
+                                   ? annotations
+                                   : Collections.<StickyAxisAnnotation>emptyList();
+        this.boxes = (bAnnotations != null)
+                     ? bAnnotations
+                     : Collections.<HYKFactory.Zone>emptyList();
+        this.textAnnotations = Collections.<XYTextAnnotation>emptyList();
+        this.setTheme(theme);
+    }
+
+
+    public void setLabel(String label) {
+        this.label = label;
+    }
+
+    public String getLabel() {
+        return label;
+    }
+
+    public List<StickyAxisAnnotation> getAxisTextAnnotations() {
+        return axisTextAnnotations;
+    }
+
+    public void setTextAnnotations(List<XYTextAnnotation> annotations) {
+        this.textAnnotations = annotations;
+    }
+
+    /** Set the "other" Text Annotations. */
+    public List<XYTextAnnotation> getTextAnnotations() {
+        return textAnnotations;
+    }
+
+    public List<HYKFactory.Zone> getBoxes() {
+        return boxes;
+    }
+
+    public void setTheme(Document theme) {
+        this.theme = theme;
+    }
+
+    public Document getTheme() {
+        return theme;
+    }
+
+    /**
+     * Set sticky axis of all axisTextAnnotations
+     * to the X axis if it is currently Y, and vice versa.
+     * @return this
+     */
+    public FLYSAnnotation flipStickyAxis() {
+        for (StickyAxisAnnotation saa: axisTextAnnotations) {
+            saa.flipStickyAxis();
+        }
+        return this;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/jfree/HasLabel.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,8 @@
+package org.dive4elements.river.jfree;
+
+/** Interface to say something is labelled. */
+public interface HasLabel {
+    public String getLabel();
+    public void setLabel(String label);
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/jfree/JFreeUtil.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,143 @@
+package org.dive4elements.river.jfree;
+
+import java.awt.Shape;
+import java.awt.geom.Rectangle2D;
+import java.util.Iterator;
+import java.util.Random;
+
+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;
+
+public class JFreeUtil {
+
+    private static final Logger logger = Logger.getLogger(JFreeUtil.class);
+
+    /** Do not instantiate. */
+    private JFreeUtil() {
+    }
+
+
+    /**
+     * True if \param hotspot collides with a Entity in \param entities.
+     * @param hotspot Shape to compare against other shapes (bounds only).
+     * @param entities entities against which to compare shape.
+     * @param exclusiveEntityClass If not null, consider only entities of
+     *        given class.
+     * @return true if a collision (non-zero intersection) exists between
+     *        shapes.
+     */
+    public static boolean collides(Shape hotspot, EntityCollection entities,
+        Class exclusiveEntityClass) {
+        if (entities == null) return false;
+
+        Rectangle2D hotspotBox = hotspot.getBounds2D();
+
+        for (Iterator i = entities.iterator(); i.hasNext(); ) {
+            Object next = i.next();
+            ChartEntity entity = (ChartEntity) next;
+            if (exclusiveEntityClass == null
+                || exclusiveEntityClass.isInstance(entity))
+                {
+                if (entity.getArea().intersects(hotspotBox)) {
+                    // Found collision, early stop.
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+
+    /**
+     * This function samples a randomized line that contains of x and y values
+     * between <i>startX</i>, <i>endX</i>, <i>startY</i> and <i>endY</i>. The
+     * number of points in the line is specified by <i>num</i>.
+     *
+     * @param num The number of points in the line.
+     * @param startX The min value of the x values.
+     * @param endX The max value of the x values.
+     * @param startY The min value of the y values.
+     * @param endY The max value of the y values.
+     * @return an array with [allX-values, allY-values].
+     * @throws IllegalArgumentException
+     */
+    public static double[][] randomizeLine(
+        int    num,
+        double startX,
+        double endX,
+        double startY,
+        double endY
+    ) throws IllegalArgumentException
+    {
+        if (num <= 0) {
+            throw new IllegalArgumentException("Parameter 'num' has to be > 0");
+        }
+
+        Random random = new Random();
+
+        double[] x = new double[num];
+        double[] y = new double[num];
+
+        for (int i = 0; i < num; i++) {
+            double xFac = random.nextDouble();
+            double yFac = random.nextDouble();
+
+            x[i] = startX + xFac * (endX - startX);
+            y[i] = startY + yFac * (endY - startY);
+
+            logger.debug("Created new point: " + x[i] + "|" + y[i]);
+        }
+
+        return new double[][] { x, y };
+    }
+
+
+    public static StyledXYSeries sampleFunction2D(
+        Function func,
+        Document theme,
+        String   seriesKey,
+        int      samples,
+        double   start,
+        double   end
+    ) {
+        StyledXYSeries series = new StyledXYSeries(seriesKey, theme);
+
+        double step = (end - start) / (samples - 1);
+
+        for (int i = 0; i < samples; i++) {
+            double x = start + (step * i);
+            series.add(x, func.value(x));
+        }
+
+        return series;
+    }
+
+    public static StyledXYSeries sampleFunction2DPositive(
+        Function func,
+        Document theme,
+        String   seriesKey,
+        int      samples,
+        double   start,
+        double   end
+    ) {
+        StyledXYSeries series = new StyledXYSeries(seriesKey, theme);
+
+        double step = (end - start) / (samples - 1);
+
+        for (int i = 0; i < samples; i++) {
+            double x = start + (step * i);
+            double v = func.value(x);
+            if (x > 0d && v > 0d) {
+                series.add(x, v);
+            }
+        }
+
+        return series;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/jfree/ShapeRenderer.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,357 @@
+package org.dive4elements.river.jfree;
+
+/**
+ * Copyright (c) 2006, 2012 by Intevation GmbH
+ *
+ * @author Sascha L. Teichmann (teichmann@intevation.de)
+ *
+ * This program is free software under the LGPL (&gt;=v2.1)
+ * Read the file LGPL coming with FLYS for details.
+ */
+
+import java.awt.Font;
+import java.awt.Graphics2D;
+import java.awt.Paint;
+import java.awt.Shape;
+
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.jfree.chart.axis.ValueAxis;
+
+import org.jfree.chart.labels.ItemLabelPosition;
+import org.jfree.chart.labels.XYItemLabelGenerator;
+
+import org.jfree.chart.plot.CrosshairState;
+import org.jfree.chart.plot.PlotOrientation;
+import org.jfree.chart.plot.PlotRenderingInfo;
+import org.jfree.chart.plot.XYPlot;
+
+import org.jfree.chart.renderer.xy.StandardXYItemRenderer;
+import org.jfree.chart.renderer.xy.XYItemRendererState;
+
+import org.jfree.data.xy.XYDataset;
+
+import org.jfree.text.TextUtilities;
+
+import org.jfree.ui.RectangleEdge;
+
+public class ShapeRenderer
+extends      StandardXYItemRenderer {
+
+    public static class Entry {
+        protected Shape   shape;
+        protected Shape   frame;
+        protected Paint   paint;
+        protected boolean filled;
+
+        public Entry(
+            Shape shape,
+            Paint paint,
+            boolean filled
+        ) {
+            this.shape = shape;
+            this.paint = paint;
+            this.filled = filled;
+        }
+
+        public Entry(
+            Shape   shape,
+            Shape   frame,
+            Paint   paint,
+            boolean filled
+        ) {
+            this.shape  = shape;
+            this.frame  = frame;
+            this.paint  = paint;
+            this.filled = filled;
+        }
+
+        public Shape getShape() {
+            return shape;
+        }
+
+        public void setShape(Shape shape) {
+            this.shape = shape;
+        }
+
+
+        public Paint getPaint() {
+            return paint;
+        }
+
+        public void setPaint(Paint paint) {
+            this.paint = paint;
+        }
+
+        public boolean getFilled() {
+            return filled;
+        }
+
+        public void setFilled(boolean filled) {
+            this.filled = filled;
+        }
+
+        public boolean equals(Object other) {
+            Entry entry = (Entry)other;
+            return filled == entry.filled
+                   &&   paint.equals(entry.paint)
+                   &&   shape.equals(entry.shape);
+        }
+
+        public int hashCode() {
+            return
+                shape.hashCode() ^
+                paint.hashCode() ^
+                (filled ? 1231 : 1237);
+        }
+    } // class Entry
+
+    public interface LabelGenerator {
+        String createLabel(Entry entry);
+    } // interface EntryLabelGenerator
+
+    protected Entry []  entries;
+
+    protected List<Rectangle2D> labelBoundingBoxes;
+
+    protected Rectangle2D area;
+
+    public ShapeRenderer() {
+        this(SHAPES);
+    }
+
+    public ShapeRenderer(int type) {
+        super(type);
+    }
+
+    public ShapeRenderer(Map<Entry, Integer> map) {
+        super(SHAPES);
+        setEntries(map);
+    }
+
+    public void setEntries(Entry [] entries) {
+        this.entries = entries;
+    }
+
+    public void setEntries(Map<Entry, Integer> map) {
+        Entry [] entries = new Entry[map.size()];
+
+        for (Map.Entry<Entry, Integer> entry: map.entrySet()) {
+            entries[entry.getValue()] = entry.getKey();
+        }
+
+        setEntries(entries);
+    }
+
+    @Override
+    public Shape getSeriesShape(int series) {
+        return entries[series].shape;
+    }
+
+    public Shape getSeriesFrame(int series) {
+        return entries[series].frame;
+    }
+
+    @Override
+    public Paint getSeriesPaint(int series) {
+        return entries[series].paint;
+    }
+
+    @Override
+    public boolean getItemShapeFilled(int series, int item) {
+        return entries[series].filled;
+    }
+
+    @Override
+    public XYItemRendererState initialise(
+        Graphics2D        g2,
+        Rectangle2D       dataArea,
+        XYPlot            plot,
+        XYDataset         data,
+        PlotRenderingInfo info
+    ) {
+        if (labelBoundingBoxes == null) {
+            labelBoundingBoxes = new ArrayList<Rectangle2D>(32);
+        }
+        else {
+            labelBoundingBoxes.clear();
+        }
+
+        area = dataArea;
+
+        return super.initialise(g2, dataArea, plot, data, info);
+    }
+
+    @Override
+    public void drawItem(
+        Graphics2D          g2,
+        XYItemRendererState state,
+        Rectangle2D         dataArea,
+        PlotRenderingInfo   info,
+        XYPlot              plot,
+        ValueAxis           domainAxis,
+        ValueAxis           rangeAxis,
+        XYDataset           dataset,
+        int                 series,
+        int                 item,
+        CrosshairState      crosshairState,
+        int                 pass
+    ) {
+        if (!getItemVisible(series, item)) {
+            return;
+        }
+
+        // get the data point...
+        double x1 = dataset.getXValue(series, item);
+        double y1 = dataset.getYValue(series, item);
+        if (Double.isNaN(x1) || Double.isNaN(y1)) {
+            return;
+        }
+
+        RectangleEdge xAxisLocation = plot.getDomainAxisEdge();
+        RectangleEdge yAxisLocation = plot.getRangeAxisEdge();
+        double x = domainAxis.valueToJava2D(x1, dataArea, xAxisLocation);
+        double y = rangeAxis.valueToJava2D(y1, dataArea, yAxisLocation);
+
+        if (dataArea.contains(x, y))
+            super.drawItem(
+                g2,
+                state,
+                dataArea,
+                info,
+                plot,
+                domainAxis,
+                rangeAxis,
+                dataset,
+                series,
+                item,
+                crosshairState,
+                pass);
+    }
+
+    protected Point2D shiftBox(Rectangle2D box) {
+
+        double cx1 = area.getX();
+        double cy1 = area.getY();
+        double cx2 = cx1 + area.getWidth();
+        double cy2 = cy1 + area.getHeight();
+
+        double bx1 = box.getX();
+        double by1 = box.getY();
+        double bx2 = bx1 + box.getWidth();
+        double by2 = by1 + box.getHeight();
+
+        double dx;
+        double dy;
+
+        if (bx1 < cx1) {
+            dx = cx1 - bx1;
+        }
+        else if (bx2 > cx2) {
+            dx = cx2 - bx2;
+        }
+        else {
+            dx = 0d;
+        }
+
+        if (by1 < cy1) {
+            dy = cy1 - by1;
+        }
+        else if (by2 > cy2) {
+            dy = cy2 - by2;
+        }
+        else {
+            dy = 0d;
+        }
+
+        return new Point2D.Double(dx, dy);
+    }
+
+    @Override
+    protected void drawItemLabel(
+        Graphics2D      g2,
+        PlotOrientation orientation,
+        XYDataset       dataset,
+        int             series,
+        int             item,
+        double          x,
+        double          y,
+        boolean         negative
+    ) {
+        XYItemLabelGenerator generator = getItemLabelGenerator(series, item);
+        if (generator == null) {
+            return;
+        }
+
+        Font labelFont = getItemLabelFont(series, item);
+
+        Paint paint = getItemLabelPaint(series, item);
+
+        g2.setFont(labelFont);
+        g2.setPaint(paint);
+
+        String label = generator.generateLabel(dataset, series, item);
+
+        ATTEMPS: for (int attempt = 0; attempt < 2; ++attempt) {
+            // get the label position..
+            ItemLabelPosition position = null;
+
+            boolean pos;
+            switch (attempt) {
+                case 0: pos = negative; break;
+                case 1: pos = !negative; break;
+                default: break ATTEMPS;
+            }
+
+            if (pos) {
+                position = getNegativeItemLabelPosition(series, item);
+            }
+            else {
+                position = getPositiveItemLabelPosition(series, item);
+            }
+
+            // work out the label anchor point...
+            Point2D anchorPoint = calculateLabelAnchorPoint(
+                position.getItemLabelAnchor(), x, y, orientation);
+
+            Shape labelShape = TextUtilities.calculateRotatedStringBounds(
+                label, g2,
+                (float)anchorPoint.getX(), (float)anchorPoint.getY(),
+                position.getTextAnchor(), position.getAngle(),
+                position.getRotationAnchor());
+
+            Rectangle2D bbox = labelShape.getBounds2D();
+
+            Point2D shift = shiftBox(bbox);
+
+            bbox = new Rectangle2D.Double(
+                bbox.getX() + shift.getX(),
+                bbox.getY() + shift.getY(),
+                bbox.getWidth(),
+                bbox.getHeight());
+
+            if (labelBoundingBoxes != null) {
+                for (Rectangle2D old: labelBoundingBoxes) {
+                    if (old.intersects(bbox)) {
+                        continue ATTEMPS;
+                    }
+                }
+                labelBoundingBoxes.add(bbox);
+            }
+
+            TextUtilities.drawRotatedString(
+                label, g2,
+                (float)(anchorPoint.getX() + shift.getX()),
+                (float)(anchorPoint.getY() + shift.getY()),
+                position.getTextAnchor(), position.getAngle(),
+                position.getRotationAnchor());
+            break;
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/jfree/StableXYDifferenceRenderer.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,1856 @@
+/* ===========================================================
+ * JFreeChart : a free chart library for the Java(tm) platform
+ * ===========================================================
+ *
+ * (C) Copyright 2000-2008, by Object Refinery Limited and Contributors.
+ *
+ * Project Info:  http://www.jfree.org/jfreechart/index.html
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ *
+ * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
+ * in the United States and other countries.]
+ *
+ * -------------------------
+ * StableXYDifferenceRenderer.java
+ * -------------------------
+ * (C) Copyright 2003-2008, by Object Refinery Limited and Contributors.
+ *
+ * Original Author:  David Gilbert (for Object Refinery Limited);
+ * Contributor(s):   Richard West, Advanced Micro Devices, Inc. (major rewrite
+ *                   of difference drawing algorithm);
+ *
+ * Changes:
+ * --------
+ * 30-Apr-2003 : Version 1 (DG);
+ * 30-Jul-2003 : Modified entity constructor (CZ);
+ * 20-Aug-2003 : Implemented Cloneable and PublicCloneable (DG);
+ * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
+ * 09-Feb-2004 : Updated to support horizontal plot orientation (DG);
+ * 10-Feb-2004 : Added default constructor, setter methods and updated
+ *               Javadocs (DG);
+ * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState (DG);
+ * 30-Mar-2004 : Fixed bug in getNegativePaint() method (DG);
+ * 15-Jul-2004 : Switched getX() with getXValue() and getY() with
+ *               getYValue() (DG);
+ * 25-Aug-2004 : Fixed a bug preventing the use of crosshairs (DG);
+ * 11-Nov-2004 : Now uses ShapeUtilities to translate shapes (DG);
+ * 19-Jan-2005 : Now accesses only primitive values from dataset (DG);
+ * 22-Feb-2005 : Override getLegendItem(int, int) to return "line" items (DG);
+ * 13-Apr-2005 : Fixed shape positioning bug (id = 1182062) (DG);
+ * 20-Apr-2005 : Use generators for legend tooltips and URLs (DG);
+ * 04-May-2005 : Override equals() method, renamed get/setPlotShapes() -->
+ *               get/setShapesVisible (DG);
+ * 09-Jun-2005 : Updated equals() to handle GradientPaint (DG);
+ * 16-Jun-2005 : Fix bug (1221021) affecting stroke used for each series (DG);
+ * ------------- JFREECHART 1.0.x ---------------------------------------------
+ * 24-Jan-2007 : Added flag to allow rounding of x-coordinates, and fixed
+ *               bug in clone() (DG);
+ * 05-Feb-2007 : Added an extra call to updateCrosshairValues() in
+ *               drawItemPass1(), to fix bug 1564967 (DG);
+ * 06-Feb-2007 : Fixed bug 1086307, crosshairs with multiple axes (DG);
+ * 08-Mar-2007 : Fixed entity generation (DG);
+ * 20-Apr-2007 : Updated getLegendItem() for renderer change (DG);
+ * 23-Apr-2007 : Rewrite of difference drawing algorithm to allow use of
+ *               series with disjoint x-values (RW);
+ * 04-May-2007 : Set processVisibleItemsOnly flag to false (DG);
+ * 17-May-2007 : Set datasetIndex and seriesIndex in getLegendItem() (DG);
+ * 18-May-2007 : Set dataset and seriesKey for LegendItem (DG);
+ * 05-Nov-2007 : Draw item labels if visible (RW);
+ * 17-Jun-2008 : Apply legend shape, font and paint attributes (DG);
+ */
+/*
+ * For further changes within the FLYS project, refer to the ChangeLog.
+ */
+package org.dive4elements.river.jfree;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Font;
+import java.awt.Paint;
+import java.awt.geom.Point2D;
+import java.awt.Shape;
+import java.awt.Stroke;
+import java.awt.geom.GeneralPath;
+import java.awt.geom.Line2D;
+import java.awt.geom.Rectangle2D;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.jfree.chart.LegendItem;
+import org.jfree.chart.axis.ValueAxis;
+import org.jfree.chart.entity.EntityCollection;
+import org.jfree.chart.entity.XYItemEntity;
+import org.jfree.chart.event.RendererChangeEvent;
+import org.jfree.chart.labels.XYToolTipGenerator;
+import org.jfree.chart.plot.CrosshairState;
+import org.jfree.chart.plot.PlotOrientation;
+import org.jfree.chart.plot.PlotRenderingInfo;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.chart.urls.XYURLGenerator;
+import org.jfree.data.xy.XYDataset;
+import org.jfree.data.xy.DefaultXYDataset;
+import org.jfree.io.SerialUtilities;
+import org.jfree.ui.RectangleEdge;
+import org.jfree.util.PaintUtilities;
+import org.jfree.util.PublicCloneable;
+import org.jfree.util.ShapeUtilities;
+
+import org.jfree.chart.renderer.xy.AbstractXYItemRenderer;
+import org.jfree.chart.renderer.xy.XYItemRenderer;
+import org.jfree.chart.renderer.xy.XYItemRendererState;
+
+import gnu.trove.TDoubleArrayList;
+
+import org.dive4elements.river.artifacts.math.Linear;
+
+import java.text.NumberFormat;
+
+import org.apache.log4j.Logger;
+
+/**
+ * A renderer for an {@link XYPlot} that highlights the differences between two
+ * series.  The example shown here is generated by the
+ * <code>DifferenceChartDemo1.java</code> program included in the JFreeChart
+ * demo collection:
+ * <br><br>
+ * <img src="../../../../../images/StableXYDifferenceRendererSample.png"
+ * alt="StableXYDifferenceRendererSample.png" />
+ */
+public class StableXYDifferenceRenderer extends AbstractXYItemRenderer
+        implements XYItemRenderer, PublicCloneable {
+
+    private static Logger log = Logger.getLogger(StableXYDifferenceRenderer.class);
+
+    public static final int CALCULATE_POSITIVE_AREA = 1;
+    public static final int CALCULATE_NEGATIVE_AREA = 2;
+    public static final int CALCULATE_ALL_AREA =
+                            CALCULATE_POSITIVE_AREA | CALCULATE_NEGATIVE_AREA;
+
+    /** For serialization. */
+    private static final long serialVersionUID = -8447915602375584857L;
+
+    /** The paint used to highlight positive differences (y(0) > y(1)). */
+    private transient Paint positivePaint;
+
+    /** The paint used to highlight negative differences (y(0) < y(1)). */
+    private transient Paint negativePaint;
+
+    /** Display shapes at each point? */
+    private boolean shapesVisible;
+
+    /** Display shapes at each point? */
+    protected boolean drawOutline;
+
+    /** Which stroke to draw outline with? */
+    protected Stroke outlineStroke;
+
+    /** Which paint to draw outline with? */
+    protected Paint outlinePaint;
+
+    /** The shape to display in the legend item. */
+    private transient Shape legendShape;
+
+    protected boolean drawOriginalSeries;
+
+    /** The color of the label showing the calculated area. */
+    protected Color labelColor;
+
+    /** The background color of the label showing the calculated area. */
+    protected Color labelBGColor;
+
+    /** Font to draw label of calculated area with. */
+    protected Font labelFont;
+
+    /** Template to create i18ned label for area. */
+    protected String areaLabelTamplate;
+
+    /** NumberFormat to use for area. */
+    protected NumberFormat areaLabelNumberFormat;
+
+    protected int areaCalculationMode;
+
+    protected double positiveArea;
+    protected double negativeArea;
+
+    /** Whether or not to draw a label in the area. */
+    protected boolean labelArea = true;
+
+
+    /** Arithmetic centroid of drawn polygons. */
+    protected Point2D.Double centroid;
+
+
+    /** Number of points that contributed to the centroid. */
+    protected int centroidNPoints = 0;
+
+
+    /**
+     * This flag controls whether or not the x-coordinates (in Java2D space)
+     * are rounded to integers.  When set to true, this can avoid the vertical
+     * striping that anti-aliasing can generate.  However, the rounding may not
+     * be appropriate for output in high resolution formats (for example,
+     * vector graphics formats such as SVG and PDF).
+     *
+     * @since 1.0.4
+     */
+    private boolean roundXCoordinates;
+
+    /**
+     * Creates a new renderer with default attributes.
+     */
+    public StableXYDifferenceRenderer() {
+        this(Color.green, Color.red, false /*,  null */);
+    }
+
+    public StableXYDifferenceRenderer(Paint positivePaint, Paint negativePaint,
+                                boolean shapes) {
+        this(positivePaint, negativePaint, shapes, CALCULATE_ALL_AREA);
+    }
+
+    /**
+     * Creates a new renderer.
+     *
+     * @param positivePaint  the highlight color for positive differences
+     *                       (<code>null</code> not permitted).
+     * @param negativePaint  the highlight color for negative differences
+     *                       (<code>null</code> not permitted).
+     * @param shapes  draw shapes?
+     */
+    public StableXYDifferenceRenderer(Paint positivePaint, Paint negativePaint,
+                                boolean shapes, int areaCalculationMode) {
+        if (positivePaint == null) {
+            throw new IllegalArgumentException(
+                    "Null 'positivePaint' argument.");
+        }
+        if (negativePaint == null) {
+            throw new IllegalArgumentException(
+                    "Null 'negativePaint' argument.");
+        }
+        this.positivePaint = positivePaint;
+        this.negativePaint = negativePaint;
+        this.shapesVisible = shapes;
+        this.legendShape   = new Rectangle2D.Double(-3.0, -3.0, 10.0, 10.0);
+        this.roundXCoordinates = false;
+        this.drawOutline   = true;
+        this.outlineStroke = new BasicStroke(1);
+        this.outlinePaint  = Color.black;
+        this.drawOriginalSeries = false;
+        this.areaCalculationMode = areaCalculationMode;
+        this.labelBGColor  = null;
+        this.centroid      = new Point2D.Double(0,0);
+    }
+
+    public int getAreaCalculationMode() {
+        return areaCalculationMode;
+    }
+
+    public void setAreaCalculationMode(int areaCalculationMode) {
+        this.areaCalculationMode = areaCalculationMode;
+    }
+
+
+    /** Set template to use to create area label (e.g. 'Area=%dm2'). */
+    public void setAreaLabelTemplate(String areaTemplate) {
+        this.areaLabelTamplate = areaTemplate;
+    }
+
+
+    public void setAreaLabelNumberFormat(NumberFormat nf) {
+        this.areaLabelNumberFormat = nf;
+    }
+
+
+    public boolean isLabelArea() {
+        return this.labelArea;
+    }
+
+    public void setLabelArea(boolean label) {
+        this.labelArea = label;
+    }
+
+
+    /** Set font to paint label with. */
+    public void setLabelFont(Font font) {
+        this.labelFont = font;
+    }
+
+
+    /** Get font with which label is painted. */
+    public Font getLabelFont() {
+        return this.labelFont;
+    }
+
+
+    /** Set color with which to paint label. */
+    public void setLabelColor(Color color) {
+        this.labelColor = color;
+    }
+
+
+    /** Get color with which label is painted. */
+    public Color getLabelColor() {
+        return this.labelColor;
+    }
+
+
+    /** Set color with which to paint label bg. */
+    public void setLabelBGColor(Color color) {
+        this.labelBGColor = color;
+    }
+
+
+    /** Get color with which label is painted. */
+    public Color getLabelBGColor() {
+        return this.labelBGColor;
+    }
+
+
+    public double getCalculatedArea() {
+        return positiveArea + negativeArea;
+    }
+
+    /**
+     * Sets color that is used if drawOutline is true.
+     */
+    public void setOutlinePaint(Paint outlinePaint) {
+        this.outlinePaint = outlinePaint;
+    }
+
+
+    /**
+     * Gets color which is used if drawOutline is true.
+     */
+    public Paint getOutlinePaint() {
+        return this.outlinePaint;
+    }
+
+
+    /**
+     * Sets Stroke that is used if drawOutline is true.
+     */
+    public void setOutlineStroke(Stroke stroke) {
+        this.outlineStroke = stroke;
+    }
+
+
+    /**
+     * Returns Stroke that is used if drawOutline is true.
+     */
+    public Stroke getOutlineStroke() {
+        return this.outlineStroke;
+    }
+
+
+    /**
+     * Whether or not to draw the 'Shape' of the area (in contrast to
+     * shapes at data items).
+     */
+    public void setDrawOutline(boolean doDrawOutline) {
+        this.drawOutline = doDrawOutline;
+    }
+
+
+    /**
+     * Returns whether or not to draw the shape of the outline.
+     */
+    public boolean getDrawOutline() {
+        return this.drawOutline;
+    }
+
+
+    /**
+     * Returns the paint used to highlight positive differences.
+     *
+     * @return The paint (never <code>null</code>).
+     *
+     * @see #setPositivePaint(Paint)
+     */
+    public Paint getPositivePaint() {
+        return this.positivePaint;
+    }
+
+    /**
+     * Sets the paint used to highlight positive differences and sends a
+     * {@link RendererChangeEvent} to all registered listeners.
+     *
+     * @param paint  the paint (<code>null</code> not permitted).
+     *
+     * @see #getPositivePaint()
+     */
+    public void setPositivePaint(Paint paint) {
+        if (paint == null) {
+            throw new IllegalArgumentException("Null 'paint' argument.");
+        }
+        this.positivePaint = paint;
+        fireChangeEvent();
+    }
+
+    /**
+     * Returns the paint used to highlight negative differences.
+     *
+     * @return The paint (never <code>null</code>).
+     *
+     * @see #setNegativePaint(Paint)
+     */
+    public Paint getNegativePaint() {
+        return this.negativePaint;
+    }
+
+    /**
+     * Sets the paint used to highlight negative differences.
+     *
+     * @param paint  the paint (<code>null</code> not permitted).
+     *
+     * @see #getNegativePaint()
+     */
+    public void setNegativePaint(Paint paint) {
+        if (paint == null) {
+            throw new IllegalArgumentException("Null 'paint' argument.");
+        }
+        this.negativePaint = paint;
+        notifyListeners(new RendererChangeEvent(this));
+    }
+
+    /**
+     * Returns a flag that controls whether or not shapes are drawn for each
+     * data value.
+     *
+     * @return A boolean.
+     *
+     * @see #setShapesVisible(boolean)
+     */
+    public boolean getShapesVisible() {
+        return this.shapesVisible;
+    }
+
+    /**
+     * Sets a flag that controls whether or not shapes are drawn for each
+     * data value, and sends a {@link RendererChangeEvent} to all registered
+     * listeners.
+     *
+     * @param flag  the flag.
+     *
+     * @see #getShapesVisible()
+     */
+    public void setShapesVisible(boolean flag) {
+        this.shapesVisible = flag;
+        fireChangeEvent();
+    }
+
+    /**
+     * Returns the shape used to represent a line in the legend.
+     *
+     * @return The legend line (never <code>null</code>).
+     *
+     * @see #setLegendLine(Shape)
+     */
+    public Shape getLegendLine() {
+        return this.legendShape;
+    }
+
+    /**
+     * Sets the shape used as a line in each legend item and sends a
+     * {@link RendererChangeEvent} to all registered listeners.
+     *
+     * @param line  the line (<code>null</code> not permitted).
+     *
+     * @see #getLegendLine()
+     */
+    public void setLegendLine(Shape line) {
+        if (line == null) {
+            throw new IllegalArgumentException("Null 'line' argument.");
+        }
+        this.legendShape = line;
+        fireChangeEvent();
+    }
+
+    /**
+     * Returns the flag that controls whether or not the x-coordinates (in
+     * Java2D space) are rounded to integer values.
+     *
+     * @return The flag.
+     *
+     * @since 1.0.4
+     *
+     * @see #setRoundXCoordinates(boolean)
+     */
+    public boolean getRoundXCoordinates() {
+        return this.roundXCoordinates;
+    }
+
+    /**
+     * Sets the flag that controls whether or not the x-coordinates (in
+     * Java2D space) are rounded to integer values, and sends a
+     * {@link RendererChangeEvent} to all registered listeners.
+     *
+     * @param round  the new flag value.
+     *
+     * @since 1.0.4
+     *
+     * @see #getRoundXCoordinates()
+     */
+    public void setRoundXCoordinates(boolean round) {
+        this.roundXCoordinates = round;
+        fireChangeEvent();
+    }
+
+    /**
+     * Initialises the renderer and returns a state object that should be
+     * passed to subsequent calls to the drawItem() method.  This method will
+     * be called before the first item is rendered, giving the renderer an
+     * opportunity to initialise any state information it wants to maintain.
+     * The renderer can do nothing if it chooses.
+     *
+     * @param g2  the graphics device.
+     * @param dataArea  the (visible) area inside the axes.
+     * @param plot  the plot.
+     * @param data  the data.
+     * @param info  an optional info collection object to return data back to
+     *              the caller.
+     *
+     * @return A state object.
+     */
+    public XYItemRendererState initialise(Graphics2D g2,
+                                          Rectangle2D dataArea,
+                                          XYPlot plot,
+                                          XYDataset data,
+                                          PlotRenderingInfo info) {
+
+        XYItemRendererState state = super.initialise(g2, dataArea, plot, data,
+                info);
+        state.setProcessVisibleItemsOnly(false);
+        return state;
+    }
+
+    /**
+     * Returns <code>2</code>, the number of passes required by the renderer.
+     * The {@link XYPlot} will run through the dataset this number of times.
+     *
+     * @return The number of passes required by the renderer.
+     */
+    public int getPassCount() {
+        return 2;
+    }
+
+
+    /**
+     * Adds x/y data to series.
+     */
+    private static final void addSeries(
+        DefaultXYDataset ds,
+        Comparable       key,
+        TDoubleArrayList xs,
+        TDoubleArrayList ys
+    ) {
+        ds.addSeries(
+            key,
+            new double [][] {
+                xs.toNativeArray(),
+                ys.toNativeArray()
+            });
+    }
+
+    protected static List<XYDataset> splitByNaNsOneSeries(
+        XYDataset dataset
+    ) {
+        List<XYDataset> datasets = new ArrayList<XYDataset>();
+
+        int N = dataset.getItemCount(0);
+        TDoubleArrayList xs = new TDoubleArrayList(N);
+        TDoubleArrayList ys = new TDoubleArrayList(N);
+        for (int i = 0; i < N; ++i) {
+            double x = dataset.getXValue(0, i);
+            double y = dataset.getYValue(0, i);
+            if (Double.isNaN(x) || Double.isNaN(y)) {
+                if (!xs.isEmpty()) {
+                    DefaultXYDataset ds = new DefaultXYDataset();
+                    addSeries(ds, dataset.getSeriesKey(0), xs, ys);
+                    datasets.add(ds);
+                    xs.resetQuick();
+                    ys.resetQuick();
+                }
+            }
+            else {
+                xs.add(x);
+                ys.add(y);
+            }
+        }
+        if (!xs.isEmpty()) {
+            DefaultXYDataset ds = new DefaultXYDataset();
+            addSeries(ds, dataset.getSeriesKey(0), xs, ys);
+            datasets.add(ds);
+        }
+
+        return datasets;
+    }
+
+    private static final boolean add(TDoubleArrayList xs, double x) {
+        int N = xs.size();
+        if (N == 0 || xs.getQuick(N-1) < x) {
+            xs.add(x);
+            return true;
+        }
+        log.debug("pushed smaller");
+        return false;
+    }
+
+    protected static List<XYDataset> splitByNaNsTwoSeries(
+        XYDataset dataset
+    ) {
+        boolean debug = log.isDebugEnabled();
+
+        List<XYDataset> datasets = new ArrayList<XYDataset>();
+
+        int N = dataset.getItemCount(0);
+        int M = dataset.getItemCount(1);
+
+        int i = 0, j = 0;
+        // ignore leading NaNs
+        for (; i < N; ++i) {
+            double x = dataset.getXValue(0, i);
+            double y = dataset.getYValue(0, i);
+            if (!Double.isNaN(x) && !Double.isNaN(y)) {
+                break;
+            }
+        }
+
+        for (; j < M; ++j) {
+            double x = dataset.getXValue(1, j);
+            double y = dataset.getYValue(1, j);
+            if (!Double.isNaN(x) && !Double.isNaN(y)) {
+                break;
+            }
+        }
+
+        TDoubleArrayList six = new TDoubleArrayList();
+        TDoubleArrayList siy = new TDoubleArrayList();
+        TDoubleArrayList sjx = new TDoubleArrayList();
+        TDoubleArrayList sjy = new TDoubleArrayList();
+
+        while (i < N && j < M) {
+            int ni = i+1;
+            for (; ni < N && !Double.isNaN(dataset.getXValue(0, ni)); ++ni);
+            for (; ni < N &&  Double.isNaN(dataset.getXValue(0, ni)); ++ni);
+
+            int nj = j+1;
+            for (; nj < M && !Double.isNaN(dataset.getXValue(1, nj)); ++nj);
+            for (; nj < M &&  Double.isNaN(dataset.getXValue(1, nj)); ++nj);
+
+            if (ni == N && nj == M) { // no more splits
+                log.debug("no more splits ....");
+                for (; i < ni; ++i) {
+                    double x = dataset.getXValue(0, i);
+                    double y = dataset.getYValue(0, i);
+                    if (!Double.isNaN(x)
+                    &&  !Double.isNaN(y)
+                    &&  add(six, x)) {
+                        siy.add(y);
+                    }
+                }
+                for (; j < nj; ++j) {
+                    double x = dataset.getXValue(1, j);
+                    double y = dataset.getYValue(1, j);
+                    if (!Double.isNaN(x)
+                    &&  !Double.isNaN(y)
+                    &&  add(sjx, x)) {
+                        sjy.add(y);
+                    }
+                }
+                if (!six.isEmpty() && !sjx.isEmpty()) {
+                    DefaultXYDataset ds = new DefaultXYDataset();
+                    addSeries(ds, dataset.getSeriesKey(0), six, siy);
+                    addSeries(ds, dataset.getSeriesKey(1), sjx, sjy);
+                    datasets.add(ds);
+                }
+                break;
+            }
+
+            if (debug) {
+                log.debug("ni: " + ni + " " + N);
+                log.debug("nj: " + nj + " " + M);
+            }
+
+            double xni = ni < N
+                ? dataset.getXValue(0, ni)
+                : Double.MAX_VALUE;
+
+            double xnj = nj < M
+                ? dataset.getXValue(1, nj)
+                : Double.MAX_VALUE;
+
+            double xns = Math.min(xni, xnj);
+
+            double pushxi = Double.NaN;
+            double pushyi = Double.NaN;
+            double pushxj = Double.NaN;
+            double pushyj = Double.NaN;
+
+            for (; i < ni; ++i) {
+                double x = dataset.getXValue(0, i);
+                double y = dataset.getYValue(0, i);
+                if (Double.isNaN(x) || Double.isNaN(y)) {
+                    continue;
+                }
+                if (x < xns) {
+                    if (add(six, x)) {
+                        siy.add(y);
+                    }
+                    continue;
+                }
+                if (x == xns) { // exact match
+                    if (add(six, x)) {
+                        siy.add(y);
+                    }
+                    pushxi = x; pushyi = y;
+                }
+                else { // x > xns: intersection
+                    if (debug) {
+                        log.debug("xns: " + xns);
+                        log.debug("x/y: " + x + " / " + y);
+                    }
+                    int SIX = six.size();
+                    if (SIX > 0) { // should always be true
+                        double yns = Linear.linear(
+                            xns,
+                            six.getQuick(SIX-1), x,
+                            siy.getQuick(SIX-1), y);
+                        if (debug) {
+                            log.debug("intersection at: " + yns);
+                        }
+                        if (add(six, xns)) {
+                            siy.add(yns);
+                        }
+                        pushxi = xns;
+                        pushyi = yns;
+                    }
+                }
+                break; // Split point reached.
+            }
+
+            for (; j < nj; ++j) {
+                double x = dataset.getXValue(1, j);
+                double y = dataset.getYValue(1, j);
+                if (Double.isNaN(x) || Double.isNaN(y)) {
+                    continue;
+                }
+                if (x < xns) {
+                    if (add(sjx, x)) {
+                        sjy.add(y);
+                    }
+                    continue;
+                }
+                if (x == xns) { // exact match
+                    if (add(sjx, x)) {
+                        sjy.add(y);
+                    }
+                    pushxj = x; pushyj = y;
+                }
+                else { // x > xns: intersection
+                    int SJX = sjx.size();
+                    if (SJX > 0) { // should always be true
+                        double yns = Linear.linear(
+                            xns,
+                            sjx.getQuick(SJX-1), x,
+                            sjy.getQuick(SJX-1), y);
+                        if (debug) {
+                            log.debug("intersection at: " + yns);
+                        }
+                        if (add(sjx, xns)) {
+                            sjy.add(yns);
+                        }
+                        pushxj = xns; pushyj = yns;
+                    }
+                }
+                break; // Split point reached.
+            }
+
+            if (!six.isEmpty() && !sjx.isEmpty()) {
+                DefaultXYDataset ds = new DefaultXYDataset();
+                addSeries(ds, dataset.getSeriesKey(0), six, siy);
+                addSeries(ds, dataset.getSeriesKey(1), sjx, sjy);
+                datasets.add(ds);
+            }
+
+            six.resetQuick(); siy.resetQuick();
+            sjx.resetQuick(); sjy.resetQuick();
+
+            // Push split points.
+            if (!Double.isNaN(pushxi)) {
+                six.add(pushxi);
+                siy.add(pushyi);
+            }
+
+            if (!Double.isNaN(pushxj)) {
+                sjx.add(pushxj);
+                sjy.add(pushyj);
+            }
+        }
+
+        // Copy the rest.
+        for (; i < N; ++i) {
+            double x = dataset.getXValue(0, i);
+            double y = dataset.getXValue(0, i);
+            if (!Double.isNaN(x)
+            &&  !Double.isNaN(y)
+            &&  add(six, x)) {
+                siy.add(y);
+            }
+        }
+
+        for (; j < M; ++j) {
+            double x = dataset.getXValue(1, j);
+            double y = dataset.getXValue(1, j);
+            if (!Double.isNaN(x)
+            &&  !Double.isNaN(y)
+            &&  add(sjx, x)) {
+                sjy.add(y);
+            }
+        }
+
+        // Build final dataset.
+        if (!six.isEmpty() && !sjx.isEmpty()) {
+            DefaultXYDataset ds = new DefaultXYDataset();
+            addSeries(ds, dataset.getSeriesKey(0), six, siy);
+            addSeries(ds, dataset.getSeriesKey(1), sjx, sjy);
+            datasets.add(ds);
+        }
+
+        if (debug) {
+            log.debug("datasets after split: " + datasets.size());
+        }
+
+        return datasets;
+    }
+
+    public static List<XYDataset> splitByNaNs(XYDataset dataset)  {
+
+        switch (dataset.getSeriesCount()) {
+            case 0:
+                return Collections.<XYDataset>emptyList();
+            case 1:
+                return splitByNaNsOneSeries(dataset);
+            default: // two or more
+                return splitByNaNsTwoSeries(dataset);
+        }
+    }
+
+
+    /**
+     * Draws the visual representation of a single data item.
+     *
+     * @param g2  the graphics device.
+     * @param state  the renderer state.
+     * @param dataArea  the area within which the data is being drawn.
+     * @param info  collects information about the drawing.
+     * @param plot  the plot (can be used to obtain standard color
+     *              information etc).
+     * @param domainAxis  the domain (horizontal) axis.
+     * @param rangeAxis  the range (vertical) axis.
+     * @param dataset  the dataset.
+     * @param series  the series index (zero-based).
+     * @param item  the item index (zero-based).
+     * @param crosshairState  crosshair information for the plot
+     *                        (<code>null</code> permitted).
+     * @param pass  the pass index.
+     */
+    public void drawItem(Graphics2D g2,
+                         XYItemRendererState state,
+                         Rectangle2D dataArea,
+                         PlotRenderingInfo info,
+                         XYPlot plot,
+                         ValueAxis domainAxis,
+                         ValueAxis rangeAxis,
+                         XYDataset dataset,
+                         int series,
+                         int item,
+                         CrosshairState crosshairState,
+                         int pass) {
+        switch (pass) {
+            case 0:
+                for (XYDataset ds: splitByNaNs(dataset)) {
+                    drawItemPass0(g2, dataArea, info,
+                        plot, domainAxis, rangeAxis,
+                        ds, series, item, crosshairState);
+                }
+                break;
+            case 1:
+                drawItemPass1(g2, dataArea, info,
+                    plot, domainAxis, rangeAxis,
+                    dataset, series, item, crosshairState);
+        }
+
+        // Find geometric middle, calculate area and paint a string with it here.
+        if (pass == 1 && this.labelArea && areaLabelNumberFormat != null && areaLabelTamplate != null) {
+            double center_x = centroid.getX();
+            double center_y = centroid.getY();
+            center_x = domainAxis.valueToJava2D(center_x, dataArea,
+                plot.getDomainAxisEdge());
+            center_y = rangeAxis.valueToJava2D(center_y, dataArea,
+                plot.getRangeAxisEdge());
+
+            // Respect text-extend if text should appear really centered.
+
+            float area = 0f;
+            if (areaCalculationMode == CALCULATE_POSITIVE_AREA
+               || areaCalculationMode == CALCULATE_ALL_AREA) {
+                area += Math.abs(positiveArea);
+            }
+            if (areaCalculationMode == CALCULATE_NEGATIVE_AREA
+               || areaCalculationMode == CALCULATE_ALL_AREA) {
+                area += Math.abs(negativeArea);
+            }
+            if (area != 0f) {
+                Color oldColor = g2.getColor();
+                Font oldFont = g2.getFont();
+                g2.setFont(labelFont);
+                String labelText = String.format(this.areaLabelTamplate,
+                    areaLabelNumberFormat.format(area));
+                if (labelBGColor != null) {
+                    EnhancedLineAndShapeRenderer.drawTextBox(g2, labelText,
+                        (float)center_x, (float)center_y, labelBGColor);
+                }
+                g2.setColor(labelColor);
+                g2.drawString(labelText, (float)center_x, (float)center_y);
+                g2.setFont(oldFont);
+                g2.setColor(oldColor);
+            }
+        }
+    }
+
+    /**
+     * Draws the visual representation of a single data item, first pass.
+     *
+     * @param x_graphics  the graphics device.
+     * @param x_dataArea  the area within which the data is being drawn.
+     * @param x_info  collects information about the drawing.
+     * @param x_plot  the plot (can be used to obtain standard color
+     *                information etc).
+     * @param x_domainAxis  the domain (horizontal) axis.
+     * @param x_rangeAxis  the range (vertical) axis.
+     * @param x_dataset  the dataset.
+     * @param x_series  the series index (zero-based).
+     * @param x_item  the item index (zero-based).
+     * @param x_crosshairState  crosshair information for the plot
+     *                          (<code>null</code> permitted).
+     */
+    protected void drawItemPass0(Graphics2D x_graphics,
+                                 Rectangle2D x_dataArea,
+                                 PlotRenderingInfo x_info,
+                                 XYPlot x_plot,
+                                 ValueAxis x_domainAxis,
+                                 ValueAxis x_rangeAxis,
+                                 XYDataset x_dataset,
+                                 int x_series,
+                                 int x_item,
+                                 CrosshairState x_crosshairState) {
+
+        if (!((0 == x_series) && (0 == x_item))) {
+            return;
+        }
+
+        boolean b_impliedZeroSubtrahend = (1 == x_dataset.getSeriesCount());
+
+        // check if either series is a degenerate case (i.e. less than 2 points)
+        if (isEitherSeriesDegenerate(x_dataset, b_impliedZeroSubtrahend)) {
+            return;
+        }
+
+        // check if series are disjoint (i.e. domain-spans do not overlap)
+        if (!b_impliedZeroSubtrahend && areSeriesDisjoint(x_dataset)) {
+            return;
+        }
+
+        // polygon definitions
+        LinkedList l_minuendXs    = new LinkedList();
+        LinkedList l_minuendYs    = new LinkedList();
+        LinkedList l_subtrahendXs = new LinkedList();
+        LinkedList l_subtrahendYs = new LinkedList();
+        LinkedList l_polygonXs    = new LinkedList();
+        LinkedList l_polygonYs    = new LinkedList();
+
+        // state
+        int l_minuendItem      = 0;
+        int l_minuendItemCount = x_dataset.getItemCount(0);
+        Double l_minuendCurX   = null;
+        Double l_minuendNextX  = null;
+        Double l_minuendCurY   = null;
+        Double l_minuendNextY  = null;
+        double l_minuendMaxY   = Double.NEGATIVE_INFINITY;
+        double l_minuendMinY   = Double.POSITIVE_INFINITY;
+
+        int l_subtrahendItem      = 0;
+        int l_subtrahendItemCount = 0; // actual value set below
+        Double l_subtrahendCurX   = null;
+        Double l_subtrahendNextX  = null;
+        Double l_subtrahendCurY   = null;
+        Double l_subtrahendNextY  = null;
+        double l_subtrahendMaxY   = Double.NEGATIVE_INFINITY;
+        double l_subtrahendMinY   = Double.POSITIVE_INFINITY;
+
+        // if a subtrahend is not specified, assume it is zero
+        if (b_impliedZeroSubtrahend) {
+            l_subtrahendItem      = 0;
+            l_subtrahendItemCount = 2;
+            l_subtrahendCurX      = new Double(x_dataset.getXValue(0, 0));
+            l_subtrahendNextX     = new Double(x_dataset.getXValue(0,
+                    (l_minuendItemCount - 1)));
+            l_subtrahendCurY      = new Double(0.0);
+            l_subtrahendNextY     = new Double(0.0);
+            l_subtrahendMaxY      = 0.0;
+            l_subtrahendMinY      = 0.0;
+
+            l_subtrahendXs.add(l_subtrahendCurX);
+            l_subtrahendYs.add(l_subtrahendCurY);
+        }
+        else {
+            l_subtrahendItemCount = x_dataset.getItemCount(1);
+        }
+
+        boolean b_minuendDone           = false;
+        boolean b_minuendAdvanced       = true;
+        boolean b_minuendAtIntersect    = false;
+        boolean b_minuendFastForward    = false;
+        boolean b_subtrahendDone        = false;
+        boolean b_subtrahendAdvanced    = true;
+        boolean b_subtrahendAtIntersect = false;
+        boolean b_subtrahendFastForward = false;
+        boolean b_colinear              = false;
+
+        boolean b_positive;
+
+        // coordinate pairs
+        double l_x1 = 0.0, l_y1 = 0.0; // current minuend point
+        double l_x2 = 0.0, l_y2 = 0.0; // next minuend point
+        double l_x3 = 0.0, l_y3 = 0.0; // current subtrahend point
+        double l_x4 = 0.0, l_y4 = 0.0; // next subtrahend point
+
+        // fast-forward through leading tails
+        boolean b_fastForwardDone = false;
+        while (!b_fastForwardDone) {
+            // get the x and y coordinates
+            l_x1 = x_dataset.getXValue(0, l_minuendItem);
+            l_y1 = x_dataset.getYValue(0, l_minuendItem);
+            l_x2 = x_dataset.getXValue(0, l_minuendItem + 1);
+            l_y2 = x_dataset.getYValue(0, l_minuendItem + 1);
+
+            l_minuendCurX  = new Double(l_x1);
+            l_minuendCurY  = new Double(l_y1);
+            l_minuendNextX = new Double(l_x2);
+            l_minuendNextY = new Double(l_y2);
+
+            if (b_impliedZeroSubtrahend) {
+                l_x3 = l_subtrahendCurX.doubleValue();
+                l_y3 = l_subtrahendCurY.doubleValue();
+                l_x4 = l_subtrahendNextX.doubleValue();
+                l_y4 = l_subtrahendNextY.doubleValue();
+            }
+            else {
+                l_x3 = x_dataset.getXValue(1, l_subtrahendItem);
+                l_y3 = x_dataset.getYValue(1, l_subtrahendItem);
+                l_x4 = x_dataset.getXValue(1, l_subtrahendItem + 1);
+                l_y4 = x_dataset.getYValue(1, l_subtrahendItem + 1);
+
+                l_subtrahendCurX  = new Double(l_x3);
+                l_subtrahendCurY  = new Double(l_y3);
+                l_subtrahendNextX = new Double(l_x4);
+                l_subtrahendNextY = new Double(l_y4);
+            }
+
+            if (l_x2 <= l_x3) {
+                // minuend needs to be fast forwarded
+                l_minuendItem++;
+                b_minuendFastForward = true;
+                continue;
+            }
+
+            if (l_x4 <= l_x1) {
+                // subtrahend needs to be fast forwarded
+                l_subtrahendItem++;
+                b_subtrahendFastForward = true;
+                continue;
+            }
+
+            // check if initial polygon needs to be clipped
+            if ((l_x3 < l_x1) && (l_x1 < l_x4)) {
+                // project onto subtrahend
+                double l_slope   = (l_y4 - l_y3) / (l_x4 - l_x3);
+                l_subtrahendCurX = l_minuendCurX;
+                l_subtrahendCurY = new Double((l_slope * l_x1)
+                        + (l_y3 - (l_slope * l_x3)));
+
+                l_subtrahendXs.add(l_subtrahendCurX);
+                l_subtrahendYs.add(l_subtrahendCurY);
+            }
+
+            if ((l_x1 < l_x3) && (l_x3 < l_x2)) {
+                // project onto minuend
+                double l_slope = (l_y2 - l_y1) / (l_x2 - l_x1);
+                l_minuendCurX  = l_subtrahendCurX;
+                l_minuendCurY  = new Double((l_slope * l_x3)
+                        + (l_y1 - (l_slope * l_x1)));
+
+                l_minuendXs.add(l_minuendCurX);
+                l_minuendYs.add(l_minuendCurY);
+            }
+
+            l_minuendMaxY    = l_minuendCurY.doubleValue();
+            l_minuendMinY    = l_minuendCurY.doubleValue();
+            l_subtrahendMaxY = l_subtrahendCurY.doubleValue();
+            l_subtrahendMinY = l_subtrahendCurY.doubleValue();
+
+            b_fastForwardDone = true;
+        }
+
+        // start of algorithm
+        while (!b_minuendDone && !b_subtrahendDone) {
+            if (!b_minuendDone && !b_minuendFastForward && b_minuendAdvanced) {
+                l_x1 = x_dataset.getXValue(0, l_minuendItem);
+                l_y1 = x_dataset.getYValue(0, l_minuendItem);
+                l_minuendCurX = new Double(l_x1);
+                l_minuendCurY = new Double(l_y1);
+
+                if (!b_minuendAtIntersect) {
+                    l_minuendXs.add(l_minuendCurX);
+                    l_minuendYs.add(l_minuendCurY);
+                }
+
+                l_minuendMaxY = Math.max(l_minuendMaxY, l_y1);
+                l_minuendMinY = Math.min(l_minuendMinY, l_y1);
+
+                l_x2 = x_dataset.getXValue(0, l_minuendItem + 1);
+                l_y2 = x_dataset.getYValue(0, l_minuendItem + 1);
+                l_minuendNextX = new Double(l_x2);
+                l_minuendNextY = new Double(l_y2);
+            }
+
+            // never updated the subtrahend if it is implied to be zero
+            if (!b_impliedZeroSubtrahend && !b_subtrahendDone
+                    && !b_subtrahendFastForward && b_subtrahendAdvanced) {
+                l_x3 = x_dataset.getXValue(1, l_subtrahendItem);
+                l_y3 = x_dataset.getYValue(1, l_subtrahendItem);
+                l_subtrahendCurX = new Double(l_x3);
+                l_subtrahendCurY = new Double(l_y3);
+
+                if (!b_subtrahendAtIntersect) {
+                    l_subtrahendXs.add(l_subtrahendCurX);
+                    l_subtrahendYs.add(l_subtrahendCurY);
+                }
+
+                l_subtrahendMaxY = Math.max(l_subtrahendMaxY, l_y3);
+                l_subtrahendMinY = Math.min(l_subtrahendMinY, l_y3);
+
+                l_x4 = x_dataset.getXValue(1, l_subtrahendItem + 1);
+                l_y4 = x_dataset.getYValue(1, l_subtrahendItem + 1);
+                l_subtrahendNextX = new Double(l_x4);
+                l_subtrahendNextY = new Double(l_y4);
+            }
+
+            // deassert b_*FastForward (only matters for 1st time through loop)
+            b_minuendFastForward    = false;
+            b_subtrahendFastForward = false;
+
+            Double l_intersectX = null;
+            Double l_intersectY = null;
+            boolean b_intersect = false;
+
+            b_minuendAtIntersect    = false;
+            b_subtrahendAtIntersect = false;
+
+            // check for intersect
+            if ((l_x2 == l_x4) && (l_y2 == l_y4)) {
+                // check if line segments are colinear
+                if ((l_x1 == l_x3) && (l_y1 == l_y3)) {
+                    b_colinear = true;
+                }
+                else {
+                    // the intersect is at the next point for both the minuend
+                    // and subtrahend
+                    l_intersectX = new Double(l_x2);
+                    l_intersectY = new Double(l_y2);
+
+                    b_intersect             = true;
+                    b_minuendAtIntersect    = true;
+                    b_subtrahendAtIntersect = true;
+                 }
+            }
+            else {
+                // compute common denominator
+                double l_denominator = ((l_y4 - l_y3) * (l_x2 - l_x1))
+                        - ((l_x4 - l_x3) * (l_y2 - l_y1));
+
+                // compute common deltas
+                double l_deltaY = l_y1 - l_y3;
+                double l_deltaX = l_x1 - l_x3;
+
+                // compute numerators
+                double l_numeratorA = ((l_x4 - l_x3) * l_deltaY)
+                        - ((l_y4 - l_y3) * l_deltaX);
+                double l_numeratorB = ((l_x2 - l_x1) * l_deltaY)
+                        - ((l_y2 - l_y1) * l_deltaX);
+
+                // check if line segments are colinear
+                if ((0 == l_numeratorA) && (0 == l_numeratorB)
+                        && (0 == l_denominator)) {
+                    b_colinear = true;
+                }
+                else {
+                    // check if previously colinear
+                    if (b_colinear) {
+                        // clear colinear points and flag
+                        l_minuendXs.clear();
+                        l_minuendYs.clear();
+                        l_subtrahendXs.clear();
+                        l_subtrahendYs.clear();
+                        l_polygonXs.clear();
+                        l_polygonYs.clear();
+
+                        b_colinear = false;
+
+                        // set new starting point for the polygon
+                        boolean b_useMinuend = ((l_x3 <= l_x1)
+                                && (l_x1 <= l_x4));
+                        l_polygonXs.add(b_useMinuend ? l_minuendCurX
+                                : l_subtrahendCurX);
+                        l_polygonYs.add(b_useMinuend ? l_minuendCurY
+                                : l_subtrahendCurY);
+                    }
+
+                    // compute slope components
+                    double l_slopeA = l_numeratorA / l_denominator;
+                    double l_slopeB = l_numeratorB / l_denominator;
+
+                    // check if the line segments intersect
+                    if ((0 < l_slopeA) && (l_slopeA <= 1) && (0 < l_slopeB)
+                            && (l_slopeB <= 1)) {
+                        // compute the point of intersection
+                        double l_xi = l_x1 + (l_slopeA * (l_x2 - l_x1));
+                        double l_yi = l_y1 + (l_slopeA * (l_y2 - l_y1));
+
+                        l_intersectX            = new Double(l_xi);
+                        l_intersectY            = new Double(l_yi);
+                        b_intersect             = true;
+                        b_minuendAtIntersect    = ((l_xi == l_x2)
+                                && (l_yi == l_y2));
+                        b_subtrahendAtIntersect = ((l_xi == l_x4)
+                                && (l_yi == l_y4));
+
+                        // advance minuend and subtrahend to intesect
+                        l_minuendCurX    = l_intersectX;
+                        l_minuendCurY    = l_intersectY;
+                        l_subtrahendCurX = l_intersectX;
+                        l_subtrahendCurY = l_intersectY;
+                    }
+                }
+            }
+
+            if (b_intersect) {
+                // create the polygon
+                // add the minuend's points to polygon
+                l_polygonXs.addAll(l_minuendXs);
+                l_polygonYs.addAll(l_minuendYs);
+
+                // add intersection point to the polygon
+                l_polygonXs.add(l_intersectX);
+                l_polygonYs.add(l_intersectY);
+
+                // add the subtrahend's points to the polygon in reverse
+                Collections.reverse(l_subtrahendXs);
+                Collections.reverse(l_subtrahendYs);
+                l_polygonXs.addAll(l_subtrahendXs);
+                l_polygonYs.addAll(l_subtrahendYs);
+
+                // create an actual polygon
+                b_positive = (l_subtrahendMaxY <= l_minuendMaxY)
+                        && (l_subtrahendMinY <= l_minuendMinY);
+                createPolygon(x_graphics, x_dataArea, x_plot, x_domainAxis,
+                        x_rangeAxis, b_positive, l_polygonXs, l_polygonYs);
+
+                // clear the point vectors
+                l_minuendXs.clear();
+                l_minuendYs.clear();
+                l_subtrahendXs.clear();
+                l_subtrahendYs.clear();
+                l_polygonXs.clear();
+                l_polygonYs.clear();
+
+                // set the maxY and minY values to intersect y-value
+                double l_y       = l_intersectY.doubleValue();
+                l_minuendMaxY    = l_y;
+                l_subtrahendMaxY = l_y;
+                l_minuendMinY    = l_y;
+                l_subtrahendMinY = l_y;
+
+                // add interection point to new polygon
+                l_polygonXs.add(l_intersectX);
+                l_polygonYs.add(l_intersectY);
+            }
+
+            // advance the minuend if needed
+            if (l_x2 <= l_x4) {
+                l_minuendItem++;
+                b_minuendAdvanced = true;
+            }
+            else {
+                b_minuendAdvanced = false;
+            }
+
+            // advance the subtrahend if needed
+            if (l_x4 <= l_x2) {
+                l_subtrahendItem++;
+                b_subtrahendAdvanced = true;
+            }
+            else {
+                b_subtrahendAdvanced = false;
+            }
+
+            b_minuendDone    = (l_minuendItem == (l_minuendItemCount - 1));
+            b_subtrahendDone = (l_subtrahendItem == (l_subtrahendItemCount
+                    - 1));
+        }
+
+        // check if the final polygon needs to be clipped
+        if (b_minuendDone && (l_x3 < l_x2) && (l_x2 < l_x4)) {
+            // project onto subtrahend
+            double l_slope    = (l_y4 - l_y3) / (l_x4 - l_x3);
+            l_subtrahendNextX = l_minuendNextX;
+            l_subtrahendNextY = new Double((l_slope * l_x2)
+                    + (l_y3 - (l_slope * l_x3)));
+        }
+
+        if (b_subtrahendDone && (l_x1 < l_x4) && (l_x4 < l_x2)) {
+            // project onto minuend
+            double l_slope = (l_y2 - l_y1) / (l_x2 - l_x1);
+            l_minuendNextX = l_subtrahendNextX;
+            l_minuendNextY = new Double((l_slope * l_x4)
+                    + (l_y1 - (l_slope * l_x1)));
+        }
+
+        // consider last point of minuend and subtrahend for determining
+        // positivity
+        l_minuendMaxY    = Math.max(l_minuendMaxY,
+                l_minuendNextY.doubleValue());
+        l_subtrahendMaxY = Math.max(l_subtrahendMaxY,
+                l_subtrahendNextY.doubleValue());
+        l_minuendMinY    = Math.min(l_minuendMinY,
+                l_minuendNextY.doubleValue());
+        l_subtrahendMinY = Math.min(l_subtrahendMinY,
+                l_subtrahendNextY.doubleValue());
+
+        // add the last point of the minuned and subtrahend
+        l_minuendXs.add(l_minuendNextX);
+        l_minuendYs.add(l_minuendNextY);
+        l_subtrahendXs.add(l_subtrahendNextX);
+        l_subtrahendYs.add(l_subtrahendNextY);
+
+        // create the polygon
+        // add the minuend's points to polygon
+        l_polygonXs.addAll(l_minuendXs);
+        l_polygonYs.addAll(l_minuendYs);
+
+        // add the subtrahend's points to the polygon in reverse
+        Collections.reverse(l_subtrahendXs);
+        Collections.reverse(l_subtrahendYs);
+        l_polygonXs.addAll(l_subtrahendXs);
+        l_polygonYs.addAll(l_subtrahendYs);
+
+        // create an actual polygon
+        b_positive = (l_subtrahendMaxY <= l_minuendMaxY)
+                && (l_subtrahendMinY <= l_minuendMinY);
+        createPolygon(x_graphics, x_dataArea, x_plot, x_domainAxis,
+                x_rangeAxis, b_positive, l_polygonXs, l_polygonYs);
+    }
+
+    /**
+     * Draws the visual representation of a single data item, second pass.  In
+     * the second pass, the renderer draws the lines and shapes for the
+     * individual points in the two series.
+     *
+     * @param x_graphics  the graphics device.
+     * @param x_dataArea  the area within which the data is being drawn.
+     * @param x_info  collects information about the drawing.
+     * @param x_plot  the plot (can be used to obtain standard color
+     *         information etc).
+     * @param x_domainAxis  the domain (horizontal) axis.
+     * @param x_rangeAxis  the range (vertical) axis.
+     * @param x_dataset  the dataset.
+     * @param x_series  the series index (zero-based).
+     * @param x_item  the item index (zero-based).
+     * @param x_crosshairState  crosshair information for the plot
+     *                          (<code>null</code> permitted).
+     */
+    protected void drawItemPass1(Graphics2D x_graphics,
+                                 Rectangle2D x_dataArea,
+                                 PlotRenderingInfo x_info,
+                                 XYPlot x_plot,
+                                 ValueAxis x_domainAxis,
+                                 ValueAxis x_rangeAxis,
+                                 XYDataset x_dataset,
+                                 int x_series,
+                                 int x_item,
+                                 CrosshairState x_crosshairState) {
+
+        Shape l_entityArea = null;
+        EntityCollection l_entities = null;
+        if (null != x_info) {
+            l_entities = x_info.getOwner().getEntityCollection();
+        }
+
+        Paint l_seriesPaint   = getItemPaint(x_series, x_item);
+        Stroke l_seriesStroke = getItemStroke(x_series, x_item);
+        x_graphics.setPaint(l_seriesPaint);
+        x_graphics.setStroke(l_seriesStroke);
+
+        PlotOrientation l_orientation      = x_plot.getOrientation();
+        RectangleEdge l_domainAxisLocation = x_plot.getDomainAxisEdge();
+        RectangleEdge l_rangeAxisLocation  = x_plot.getRangeAxisEdge();
+
+        double l_x0 = x_dataset.getXValue(x_series, x_item);
+        double l_y0 = x_dataset.getYValue(x_series, x_item);
+        double l_x1 = x_domainAxis.valueToJava2D(l_x0, x_dataArea,
+                l_domainAxisLocation);
+        double l_y1 = x_rangeAxis.valueToJava2D(l_y0, x_dataArea,
+                l_rangeAxisLocation);
+
+        // These are the shapes of the series items.
+        if (getShapesVisible()) {
+            Shape l_shape = getItemShape(x_series, x_item);
+            if (l_orientation == PlotOrientation.HORIZONTAL) {
+                l_shape = ShapeUtilities.createTranslatedShape(l_shape,
+                        l_y1, l_x1);
+            }
+            else {
+                l_shape = ShapeUtilities.createTranslatedShape(l_shape,
+                        l_x1, l_y1);
+            }
+            if (l_shape.intersects(x_dataArea)) {
+                x_graphics.setPaint(getItemPaint(x_series, x_item));
+                x_graphics.fill(l_shape);
+                /* TODO We could draw the shapes of single items here.
+                if (drawOutline) {
+                    x_graphics.setPaint(this.outlinePaint);
+                    x_graphics.setStroke(this.outlineStroke);
+                    x_graphics.draw(l_shape);
+                }
+                */
+            }
+            l_entityArea = l_shape;
+        } // if (getShapesVisible())
+
+        // add an entity for the item...
+        if (null != l_entities) {
+            if (null == l_entityArea) {
+                l_entityArea = new Rectangle2D.Double((l_x1 - 2), (l_y1 - 2),
+                        4, 4);
+            }
+            String l_tip = null;
+            XYToolTipGenerator l_tipGenerator = getToolTipGenerator(x_series,
+                    x_item);
+            if (null != l_tipGenerator) {
+                l_tip = l_tipGenerator.generateToolTip(x_dataset, x_series,
+                        x_item);
+            }
+            String l_url = null;
+            XYURLGenerator l_urlGenerator = getURLGenerator();
+            if (null != l_urlGenerator) {
+                l_url = l_urlGenerator.generateURL(x_dataset, x_series,
+                        x_item);
+            }
+            XYItemEntity l_entity = new XYItemEntity(l_entityArea, x_dataset,
+                    x_series, x_item, l_tip, l_url);
+            l_entities.add(l_entity);
+        }
+
+        // draw the item label if there is one...
+        if (isItemLabelVisible(x_series, x_item)) {
+            drawItemLabel(x_graphics, l_orientation, x_dataset, x_series,
+                          x_item, l_x1, l_y1, (l_y1 < 0.0));
+        }
+
+        int l_domainAxisIndex = x_plot.getDomainAxisIndex(x_domainAxis);
+        int l_rangeAxisIndex  = x_plot.getRangeAxisIndex(x_rangeAxis);
+        updateCrosshairValues(x_crosshairState, l_x0, l_y0, l_domainAxisIndex,
+                              l_rangeAxisIndex, l_x1, l_y1, l_orientation);
+
+        if (0 == x_item) {
+            return;
+        }
+
+        double l_x2 = x_domainAxis.valueToJava2D(x_dataset.getXValue(x_series,
+                (x_item - 1)), x_dataArea, l_domainAxisLocation);
+        double l_y2 = x_rangeAxis.valueToJava2D(x_dataset.getYValue(x_series,
+                (x_item - 1)), x_dataArea, l_rangeAxisLocation);
+
+        Line2D l_line = null;
+        if (PlotOrientation.HORIZONTAL == l_orientation) {
+            l_line = new Line2D.Double(l_y1, l_x1, l_y2, l_x2);
+        }
+        else if (PlotOrientation.VERTICAL == l_orientation) {
+            l_line = new Line2D.Double(l_x1, l_y1, l_x2, l_y2);
+        }
+
+        if ((null != l_line) && l_line.intersects(x_dataArea)) {
+            x_graphics.setPaint(getItemPaint(x_series, x_item));
+            x_graphics.setStroke(getItemStroke(x_series, x_item));
+            if (drawOriginalSeries) {
+                x_graphics.setPaint(this.outlinePaint);
+                x_graphics.setStroke(this.outlineStroke);
+                x_graphics.draw(l_line);
+            }
+        }
+    }
+
+    /**
+     * Determines if a dataset is degenerate.  A degenerate dataset is a
+     * dataset where either series has less than two (2) points.
+     *
+     * @param x_dataset  the dataset.
+     * @param x_impliedZeroSubtrahend  if false, do not check the subtrahend
+     *
+     * @return true if the dataset is degenerate.
+     */
+    private boolean isEitherSeriesDegenerate(XYDataset x_dataset,
+            boolean x_impliedZeroSubtrahend) {
+
+        if (x_impliedZeroSubtrahend) {
+            return (x_dataset.getItemCount(0) < 2);
+        }
+
+        return ((x_dataset.getItemCount(0) < 2)
+                || (x_dataset.getItemCount(1) < 2));
+    }
+
+    /**
+     * Determines if the two (2) series are disjoint.
+     * Disjoint series do not overlap in the domain space.
+     *
+     * @param x_dataset  the dataset.
+     *
+     * @return true if the dataset is degenerate.
+     */
+    private boolean areSeriesDisjoint(XYDataset x_dataset) {
+
+        int l_minuendItemCount = x_dataset.getItemCount(0);
+        double l_minuendFirst  = x_dataset.getXValue(0, 0);
+        double l_minuendLast   = x_dataset.getXValue(0, l_minuendItemCount - 1);
+
+        int l_subtrahendItemCount = x_dataset.getItemCount(1);
+        double l_subtrahendFirst  = x_dataset.getXValue(1, 0);
+        double l_subtrahendLast   = x_dataset.getXValue(1,
+                l_subtrahendItemCount - 1);
+
+        return ((l_minuendLast < l_subtrahendFirst)
+                || (l_subtrahendLast < l_minuendFirst));
+    }
+
+
+    public void updateCentroid(Object [] xValues, Object [] yValues) {
+        double x = 0d, y = 0d;
+
+        for (int i = 0, N = xValues.length; i < N; ++i) {
+            x += ((Double)xValues[i]).doubleValue();
+            y += ((Double)yValues[i]).doubleValue();
+        }
+
+        x /= xValues.length;
+        y /= yValues.length;
+
+        centroidNPoints++;
+        double factorNew = 1d / centroidNPoints;
+        double factorOld = 1d - factorNew;
+
+        centroid = new Point2D.Double((factorNew * x + factorOld * centroid.x),
+            (factorNew * y + factorOld * centroid.y));
+    }
+
+
+    public static double calculateArea(Object [] xValues, Object [] yValues) {
+        double area = 0d;
+
+        for (int i = 0, N = xValues.length; i < N; ++i) {
+            int k = (i + 1) % N;
+            double xi = ((Double)xValues[i]).doubleValue();
+            double yi = ((Double)yValues[i]).doubleValue();
+            double xk = ((Double)xValues[k]).doubleValue();
+            double yk = ((Double)yValues[k]).doubleValue();
+
+            area += xi*yk;
+            area -= xk*yi;
+            // TODO centroid calculation here?
+        }
+
+        return 0.5d*area;
+    }
+
+    /**
+     * Draws the visual representation of a polygon
+     *
+     * @param x_graphics  the graphics device.
+     * @param x_dataArea  the area within which the data is being drawn.
+     * @param x_plot  the plot (can be used to obtain standard color
+     *                information etc).
+     * @param x_domainAxis  the domain (horizontal) axis.
+     * @param x_rangeAxis  the range (vertical) axis.
+     * @param x_positive  indicates if the polygon is positive (true) or
+     *                    negative (false).
+     * @param x_xValues  a linked list of the x values (expects values to be
+     *                   of type Double).
+     * @param x_yValues  a linked list of the y values (expects values to be
+     *                   of type Double).
+     */
+    private void createPolygon (Graphics2D x_graphics,
+                                Rectangle2D x_dataArea,
+                                XYPlot      x_plot,
+                                ValueAxis   x_domainAxis,
+                                ValueAxis   x_rangeAxis,
+                                boolean     x_positive,
+                                LinkedList  x_xValues,
+                                LinkedList  x_yValues) {
+
+        PlotOrientation l_orientation      = x_plot.getOrientation();
+        RectangleEdge l_domainAxisLocation = x_plot.getDomainAxisEdge();
+        RectangleEdge l_rangeAxisLocation  = x_plot.getRangeAxisEdge();
+
+        Object[] l_xValues = x_xValues.toArray();
+        Object[] l_yValues = x_yValues.toArray();
+
+        double area = calculateArea(l_xValues, l_yValues)/2d;
+        if (x_positive) positiveArea += area;
+        else            negativeArea += area;
+        updateCentroid(l_xValues, l_yValues);
+
+        GeneralPath l_path = new GeneralPath();
+
+        if (PlotOrientation.VERTICAL == l_orientation) {
+            double l_x = x_domainAxis.valueToJava2D((
+                    (Double) l_xValues[0]).doubleValue(), x_dataArea,
+                    l_domainAxisLocation);
+            if (this.roundXCoordinates) {
+                l_x = Math.rint(l_x);
+            }
+
+            double l_y = x_rangeAxis.valueToJava2D((
+                    (Double) l_yValues[0]).doubleValue(), x_dataArea,
+                    l_rangeAxisLocation);
+
+            l_path.moveTo((float) l_x, (float) l_y);
+            for (int i = 1; i < l_xValues.length; i++) {
+                l_x = x_domainAxis.valueToJava2D((
+                        (Double) l_xValues[i]).doubleValue(), x_dataArea,
+                        l_domainAxisLocation);
+                if (this.roundXCoordinates) {
+                    l_x = Math.rint(l_x);
+                }
+
+                l_y = x_rangeAxis.valueToJava2D((
+                        (Double) l_yValues[i]).doubleValue(), x_dataArea,
+                        l_rangeAxisLocation);
+                l_path.lineTo((float) l_x, (float) l_y);
+            }
+            l_path.closePath();
+        }
+        else {
+            double l_x = x_domainAxis.valueToJava2D((
+                    (Double) l_xValues[0]).doubleValue(), x_dataArea,
+                    l_domainAxisLocation);
+            if (this.roundXCoordinates) {
+                l_x = Math.rint(l_x);
+            }
+
+            double l_y = x_rangeAxis.valueToJava2D((
+                    (Double) l_yValues[0]).doubleValue(), x_dataArea,
+                    l_rangeAxisLocation);
+
+            l_path.moveTo((float) l_y, (float) l_x);
+            for (int i = 1; i < l_xValues.length; i++) {
+                l_x = x_domainAxis.valueToJava2D((
+                        (Double) l_xValues[i]).doubleValue(), x_dataArea,
+                        l_domainAxisLocation);
+                if (this.roundXCoordinates) {
+                    l_x = Math.rint(l_x);
+                }
+
+                l_y = x_rangeAxis.valueToJava2D((
+                        (Double) l_yValues[i]).doubleValue(), x_dataArea,
+                        l_rangeAxisLocation);
+                l_path.lineTo((float) l_y, (float) l_x);
+            }
+            l_path.closePath();
+        }
+
+        if (l_path.intersects(x_dataArea)) {
+            x_graphics.setPaint(x_positive ? getPositivePaint()
+                    : getNegativePaint());
+            x_graphics.fill(l_path);
+            if (drawOutline) {
+                x_graphics.setStroke(this.outlineStroke);
+                x_graphics.setPaint(this.outlinePaint);
+                x_graphics.draw(l_path);
+            }
+        }
+    }
+
+    /**
+     * Returns a default legend item for the specified series.  Subclasses
+     * should override this method to generate customised items.
+     *
+     * @param datasetIndex  the dataset index (zero-based).
+     * @param series  the series index (zero-based).
+     *
+     * @return A legend item for the series.
+     */
+    public LegendItem getLegendItem(int datasetIndex, int series) {
+        LegendItem result = null;
+        XYPlot p = getPlot();
+        if (p != null) {
+            XYDataset dataset = p.getDataset(datasetIndex);
+            if (dataset != null) {
+                if (getItemVisible(series, 0)) {
+                    String label = getLegendItemLabelGenerator().generateLabel(
+                            dataset, series);
+                    String description = label;
+                    String toolTipText = null;
+                    if (getLegendItemToolTipGenerator() != null) {
+                        toolTipText
+                            = getLegendItemToolTipGenerator().generateLabel(
+                                    dataset, series);
+                    }
+                    String urlText = null;
+                    if (getLegendItemURLGenerator() != null) {
+                        urlText = getLegendItemURLGenerator().generateLabel(
+                                dataset, series);
+                    }
+                    // Individualized Paints:
+                    //Paint paint = lookupSeriesPaint(series);
+
+                    // "Area-Style"- Paint.
+                    Paint paint = getPositivePaint();
+                    Stroke stroke = lookupSeriesStroke(series);
+                    Shape line = getLegendLine();
+                    // Not-filled Shape:
+                    //result = new LegendItem(label, description,
+                    //        toolTipText, urlText, line, stroke, paint);
+
+                    if (drawOutline) {
+                        // TODO Include outline style in legenditem (there is a constructor for that)
+                    }
+
+                    // Filled Shape ("Area-Style").
+                    result = new LegendItem(label, description,
+                            toolTipText, urlText, line, paint);
+                    result.setLabelFont(lookupLegendTextFont(series));
+                    Paint labelPaint = lookupLegendTextPaint(series);
+                    if (labelPaint != null) {
+                        result.setLabelPaint(labelPaint);
+                    }
+                    result.setDataset(dataset);
+                    result.setDatasetIndex(datasetIndex);
+                    result.setSeriesKey(dataset.getSeriesKey(series));
+                    result.setSeriesIndex(series);
+                }
+            }
+
+        }
+
+        return result;
+    }
+
+    /**
+     * Tests this renderer for equality with an arbitrary object.
+     *
+     * @param obj  the object (<code>null</code> permitted).
+     *
+     * @return A boolean.
+     */
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (!(obj instanceof StableXYDifferenceRenderer)) {
+            return false;
+        }
+        if (!super.equals(obj)) {
+            return false;
+        }
+        StableXYDifferenceRenderer that = (StableXYDifferenceRenderer) obj;
+        if (!PaintUtilities.equal(this.positivePaint, that.positivePaint)) {
+            return false;
+        }
+        if (!PaintUtilities.equal(this.negativePaint, that.negativePaint)) {
+            return false;
+        }
+        if (this.shapesVisible != that.shapesVisible) {
+            return false;
+        }
+        if (!ShapeUtilities.equal(this.legendShape, that.legendShape)) {
+            return false;
+        }
+        if (this.roundXCoordinates != that.roundXCoordinates) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Returns a clone of the renderer.
+     *
+     * @return A clone.
+     *
+     * @throws CloneNotSupportedException  if the renderer cannot be cloned.
+     */
+    public Object clone() throws CloneNotSupportedException {
+        StableXYDifferenceRenderer clone = (StableXYDifferenceRenderer) super.clone();
+        clone.legendShape = ShapeUtilities.clone(this.legendShape);
+        return clone;
+    }
+
+    /**
+     * Provides serialization support.
+     *
+     * @param stream  the output stream.
+     *
+     * @throws IOException  if there is an I/O error.
+     */
+    private void writeObject(ObjectOutputStream stream) throws IOException {
+        stream.defaultWriteObject();
+        SerialUtilities.writePaint(this.positivePaint, stream);
+        SerialUtilities.writePaint(this.negativePaint, stream);
+        SerialUtilities.writeShape(this.legendShape, stream);
+    }
+
+    /**
+     * Provides serialization support.
+     *
+     * @param stream  the input stream.
+     *
+     * @throws IOException  if there is an I/O error.
+     * @throws ClassNotFoundException  if there is a classpath problem.
+     */
+    private void readObject(ObjectInputStream stream)
+        throws IOException, ClassNotFoundException {
+        stream.defaultReadObject();
+        this.positivePaint = SerialUtilities.readPaint(stream);
+        this.negativePaint = SerialUtilities.readPaint(stream);
+        this.legendShape = SerialUtilities.readShape(stream);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/jfree/StickyAxisAnnotation.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,133 @@
+package org.dive4elements.river.jfree;
+
+/**
+ * Text, position on axis, and maybe a hit-point in a class.
+ *
+ * Idea is to draw a given text and a line to it from either axis.
+ * This class just keeps the info.
+ */
+public class StickyAxisAnnotation {
+
+    /** Simplified view on axes. */
+    public static enum SimpleAxis {
+        X_AXIS, /** Usually "horizontal". */
+        Y_AXIS, /** Usually "vertical". */
+        Y_AXIS2
+    }
+
+    /** The "symbolic" integer representing which axis to stick to. */
+    protected int axisSymbol;
+
+    /** Which axis to stick to. */
+    protected SimpleAxis stickyAxis = SimpleAxis.X_AXIS;
+
+    /** The 1-dimensional position of this annotation. */
+    protected float pos;
+
+    /**
+     * Optional field used when from axis a line should be drawn that
+     * hits a curve or something similar (current scenario: duration curves).
+     * This value is in the "other" dimension than the pos - field.
+     */
+    protected float hitPoint;
+
+    /** The text to display at axis. */
+    String text;
+
+
+    /**
+     * Constructor with implicit sticky x-axis.
+     * @param text the text to display.
+     * @param pos  the position at which to draw the text and mark.
+     */
+    public StickyAxisAnnotation(String text, float pos) {
+        this(text, pos, SimpleAxis.X_AXIS);
+    }
+
+
+    /**
+     * Constructor with given explicit axis.
+     * @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
+     *                   relative).
+     */
+    public StickyAxisAnnotation(String text, float pos, SimpleAxis stickAxis
+    ) {
+        this(text, pos, stickAxis, 0);
+    }
+
+
+    /**
+     * 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
+     *                   relative).
+     */
+    public StickyAxisAnnotation(String text, float pos, SimpleAxis stickAxis,
+            int axisSymbol
+    ) {
+        setStickyAxis(stickAxis);
+        this.text   = text;
+        this.pos    = pos;
+        this.axisSymbol = axisSymbol;
+        this.hitPoint = Float.NaN;
+    }
+
+
+    /**
+     * Sets the "sticky axis" (whether to draw annotations at the
+     * X- or the Y-Axis.
+     *
+     * @param stickyAxis axis to stick to.
+     */
+    public void setStickyAxis(SimpleAxis stickyAxis) {
+        this.stickyAxis = stickyAxis;
+    }
+
+
+    public float getPos() {
+        return this.pos;
+    }
+
+    public SimpleAxis getStickyAxis() {
+        return this.stickyAxis;
+    }
+
+    public boolean atX() {
+        return this.getStickyAxis() == SimpleAxis.X_AXIS;
+    }
+
+    /** Get text to be displayed at axis. */
+    public String getText() {
+        return this.text;
+    }
+
+
+    public int getAxisSymbol() {
+        return this.axisSymbol;
+    }
+
+
+    /** Set where to hit a curve (if any). */
+    public void setHitPoint(float pos) {
+        this.hitPoint = pos;
+    }
+
+    /** Get where to hit a curve (if any). */
+    public float getHitPoint() {
+        return this.hitPoint;
+    }
+
+    /** Set sticky axis to the X axis if it is currently Y, and vice versa. */
+    public void flipStickyAxis() {
+        if (this.getStickyAxis() == SimpleAxis.X_AXIS) {
+            this.setStickyAxis(SimpleAxis.Y_AXIS);
+        }
+        else {
+            this.setStickyAxis(SimpleAxis.X_AXIS);
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/jfree/Style.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,15 @@
+package org.dive4elements.river.jfree;
+
+import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface Style {
+
+    XYLineAndShapeRenderer applyTheme(XYLineAndShapeRenderer r, int idx);
+
+    XYLineAndShapeRenderer getRenderer();
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/jfree/StyledAreaSeriesCollection.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,162 @@
+package org.dive4elements.river.jfree;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+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;
+
+
+/**
+ * One or more dataseries to draw a polygon (either "open up/downwards", or
+ * the area between two curves), a theme-document and further display options.
+ * The theme-document will later "style" the graphical representation.
+ * The display options can be used to control the z-order and the axis of the
+ * dataset.
+ */
+public class StyledAreaSeriesCollection extends XYSeriesCollection {
+    private static final long serialVersionUID = 5274940965666948237L;
+
+    /** Mode, how to draw/which areas to fill. */
+    public enum FILL_MODE {UNDER, ABOVE, BETWEEN};
+
+    /** MODE in use. */
+    protected FILL_MODE mode;
+
+    /** The theme-document with attributes about actual visual representation. */
+    protected Document theme;
+
+
+    /**
+     * @param theme the theme-document.
+     */
+    public StyledAreaSeriesCollection(Document theme) {
+        this.theme = theme;
+        this.mode = FILL_MODE.BETWEEN;
+   }
+
+
+    /** Gets the Fill mode. */
+    public FILL_MODE getMode() {
+        return this.mode;
+    }
+
+
+    /** Sets the Fill mode. */
+    public void setMode(FILL_MODE fMode) {
+        this.mode = fMode;
+    }
+
+
+    /**
+     * Applies line color, size and type attributes to renderer, also
+     * whether to draw lines and/or points.
+     * @param renderer Renderer to apply theme to.
+     * @return \param renderer
+     */
+    public StableXYDifferenceRenderer applyTheme(
+        StableXYDifferenceRenderer renderer
+    ) {
+        applyFillColor(renderer);
+        applyShowShape(renderer);
+        applyOutlineColor(renderer);
+        applyOutlineStyle(renderer);
+        applyShowArea(renderer);
+        if (mode == FILL_MODE.UNDER) {
+            renderer.setAreaCalculationMode(StableXYDifferenceRenderer.CALCULATE_NEGATIVE_AREA);
+        }
+        else if (mode == FILL_MODE.ABOVE) {
+            renderer.setAreaCalculationMode(StableXYDifferenceRenderer.CALCULATE_POSITIVE_AREA);
+        }
+        else {
+            renderer.setAreaCalculationMode(StableXYDifferenceRenderer.CALCULATE_ALL_AREA);
+        }
+
+        // Apply text style.
+        new ThemeAccess(theme).parseTextStyle().apply(renderer);
+        return renderer;
+    }
+
+
+    protected void applyFillColor(StableXYDifferenceRenderer renderer) {
+        Color paint = ThemeUtil.parseColor(
+                ThemeUtil.getAreaBackgroundColorString(theme));
+
+        int transparency = ThemeUtil.parseAreaTransparency(theme);
+        if (transparency > 0 && paint != null) {
+            paint = new Color(
+                        paint.getRed(),
+                        paint.getGreen(),
+                        paint.getBlue(),
+                        (int)((100 - transparency) * 2.55f));
+        }
+
+        if (paint != null && this.getMode() == FILL_MODE.ABOVE) {
+            renderer.setPositivePaint(paint);
+            renderer.setNegativePaint(new Color(0,0,0,0));
+        }
+        else if (paint != null && this.getMode() == FILL_MODE.UNDER) {
+            renderer.setNegativePaint(paint);
+            renderer.setPositivePaint(new Color(0,0,0,0));
+        }
+        else {
+            if (paint == null)
+                paint = new Color(177, 117, 102);
+            renderer.setPositivePaint(paint);
+            renderer.setNegativePaint(paint);
+        }
+    }
+
+
+    protected void applyShowShape(StableXYDifferenceRenderer renderer) {
+        boolean show = ThemeUtil.parseAreaShowBorder(theme);
+        renderer.setDrawOutline(show);
+    }
+
+
+    protected void applyShowLine(StableXYDifferenceRenderer renderer) {
+        boolean show = ThemeUtil.parseShowLine(theme);
+        renderer.setShapesVisible(show);
+    }
+
+
+    protected void applyOutlineColor(StableXYDifferenceRenderer renderer) {
+        Color c = ThemeUtil.parseLineColorField(theme);
+        renderer.setOutlinePaint(c);
+    }
+
+    protected void applyOutlineWidth(StableXYDifferenceRenderer renderer) {
+        int size = ThemeUtil.parseLineWidth(theme);
+    }
+
+    /** Inform renderer whether it should draw a label. */
+    protected void applyShowArea(StableXYDifferenceRenderer renderer) {
+        renderer.setLabelArea(ThemeUtil.parseShowArea(theme));
+    }
+
+    protected void applyOutlineStyle(StableXYDifferenceRenderer renderer) {
+        float[] dashes = ThemeUtil.parseLineStyle(theme);
+        int size       = ThemeUtil.parseLineWidth(theme);
+
+        Stroke stroke = null;
+
+        if (dashes.length <= 1) {
+            stroke = new BasicStroke(Integer.valueOf(size));
+        }
+        else {
+            stroke = new BasicStroke(Integer.valueOf(size),
+                BasicStroke.CAP_BUTT,
+                BasicStroke.JOIN_ROUND,
+                1.0f,
+                dashes,
+                0.0f);
+        }
+
+        renderer.setOutlineStroke(stroke);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/jfree/StyledDomainMarker.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,53 @@
+package org.dive4elements.river.jfree;
+
+import java.awt.Color;
+
+import org.jfree.chart.plot.IntervalMarker;
+import org.w3c.dom.Document;
+
+import org.dive4elements.river.utils.ThemeUtil;
+
+/**
+ * Marker that represents a highlighted interval.
+ *
+ * @author <a href="mailto:christian.lins@intevation.de">Christian Lins</a>
+ */
+public class StyledDomainMarker extends IntervalMarker {
+
+    private static final long serialVersionUID = -4369417661339512342L;
+
+    private final Color backgroundColor, backgroundColor2;
+
+    public StyledDomainMarker(double start, double end, Document theme) {
+        super(start, end);
+
+        backgroundColor = ThemeUtil.parseColor(
+                ThemeUtil.getAreaBackgroundColorString(theme));
+        backgroundColor2 = new Color(
+            255 - backgroundColor.getRed(),
+            255 - backgroundColor.getGreen(),
+            255 - backgroundColor.getBlue());
+        useSecondColor(false);
+
+        int alpha = 100 - ThemeUtil.parseInteger(
+                ThemeUtil.getAreaTransparencyString(theme), 50);
+        setAlpha(alpha / 100.0f);
+    }
+
+    /**
+     * To properly differentiate several styled domain markers side by side,
+     * we can use this switch to toggle between two colors.
+     * @param secondColor
+     */
+    public void useSecondColor(boolean secondColor) {
+        if(!secondColor) {
+            if(backgroundColor != null)
+                setPaint(backgroundColor);
+        }
+        else {
+            if(backgroundColor2 != null)
+                setPaint(backgroundColor2);
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/jfree/StyledSeries.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,13 @@
+package org.dive4elements.river.jfree;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface StyledSeries {
+
+    void setStyle(Style style);
+
+    Style getStyle();
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/jfree/StyledTimeSeries.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,39 @@
+package org.dive4elements.river.jfree;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+
+import org.jfree.data.time.TimeSeries;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class StyledTimeSeries extends TimeSeries implements StyledSeries {
+
+    private static final Logger logger =
+        Logger.getLogger(StyledTimeSeries.class);
+
+
+    protected Style style;
+
+
+    public StyledTimeSeries(String key, Document theme) {
+        super(key);
+        setStyle(new XYStyle(theme));
+    }
+
+
+    @Override
+    public void setStyle(Style style) {
+        this.style = style;
+    }
+
+
+    @Override
+    public Style getStyle() {
+        return style;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/jfree/StyledValueMarker.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,32 @@
+package org.dive4elements.river.jfree;
+
+import org.dive4elements.river.utils.ThemeUtil;
+
+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.
+ * @author <a href="mailto:christian.lins@intevation.de">Christian Lins</a>
+ */
+public class StyledValueMarker extends ValueMarker {
+
+    private static final long serialVersionUID = -3607777705307785140L;
+
+    public StyledValueMarker(double value, Document theme) {
+        super(value);
+
+        Color color = ThemeUtil.parseAreaBackgroundColor(theme);
+        if(color == null) {
+            color = Color.BLACK;
+        }
+        this.setPaint(color);
+
+        int size = ThemeUtil.parsePointWidth(theme);
+        setStroke(new BasicStroke(size));
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/jfree/StyledXYSeries.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,82 @@
+package org.dive4elements.river.jfree;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+
+import org.jfree.data.xy.XYDataItem;
+import org.jfree.data.xy.XYSeries;
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class StyledXYSeries extends XYSeries implements StyledSeries, HasLabel {
+
+    private static final Logger logger = Logger.getLogger(StyledXYSeries.class);
+
+    protected Style style;
+
+    /** If this Series is to be labelled, use this String as label. */
+    protected String label;
+
+
+    public StyledXYSeries(String key, Document theme) {
+        this(key, true, theme);
+        this.label = key.toString();
+    }
+
+
+    public StyledXYSeries(String key, Document theme, XYSeries unstyledSeries) {
+        this(key, theme);
+        add(unstyledSeries);
+    }
+
+
+    /**
+     * @param sorted whether or not to sort the points. Sorting will move NANs
+     *               to one extrema which can cause problems in certain
+     *               algorithms.
+     */
+    public StyledXYSeries(String key, boolean sorted, Document theme) {
+        super(key, sorted);
+        setStyle(new XYStyle(theme));
+        this.label = key.toString();
+    }
+
+
+    @Override
+    public void setStyle(Style style) {
+        this.style = style;
+    }
+
+
+    @Override
+    public Style getStyle() {
+        return style;
+    }
+
+
+    @Override
+    public String getLabel() {
+        return label;
+    }
+
+    @Override
+    public void setLabel(String label) {
+        this.label = label;
+    }
+
+    protected void add(XYSeries series) {
+        List<XYDataItem> items = series.getItems();
+        add(items);
+    }
+
+    protected void add(List<XYDataItem> items) {
+        for(XYDataItem item : items) {
+            add(item.getXValue(), item.getYValue());
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/jfree/TimeBounds.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,88 @@
+package org.dive4elements.river.jfree;
+
+import java.util.Date;
+
+import org.jfree.chart.axis.DateAxis;
+import org.jfree.chart.axis.ValueAxis;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class TimeBounds implements Bounds {
+
+    protected long lower;
+    protected long upper;
+
+
+    public TimeBounds(long lower, long upper) {
+        this.lower = lower;
+        this.upper = upper;
+    }
+
+
+    @Override
+    public Number getLower() {
+        return Long.valueOf(lower);
+    }
+
+
+    public Date getLowerAsDate() {
+        return new Date(lower);
+    }
+
+
+    @Override
+    public Number getUpper() {
+        return Long.valueOf(upper);
+    }
+
+
+    public Date getUpperAsDate() {
+        return new Date(upper);
+    }
+
+
+    @Override
+    public void applyBounds(ValueAxis axis) {
+        DateAxis dateAxis = (DateAxis) axis;
+
+        dateAxis.setMinimumDate(new Date(lower));
+        dateAxis.setMaximumDate(new Date(upper));
+    }
+
+
+    @Override
+    public void applyBounds(ValueAxis axis, int percent) {
+        DateAxis dateAxis = (DateAxis) axis;
+
+        long space = (upper - lower) / 100 * percent;
+
+        dateAxis.setMinimumDate(new Date(lower-space));
+        dateAxis.setMaximumDate(new Date(upper+space));
+    }
+
+
+    @Override
+    public Bounds combine(Bounds bounds) {
+        if (bounds == null) {
+            return this;
+        }
+
+        TimeBounds other = (TimeBounds) bounds;
+
+        long otherLower = other.getLower().longValue();
+        long otherUpper = other.getUpper().longValue();
+
+        return new TimeBounds(
+            otherLower < lower ? otherLower : lower,
+            otherUpper > upper ? otherUpper : upper);
+    }
+
+
+    @Override
+    public String toString() {
+        return "TimeBounds=["+ getLowerAsDate() + " ; " + getUpperAsDate() +"]";
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/jfree/XYStyle.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,229 @@
+package org.dive4elements.river.jfree;
+
+import org.dive4elements.river.utils.ThemeUtil;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.geom.Ellipse2D;
+
+import org.apache.log4j.Logger;
+import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
+import org.w3c.dom.Document;
+
+
+/**
+ * Utility to apply theme-settings to a renderer.
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class XYStyle implements Style {
+
+    private static Logger logger = Logger.getLogger(XYStyle.class);
+
+    protected Document theme;
+
+    protected XYLineAndShapeRenderer renderer;
+
+
+    public XYStyle(Document theme) {
+        this.theme = theme;
+        this.renderer = null;
+    }
+
+
+    /**
+     * Applies line color, size and type attributes to renderer, also
+     * whether to draw lines and/or points.
+     */
+    @Override
+    public XYLineAndShapeRenderer applyTheme(XYLineAndShapeRenderer r, int idx){
+        this.renderer = r;
+        applyLineColor(r, idx);
+        applyLineSize(r, idx);
+        applyLineType(r, idx);
+        applyShowLine(r, idx);
+        applyShowPoints(r, idx);
+        applyPointSize(r, idx);
+        applyPointColor(r, idx);
+        applyShowMinimum(r, idx);
+        applyShowMaximum(r, idx);
+
+        // Line label styles
+        applyShowLineLabel(r, idx);
+        applyShowLineLabelBG(r, idx);
+        applyLineLabelFont(r, idx);
+        applyLineLabelColor(r, idx);
+        applyLineLabelBGColor(r, idx);
+
+        // Point label styles
+        // TODO: Currently point label are annotations and are not drawn this way
+        /*
+        applyShowPointLabelBG(r, idx);
+        applyLinePointFont(r, idx);
+        applyLinePointColor(r, idx);
+        applyLinePointBGColor(r, idx);*/
+
+        return r;
+    }
+
+
+    /** Set line color to renderer. */
+    protected void applyLineColor(XYLineAndShapeRenderer r, int idx) {
+        Color c = ThemeUtil.parseLineColorField(theme);
+        if(c != null) {
+            logger.debug("applyLineColor " + c.toString());
+            r.setSeriesPaint(idx, c);
+        }
+        else {
+            logger.warn("applyLineColor: color is null - malformed linecolor field?");
+        }
+    }
+
+
+    /** Tells the renderer whether or not to add a label to a line. */
+    protected void applyShowLineLabel(XYLineAndShapeRenderer r, int idx) {
+        if (!(r instanceof EnhancedLineAndShapeRenderer)) {
+            return;
+        }
+        boolean showLabelLine = ThemeUtil.parseShowLineLabel(theme);
+        boolean anyLabel = showLabelLine || ThemeUtil.parseShowWidth(theme) ||
+                           ThemeUtil.parseShowLevel(theme) ||
+                           ThemeUtil.parseShowMiddleHeight(theme);
+        ((EnhancedLineAndShapeRenderer)r).setShowLineLabel(anyLabel, idx);
+    }
+
+
+    /** Tells the renderer whether or not to fill the bg of a lines label. */
+    protected void applyShowLineLabelBG(XYLineAndShapeRenderer r, int idx) {
+        if (!(r instanceof EnhancedLineAndShapeRenderer)) {
+            return;
+        }
+        boolean showLabelLine = ThemeUtil.parseLabelShowBackground(theme);
+        ((EnhancedLineAndShapeRenderer)r).setShowLineLabelBG(idx, showLabelLine);
+    }
+
+    /** Tell the renderer which font (and -size and -style) to use for
+     * linelabels. */
+    protected void applyLineLabelFont(XYLineAndShapeRenderer r, int idx) {
+        if (!(r instanceof EnhancedLineAndShapeRenderer)) {
+            return;
+        }
+        ((EnhancedLineAndShapeRenderer)r).setLineLabelFont(
+                ThemeUtil.parseTextFont(theme), idx);
+    }
+
+    /** Tell the renderer which color to use for
+     * linelabels. */
+    protected void applyLineLabelColor(XYLineAndShapeRenderer r, int idx) {
+        if (!(r instanceof EnhancedLineAndShapeRenderer)) {
+            return;
+        }
+        ((EnhancedLineAndShapeRenderer)r).setLineLabelTextColor(
+                idx, ThemeUtil.parseTextColor(theme));
+    }
+
+    /** Tell the renderer which color to use for bg of
+     * linelabels. */
+    protected void applyLineLabelBGColor(XYLineAndShapeRenderer r, int idx) {
+        if (!(r instanceof EnhancedLineAndShapeRenderer)) {
+            return;
+        }
+        ((EnhancedLineAndShapeRenderer)r).setLineLabelBGColor(idx,
+            ThemeUtil.parseTextBackground(theme));
+    }
+
+    /** Set stroke of series. */
+    protected void applyLineSize(XYLineAndShapeRenderer r, int idx) {
+        int size = ThemeUtil.parseLineWidth(theme);
+        r.setSeriesStroke(
+            idx,
+            new BasicStroke(size));
+    }
+
+
+    /** Set stroke strength of series. */
+    protected void applyLineType(XYLineAndShapeRenderer r, int idx) {
+        int size = ThemeUtil.parseLineWidth(theme);
+        float[] dashes = ThemeUtil.parseLineStyle(theme);
+
+        // Do not apply the dashed style.
+        if (dashes.length <= 1) {
+            return;
+        }
+
+        r.setSeriesStroke(
+            idx,
+            new BasicStroke(size,
+                            BasicStroke.CAP_BUTT,
+                            BasicStroke.JOIN_ROUND,
+                            1.0f,
+                            dashes,
+                            0.0f));
+    }
+
+
+    protected void applyPointSize(XYLineAndShapeRenderer r, int idx) {
+        int size = ThemeUtil.parsePointWidth(theme);
+        int dim  = 2 * size;
+
+        r.setSeriesShape(idx, new Ellipse2D.Double(-size, -size, dim, dim));
+    }
+
+
+    protected void applyPointColor(XYLineAndShapeRenderer r, int idx) {
+        Color c = ThemeUtil.parsePointColor(theme);
+
+        if (c != null) {
+            r.setSeriesFillPaint(idx, c);
+            r.setUseFillPaint(true);
+            r.setDrawOutlines(false);
+        }
+    }
+
+
+    /**
+     * Sets form and visibility of points.
+     */
+    protected void applyShowPoints(XYLineAndShapeRenderer r, int idx) {
+        boolean show = ThemeUtil.parseShowPoints(theme);
+
+        r.setSeriesShapesVisible(idx, show);
+        r.setDrawOutlines(true);
+    }
+
+
+    protected void applyShowLine(XYLineAndShapeRenderer r, int idx) {
+        boolean show = ThemeUtil.parseShowLine(theme);
+        r.setSeriesLinesVisible(idx, show);
+    }
+
+
+    protected void applyShowMinimum(XYLineAndShapeRenderer r, int idx) {
+        if (!(r instanceof EnhancedLineAndShapeRenderer)) {
+            return;
+        }
+
+        boolean visible = ThemeUtil.parseShowMinimum(theme);
+
+        EnhancedLineAndShapeRenderer er = (EnhancedLineAndShapeRenderer) r;
+        er.setIsMinimumShapeVisisble(idx, visible);
+    }
+
+
+    protected void applyShowMaximum(XYLineAndShapeRenderer r, int idx) {
+        if (!(r instanceof EnhancedLineAndShapeRenderer)) {
+            return;
+        }
+
+        boolean visible = ThemeUtil.parseShowMaximum(theme);
+
+        EnhancedLineAndShapeRenderer er = (EnhancedLineAndShapeRenderer) r;
+        er.setIsMaximumShapeVisible(idx, visible);
+    }
+
+
+    @Override
+    public XYLineAndShapeRenderer getRenderer() {
+        return this.renderer;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/themes/DefaultTheme.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,184 @@
+package org.dive4elements.river.themes;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DefaultTheme implements Theme {
+
+    /** The name of the theme.*/
+    protected String name;
+
+    /** The description of the theme.*/
+    protected String description;
+
+    protected String facet;
+
+    protected int index;
+
+
+    /** The map storing the fields of this theme.*/
+    protected Map<String, ThemeField> fields;
+
+    /** The map storing the attributes of this theme.*/
+    protected Map<String, String> attr;
+
+
+    /**
+     * Initializes the components of this Theme.
+     */
+    public DefaultTheme(String name, String description) {
+        this.name        = name;
+        this.description = description;
+        this.fields      = new HashMap<String, ThemeField>();
+        this.attr        = new HashMap<String, String>();
+    }
+
+
+    public void init(Node config) {
+    }
+
+
+    public String getName() {
+        return name;
+    }
+
+
+    public String getDescription() {
+        return description;
+    }
+
+
+    public String getFacet() {
+        return facet;
+    }
+
+
+    public void setFacet(String facet) {
+        this.facet = facet;
+    }
+
+
+    public int getIndex() {
+        return index;
+    }
+
+
+    public void setIndex(int index) {
+        this.index = index;
+    }
+
+
+    public void addAttribute(String name, String value) {
+        if (name != null && value != null) {
+            attr.put(name, value);
+        }
+    }
+
+
+    public String getAttribute(String name) {
+        return attr.get(name);
+    }
+
+
+    public void addField(String name, ThemeField field) {
+        if (name != null && field != null) {
+            fields.put(name, field);
+        }
+    }
+
+
+    public void setFieldValue(String name, Object value) {
+        if (name != null && value != null) {
+            ThemeField field = fields.get(name);
+
+            if (field != null) {
+                field.setValue(value);
+            }
+        }
+    }
+
+
+    public ThemeField getField(String name) {
+        return fields.get(name);
+    }
+
+
+    public String getFieldType(String name) {
+        ThemeField field = fields.get(name);
+
+        return field != null ? field.getType() : null;
+    }
+
+
+    public Object getFieldValue(String name) {
+        ThemeField field = fields.get(name);
+
+        return field != null ? field.getValue() : null;
+    }
+
+
+    public Document toXML() {
+        Document doc = XMLUtils.newDocument();
+
+        ElementCreator cr = new ElementCreator(doc, null, null);
+
+        Element theme = cr.create("theme");
+        theme.setAttribute("facet", facet);
+        theme.setAttribute("index", String.valueOf(index));
+
+        appendAttributes(cr, theme);
+        appendFields(cr, theme);
+
+        doc.appendChild(theme);
+
+        return doc;
+    }
+
+
+    /**
+     * Appends the attributes configured for this theme.
+     *
+     * @param cr The ElementCreator.
+     * @param theme The document root element.
+     */
+    protected void appendAttributes(ElementCreator cr, Element theme) {
+
+        for (Map.Entry<String, String> entry: attr.entrySet()) {
+            String key = entry.getKey();
+            String val = entry.getValue();
+
+            if (key != null && val != null) {
+                cr.addAttr(theme, key, val);
+            }
+        }
+    }
+
+
+    /**
+     * Appends the fields configured for this theme.
+     *
+     * @param cr The ElementCreator.
+     * @param theme The document root element.
+     */
+    protected void appendFields(ElementCreator cr, Element theme) {
+
+        for (ThemeField field: fields.values()) {
+            Document doc = field.toXML();
+            Node    root = doc.getFirstChild();
+
+            theme.appendChild(theme.getOwnerDocument().importNode(root, true));
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/themes/DefaultThemeField.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,79 @@
+package org.dive4elements.river.themes;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DefaultThemeField implements ThemeField {
+
+    protected String name;
+
+    protected Map<String, Object> attr;
+
+
+    public DefaultThemeField(String name) {
+        this.name = name;
+        this.attr = new HashMap<String, Object>();
+    }
+
+
+    public String getName() {
+        return name;
+    }
+
+
+    public String getType() {
+        return (String) getAttribute("type");
+    }
+
+
+    public Object getValue() {
+        return getAttribute("value");
+    }
+
+
+    public void setValue(Object value) {
+        setAttribute("value", value);
+    }
+
+
+    public Object getAttribute(String name) {
+        return attr.get(name);
+    }
+
+
+    public void setAttribute(String name, Object value) {
+        if (name == null || value == null) {
+            return;
+        }
+
+        attr.put(name, value);
+    }
+
+
+    public Document toXML() {
+        Document doc = XMLUtils.newDocument();
+
+        ElementCreator cr = new ElementCreator(doc, null, null);
+
+        Element field = cr.create("field");
+
+        for (Map.Entry<String, Object> entry: attr.entrySet()) {
+            cr.addAttr(field, entry.getKey(), (String)entry.getValue());
+        }
+
+        doc.appendChild(field);
+
+        return doc;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/themes/LineStyle.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,21 @@
+package org.dive4elements.river.themes;
+
+import java.awt.Color;
+
+public class LineStyle {
+    protected Color lineColor;
+    protected int   lineWidth;
+
+    public LineStyle(Color color, int width) {
+        this.lineColor = color;
+        this.lineWidth = width;
+    }
+
+    public int getWidth() {
+        return lineWidth;
+    }
+
+    public Color getColor() {
+        return lineColor;
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/themes/PointStyle.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,5 @@
+package org.dive4elements.river.themes;
+
+public class PointStyle {
+    // TODO tbd
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/themes/TextStyle.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,52 @@
+package org.dive4elements.river.themes;
+
+import org.dive4elements.river.jfree.StableXYDifferenceRenderer;
+
+import java.awt.Color;
+import java.awt.Font;
+
+import org.apache.log4j.Logger;
+import org.jfree.chart.annotations.XYTextAnnotation;
+
+public class TextStyle {
+    @SuppressWarnings("unused")
+    private static Logger log = Logger.getLogger(TextStyle.class);
+
+    protected Color   textColor;
+    protected Font    font;
+    protected Color   bgColor;
+    protected boolean showBg;
+    protected boolean isVertical;
+
+    public TextStyle(Color fgColor, Font font, Color bgColor,
+        boolean showBg, boolean isVertical
+    ) {
+        this.textColor  = fgColor;
+        this.font       = font;
+        this.bgColor    = bgColor;
+        this.showBg     = showBg;
+        this.isVertical = isVertical;
+    }
+
+    public void apply(XYTextAnnotation ta) {
+        ta.setPaint(textColor);
+        ta.setFont(font);
+        if (this.showBg) {
+            ta.setBackgroundPaint(bgColor);
+        }
+        if (this.isVertical) {
+            ta.setRotationAngle(270f*Math.PI/180f);
+        }
+        else {
+            ta.setRotationAngle(0);
+        }
+    }
+
+    public void apply(StableXYDifferenceRenderer renderer) {
+        renderer.setLabelColor(textColor);
+        renderer.setLabelFont(font);
+        if (this.showBg) {
+            renderer.setLabelBGColor(bgColor);
+        }
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/themes/Theme.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,119 @@
+package org.dive4elements.river.themes;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface Theme {
+
+    /**
+     * Method to initialize the theme.
+     *
+     * @param config The configuration node.
+     */
+    void init(Node config);
+
+
+    /**
+     * Returns the name of the theme.
+     *
+     * @return the name of the theme.
+     */
+    String getName();
+
+
+    /**
+     * Returns the description of the theme.
+     *
+     * @return the description of the theme.
+     */
+    String getDescription();
+
+
+    String getFacet();
+
+    void setFacet(String facet);
+
+    int getIndex();
+
+    void setIndex(int index);
+
+
+    /**
+     * Adds a new attribute.
+     *
+     * @param name The name of the attribute.
+     * @param value The value of the attribute.
+     */
+    void addAttribute(String name, String value);
+
+
+    /**
+     * Returns the value of a specific attribute.
+     *
+     * @param name the name of the attribute.
+     *
+     * @return the value of the attribute <i>name</i>.
+     */
+    String getAttribute(String name);
+
+
+    /**
+     * Adds a new field to the theme.
+     *
+     * @param name The name of the field.
+     * @param field The field.
+     */
+    void addField(String name, ThemeField field);
+
+
+    /**
+     * Sets the value of an field.
+     *
+     * @param name The name of the field.
+     * @param value The new value of the field.
+     */
+    void setFieldValue(String name, Object value);
+
+
+    /**
+     * Returns the field specified by name.
+     *
+     * @param name The name of the desired field.
+     *
+     * @return an field.
+     */
+    ThemeField getField(String name);
+
+
+    /**
+     * Returns the typename of a field.
+     *
+     * @param name the name of the field.
+     *
+     * @return the typename of a field.
+     */
+    String getFieldType(String name);
+
+
+    /**
+     * Returns the value of a field.
+     *
+     * @param name The name of the field.
+     *
+     * @return the value of a field.
+     */
+    Object getFieldValue(String name);
+
+
+    /**
+     * Dumps the theme to XML.
+     *
+     * @return a document.
+     */
+    Document toXML();
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/themes/ThemeAccess.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,124 @@
+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/flys-artifacts/src/main/java/org/dive4elements/river/themes/ThemeFactory.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,312 @@
+package org.dive4elements.river.themes;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.context.FLYSContext;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.xpath.XPathConstants;
+
+import org.apache.log4j.Logger;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ *
+ * Mapping-matching rules:
+ *
+ */
+public class ThemeFactory {
+
+    private static Logger logger = Logger.getLogger(ThemeFactory.class);
+
+    /** Trivial, hidden constructor. */
+    private ThemeFactory() {
+    }
+
+
+    /**
+     * Creates a new theme from <i>config</i>.
+     *
+     * @param themeCfg The theme config document that is used to fetch parent
+     * themes.
+     * @param config The theme config node.
+     *
+     * @return a new theme.
+     */
+    public static Theme createTheme(Document themeCfg, Node config) {
+        String name = getName(config);
+        String desc = getDescription(config);
+
+        logger.debug("Create new theme: " + name);
+
+        Theme theme = new DefaultTheme(name, desc);
+
+        parseInherits(themeCfg, config, theme);
+        parseFields(config, theme);
+        parseAttrs(config, theme);
+
+        return theme;
+    }
+
+
+    /**
+     * Get first matching theme for facet.
+     *
+     * @param name    Name to match "from" of theme mapping.
+     * @param pattern String to 'compare' to pattern in mapping.
+     * @param output  Name of the current output
+     *
+     * @return First matching theme.
+     */
+    public static Theme getTheme(
+        FLYSContext c,
+        String name,
+        String pattern,
+        String output,
+        String groupName)
+    {
+        if (logger.isDebugEnabled()) {
+            logger.debug(
+                "Search theme for: " + name + " - pattern: " + pattern);
+        }
+
+        if (c == null || name == null) {
+            logger.warn("Cannot search for theme.");
+            return null;
+        }
+
+        // Fetch mapping and themes.
+        @SuppressWarnings("unchecked")
+        Map<String, List<ThemeMapping>> map = (Map<String, List<ThemeMapping>>)
+            c.get(FLYSContext.THEME_MAPPING);
+
+        @SuppressWarnings("unchecked")
+        List<ThemeGroup> tgs = (List<ThemeGroup>)
+            c.get(FLYSContext.THEMES);
+
+        ThemeGroup group = null;
+        for (ThemeGroup tg: tgs) {
+            if (tg.getName().equals(groupName)) {
+                group = tg;
+                break;
+            }
+        }
+
+        if (group == null) {
+            logger.warn("No theme group found: '" + groupName + "'");
+            return null;
+        }
+
+        Map<String, Theme> t = group.getThemes();
+
+        FLYSArtifact artifact = (FLYSArtifact) c.get(FLYSContext.ARTIFACT_KEY);
+
+        if (map == null || map.isEmpty() || t == null || t.isEmpty()) {
+            logger.warn("No mappings or themes found. Cannot retrieve theme!");
+            return null;
+        }
+
+        List<ThemeMapping> mapping = map.get(name);
+
+        if (mapping == null) {
+            logger.warn("No theme found for mapping: " + name);
+            return null;
+        }
+
+        // Take first mapping of which all conditions are satisfied.
+        for (ThemeMapping tm: mapping) {
+            if (name.equals(tm.getFrom())
+                && tm.applyPattern(pattern)
+                && tm.masterAttrMatches(artifact)
+                && tm.outputMatches(output))
+            {
+                String target = tm.getTo();
+
+                logger.debug("Found theme '" + target + "'");
+                return t.get(target);
+            }
+        }
+
+        String msg =
+            "No theme found for '" + name +
+            "' with pattern '" + pattern + "' and output " + output + ".";
+
+        logger.warn(msg);
+
+        return null;
+    }
+
+
+    @SuppressWarnings("unchecked")
+    public static List<ThemeGroup> getThemeGroups(FLYSContext c) {
+        List<ThemeGroup> tgs = (List<ThemeGroup>)
+            c.get(FLYSContext.THEMES);
+        return tgs;
+    }
+
+
+    @SuppressWarnings("unchecked")
+    public static List<Theme> getThemes (FLYSContext c, String name) {
+        List<ThemeGroup> tgs = (List<ThemeGroup>)
+            c.get(FLYSContext.THEMES);
+        if (tgs == null) {
+            return null;
+        }
+
+        List<Theme> themes = new ArrayList<Theme>();
+        for (ThemeGroup tg: tgs) {
+            themes.add(tg.getThemeByName(name));
+        }
+        return themes;
+    }
+
+    protected static String getName(Node config) {
+        return ((Element)config).getAttribute("name");
+    }
+
+
+    protected static String getDescription(Node config) {
+        return ((Element)config).getAttribute("desc");
+    }
+
+
+    protected static void parseInherits(Document themeCfg, Node cfg, Theme t) {
+        parseInherits(themeCfg, cfg, t, null);
+    }
+
+    protected static void parseInherits(
+        Document themeCfg,
+        Node     cfg,
+        Theme    t,
+        Map<String, Node> themes
+    ) {
+        logger.debug("ThemeFactory.parseInherits");
+
+        NodeList inherits = ((Element)cfg).getElementsByTagName("inherit");
+
+        int num = inherits.getLength();
+
+        if (num == 0) {
+            logger.debug("Theme does not inherit from other themes.");
+            return;
+        }
+
+        logger.debug("Theme inherits from " + num + " other themes.");
+
+        if (themes == null) {
+            themes = buildThemeMap(themeCfg);
+        }
+
+        for (int i = 0; i < num; i++) {
+            Node inherit = inherits.item(i);
+            String from = ((Element)inherit).getAttribute("from");
+
+            Node tmp = themes.get(from);
+
+            parseInherits(themeCfg, tmp, t, themes);
+            parseFields(tmp, t);
+        }
+    }
+
+    protected static Map<String, Node> buildThemeMap(Document themeCfg) {
+        Map<String, Node> map = new HashMap<String, Node>();
+        String xpath = "/themes/themegroup/theme";
+
+        NodeList nodes = (NodeList)XMLUtils.xpath(
+            themeCfg, xpath, XPathConstants.NODESET);
+
+        if (nodes != null) {
+            for (int i = 0, N = nodes.getLength(); i < N; ++i) {
+                Node node = nodes.item(i);
+                String name = ((Element)node).getAttribute("name");
+                map.put(name, node);
+            }
+        }
+        return map;
+    }
+
+
+    protected static void parseFields(Node config, Theme theme) {
+        if (config == null || theme == null) {
+            logger.warn("Parsing fields without node or theme is senseless!");
+            return;
+        }
+
+        NodeList fields = ((Element)config).getElementsByTagName("field");
+
+        int num = fields.getLength();
+
+        logger.debug("Found " + num + " own fields in this theme.");
+
+        if (num == 0) {
+            logger.debug("Theme has no own fields.");
+            return;
+        }
+
+        for (int i = 0; i < num; i++) {
+            Node field = fields.item(i);
+
+            addField(theme, field);
+        }
+    }
+
+
+    protected static void addField(Theme theme, Node field) {
+        String name = ((Element)field).getAttribute("name");
+
+        logger.debug("Add field " + name + " to theme " + theme.getName());
+
+        NamedNodeMap attrs = field.getAttributes();
+
+        int num = attrs != null ? attrs.getLength() : 0;
+
+        if (num == 0) {
+            logger.warn("This field has no attributes.");
+            return;
+        }
+
+        ThemeField theField = new DefaultThemeField(name);
+
+        for (int i = 0; i < num; i++) {
+            Node attr    = attrs.item(i);
+
+            String key   = attr.getNodeName();
+            String value = attr.getNodeValue();
+
+            theField.setAttribute(key, value);
+        }
+
+        theme.addField(name, theField);
+    }
+
+
+    protected static void parseAttrs(Node config, Theme theme) {
+        NamedNodeMap attrs = config.getAttributes();
+
+        int num = attrs != null ? attrs.getLength() : 0;
+
+        if (num == 0) {
+            logger.debug("Theme has no attributes set.");
+            return;
+        }
+
+        for (int i = 0; i < num; i++) {
+            Node attr = attrs.item(i);
+
+            String name  = attr.getNodeName();
+            String value = attr.getNodeValue();
+
+            theme.addAttribute(name, value);
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/themes/ThemeField.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,58 @@
+package org.dive4elements.river.themes;
+
+import org.w3c.dom.Document;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface ThemeField {
+
+    /**
+     * Returns the name of this field.
+     *
+     * @return the name of this field.
+     */
+    String getName();
+
+    /**
+     * Returns the type of this field.
+     *
+     * @return the type of this field.
+     */
+    String getType();
+
+
+    /**
+     * Returns the value of this field.
+     *
+     * @return the value of this field.
+     */
+    Object getValue();
+
+
+    /**
+     * Changes the value of this field.
+     *
+     * @param value The new value.
+     */
+    void setValue(Object value);
+
+
+    /**
+     * Sets the value of an attribute.
+     *
+     * @param name The name of an attribute.
+     * @param value The value of an attribute.
+     */
+    void setAttribute(String name, Object value);
+
+
+    /**
+     * Dumps the field to XML.
+     *
+     * @return a document.
+     */
+    Document toXML();
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/themes/ThemeGroup.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,32 @@
+package org.dive4elements.river.themes;
+
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class ThemeGroup {
+
+    protected String name;
+
+    protected Map<String, Theme> themes;
+
+
+    public ThemeGroup(String name, Map<String, Theme> themes) {
+        this.name = name;
+        this.themes = themes;
+    }
+
+    public String getName() {
+        return this.name;
+    }
+
+    public Map<String, Theme> getThemes() {
+        return this.themes;
+    }
+
+    public Theme getThemeByName(String name) {
+        return themes.get(name);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/themes/ThemeMapping.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,158 @@
+package org.dive4elements.river.themes;
+
+import org.apache.log4j.Logger;
+
+import java.io.Serializable;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.dive4elements.river.artifacts.FLYSArtifact;
+
+/**
+ * Represents mapping to a theme (including conditions).
+ */
+public class ThemeMapping implements Serializable {
+
+    /** The logger that is used in this class */
+    private static Logger logger = Logger.getLogger(ThemeMapping.class);
+
+    /** Name from which to map. */
+    protected String from;
+
+    /** Name to which to map. */
+    protected String to;
+
+    /** Given pattern (held against facet description). */
+    protected String patternStr;
+
+    /** Given masterAttr pattern (held against masterartifacts attributes). */
+    protected String masterAttr;
+
+    /** Given output for which mapping is valid. */
+    protected String output;
+
+    protected Pattern pattern;
+
+
+    public ThemeMapping(String from, String to) {
+        this(from, to, null, null, null);
+    }
+
+
+    public ThemeMapping(
+        String from,
+        String to,
+        String patternStr,
+        String masterAttr,
+        String output)
+   {
+        this.from       = from;
+        this.to         = to;
+        this.patternStr = patternStr;
+        this.masterAttr = masterAttr;
+        this.output     = output;
+
+        this.pattern = Pattern.compile(patternStr);
+    }
+
+
+    public String getFrom() {
+        return from;
+    }
+
+
+    /**
+     * Get name of theme that is mapped to.
+     */
+    public String getTo() {
+        return to;
+    }
+
+
+    /**
+     * Get pattern.
+     */
+    public String getPatternStr() {
+        return patternStr;
+    }
+
+
+    /**
+     * Match regular expression against text.
+     *
+     * @param text string to be matched against.
+     * @return true if pattern matches text or pattern is empty.
+     */
+    public boolean applyPattern(String text) {
+        if (patternStr == null || patternStr.length() == 0) {
+            return true;
+        }
+        Matcher m = pattern.matcher(text);
+
+       if (m.matches()) {
+           logger.debug("Pattern matches: " + text);
+           return true;
+       }
+       else {
+           logger.debug(
+               "Pattern '"+ text + "' does not match: " + this.patternStr);
+           return false;
+       }
+    }
+
+
+    /**
+     * Inspects Artifacts data given the masterAttr-condition.
+     *
+     * The only condition implemented so far is 'key==value', for which
+     * the Artifacts data with name "key" has to be of value "value" in order
+     * for true to be returned.
+     *
+     * @param artifact Artifact of which to inspect data.
+     * @return true if no condition is specified or condition is met.
+     */
+    public boolean masterAttrMatches(FLYSArtifact artifact) {
+        if (masterAttr == null || masterAttr.length() == 0) {
+           return true;
+        }
+
+        // Operator split.
+        String[] parts = masterAttr.split("==");
+        if (parts.length != 2) {
+            logger.error("ThemeMapping could not parse masterAttr.-condition:_"
+                + masterAttr + "_");
+            return false;
+        }
+
+        // Test.
+        if (artifact.getDataAsString(parts[0]).equals(parts[1])) {
+            logger.debug("Matches master Attribute.");
+            return true;
+        }
+        else {
+            logger.debug("Does not match master Attribute.");
+            return false;
+        }
+    }
+
+
+    /**
+     * Returns true if no output condition exists, or the condition is met
+     * in parameter output.
+     */
+    public boolean outputMatches(String output) {
+        if (this.output == null || this.output.length() == 0) {
+            return true;
+        }
+
+        if (this.output.equals(output)) {
+            logger.debug("Output matches this mapping: " + output);
+            return true;
+        }
+        else {
+            logger.debug("Output '"+ output +"' does not match: "+ this.output);
+            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/flys-artifacts/src/main/java/org/dive4elements/river/utils/ArtifactMapfileGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,340 @@
+package org.dive4elements.river.utils;
+
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.access.RiverAccess;
+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 java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.apache.velocity.Template;
+import org.geotools.data.shapefile.ShpFiles;
+import org.geotools.data.shapefile.shp.ShapefileHeader;
+import org.geotools.data.shapefile.shp.ShapefileReader;
+
+public class ArtifactMapfileGenerator extends MapfileGenerator {
+
+    private static Logger logger = Logger.getLogger(ArtifactMapfileGenerator.class);
+
+    @Override
+    protected String getVelocityLogfile() {
+        return FLYSUtils.getXPathString(FLYSUtils.XPATH_FLOODMAP_VELOCITY_LOGFILE);
+    }
+
+    @Override
+    protected String getMapserverTemplatePath() {
+        return FLYSUtils.getXPathString(FLYSUtils.XPATH_FLOODMAP_MAPSERVER_TEMPLATE_PATH);
+    }
+
+    @Override
+    public String getMapserverUrl() {
+        return FLYSUtils.getXPathString(FLYSUtils.XPATH_FLOODMAP_MAPSERVER_URL);
+    }
+
+    /**
+     * Method which starts searching for meta information file and mapfile
+     * generation.
+     */
+    @Override
+    public void generate() throws IOException
+    {
+        File[] userDirs = getUserDirs();
+        List<String> layers = parseLayers(userDirs);
+        logger.info("Found " + layers.size() + " layers for user mapfile.");
+
+        writeMapfile(layers);
+    }
+
+    /**
+     * Creates a layer file used for Mapserver's mapfile which represents the
+     * floodmap.
+     *
+     * @param flys The FLYSArtifact that owns <i>wms</i>.
+     * @param wms The WMSLayerFacet that contains information for the layer.
+     */
+    public void createUeskLayer(
+        FLYSArtifact  flys,
+        WSPLGENLayerFacet wms,
+        String        style,
+        CallContext context
+    ) throws FileNotFoundException, IOException
+    {
+        logger.debug("createUeskLayer");
+
+        LayerInfo layerinfo = new LayerInfo();
+        layerinfo.setName(MS_WSPLGEN_PREFIX + flys.identifier());
+        layerinfo.setType("POLYGON");
+        layerinfo.setDirectory(flys.identifier());
+        layerinfo.setData(WSPLGEN_RESULT_SHAPE);
+        layerinfo.setTitle(Resources.getMsg(Resources.getLocale(context.getMeta()),
+                                            "floodmap.uesk",
+                                            "Floodmap"));
+        layerinfo.setStyle(style);
+        RiverAccess access = new RiverAccess(flys);
+        String river = access.getRiver();
+        layerinfo.setSrid(FLYSUtils.getRiverDGMSrid(river));
+
+        String name = MS_LAYER_PREFIX + wms.getName();
+
+        Template template = getTemplateByName(WSPLGEN_LAYER_TEMPLATE);
+        if (template == null) {
+            logger.warn("Template '" + WSPLGEN_LAYER_TEMPLATE + "' found.");
+            return;
+        }
+
+        try {
+            File dir = new File(getShapefileBaseDir(), flys.identifier());
+            writeLayer(layerinfo, new File(dir, name), template);
+        }
+        catch (FileNotFoundException fnfe) {
+            logger.error(fnfe, fnfe);
+            logger.warn("Unable to write layer: " + name);
+        }
+    }
+
+
+    /**
+     * Creates a layer file used for Mapserver's mapfile which represents the
+     * user defined barriers.
+     *
+     * @param flys The FLYSArtifact that owns <i>wms</i>.
+     * @param wms The WMSLayerFacet that contains information for the layer.
+     */
+    public void createBarriersLayer(FLYSArtifact flys, WMSLayerFacet wms)
+    throws FileNotFoundException, IOException
+    {
+        logger.debug("createBarriersLayer");
+
+        //String uuid = flys.identifier();
+        //File   dir  = new File(getShapefileBaseDir(), uuid);
+
+        createBarriersLineLayer(flys, wms);
+        createBarriersPolygonLayer(flys, wms);
+    }
+
+
+    protected void createBarriersLineLayer(
+        FLYSArtifact  flys,
+        WMSLayerFacet wms
+    )
+    throws FileNotFoundException, IOException
+    {
+        String uuid       = flys.identifier();
+        String group      = MS_BARRIERS_PREFIX + uuid;
+        String groupTitle = "I18N_BARRIERS_TITLE";
+
+        File dir  = new File(getShapefileBaseDir(), uuid);
+        File test = new File(dir, WSPLGEN_LINES_SHAPE);
+
+        if (!test.exists() || !test.canRead()) {
+            logger.debug("No barrier line layer existing.");
+            return;
+        }
+
+        LayerInfo lineInfo = new LayerInfo();
+        lineInfo.setName(MS_LINE_PREFIX + uuid);
+        lineInfo.setType("LINE");
+        lineInfo.setDirectory(uuid);
+        lineInfo.setData(WSPLGEN_LINES_SHAPE);
+        lineInfo.setTitle("I18N_LINE_SHAPE");
+        lineInfo.setGroup(group);
+        lineInfo.setGroupTitle(groupTitle);
+        lineInfo.setSrid(wms.getSrid());
+
+        String nameLines = MS_LAYER_PREFIX + wms.getName() + "-lines";
+
+        Template tpl = getTemplateByName(SHP_LAYER_TEMPLATE);
+        if (tpl == null) {
+            logger.warn("Template '" + SHP_LAYER_TEMPLATE + "' found.");
+            return;
+        }
+
+        try {
+            writeLayer(lineInfo, new File(dir, nameLines), tpl);
+        }
+        catch (FileNotFoundException fnfe) {
+            logger.error(fnfe, fnfe);
+            logger.warn("Unable to write layer: " + nameLines);
+        }
+    }
+
+    protected void createBarriersPolygonLayer(
+            FLYSArtifact  flys,
+            WMSLayerFacet wms
+        )
+        throws FileNotFoundException, IOException
+        {
+            String uuid       = flys.identifier();
+            String group      = uuid + MS_BARRIERS_PREFIX;
+            String groupTitle = "I18N_BARRIERS_TITLE";
+
+            File dir  = new File(getShapefileBaseDir(), uuid);
+            File test = new File(dir, WSPLGEN_POLYGONS_SHAPE);
+
+            if (!test.exists() || !test.canRead()) {
+                logger.debug("No barrier line layer existing.");
+                return;
+            }
+
+            LayerInfo polygonInfo = new LayerInfo();
+            polygonInfo.setName(MS_POLYGONS_PREFIX + uuid);
+            polygonInfo.setType("POLYGON");
+            polygonInfo.setDirectory(uuid);
+            polygonInfo.setData(WSPLGEN_POLYGONS_SHAPE);
+            polygonInfo.setTitle("I18N_POLYGON_SHAPE");
+            polygonInfo.setGroup(group);
+            polygonInfo.setGroupTitle(groupTitle);
+            polygonInfo.setSrid(wms.getSrid());
+
+            String namePolygons = MS_LAYER_PREFIX + wms.getName() + "-polygons";
+
+            Template tpl = getTemplateByName(SHP_LAYER_TEMPLATE);
+            if (tpl == null) {
+                logger.warn("Template '" + SHP_LAYER_TEMPLATE + "' found.");
+                return;
+            }
+
+            try {
+                writeLayer(polygonInfo, new File(dir, namePolygons), tpl);
+            }
+            catch (FileNotFoundException fnfe) {
+                logger.error(fnfe, fnfe);
+                logger.warn("Unable to write layer: " + namePolygons);
+            }
+        }
+
+
+        /**
+         * Creates a layer file used for Mapserver's mapfile which represents the
+         * shape files uploaded by the user.
+         *
+         * @param flys The FLYSArtifact that owns <i>wms</i>.
+         * @param wms The WMSLayerFacet that contains information for the layer.
+         */
+        public void createUserShapeLayer(FLYSArtifact flys, WMSLayerFacet wms)
+        throws FileNotFoundException, IOException
+        {
+            logger.debug("createUserShapeLayer");
+
+            String uuid = flys.identifier();
+            File   dir  = new File(getShapefileBaseDir(), uuid);
+            File   test = new File(dir, WSPLGEN_USER_SHAPE);
+
+            if (!test.exists() || !test.canRead()) {
+                logger.debug("No user layer existing.");
+                return;
+            }
+
+            File userShape = new File(dir, WSPLGEN_USER_SHAPE);
+            ShpFiles sf = new ShpFiles(userShape);
+            ShapefileReader sfr = new ShapefileReader(sf, true, false, null);
+            ShapefileHeader sfh = sfr.getHeader();
+
+            String group      = uuid + MS_USERSHAPE_PREFIX;
+            String groupTitle = "I18N_USER_SHAPE_TITLE";
+
+            LayerInfo info = new LayerInfo();
+            info.setName(MS_USERSHAPE_PREFIX + uuid);
+            if (sfh.getShapeType().isLineType()) {
+                info.setType("LINE");
+            }
+            else if (sfh.getShapeType().isPolygonType()) {
+                info.setType("POLYGON");
+            }
+            else {
+                return;
+            }
+            info.setDirectory(uuid);
+            info.setData(WSPLGEN_USER_SHAPE);
+            info.setTitle("I18N_USER_SHAPE");
+            info.setGroup(group);
+            info.setGroupTitle(groupTitle);
+            info.setSrid(wms.getSrid());
+
+            String nameUser = MS_LAYER_PREFIX + wms.getName();
+
+            Template tpl = getTemplateByName(SHP_LAYER_TEMPLATE);
+            if (tpl == null) {
+                logger.warn("Template '" + SHP_LAYER_TEMPLATE + "' found.");
+                return;
+            }
+
+            try {
+                writeLayer(info, new File(dir, nameUser), tpl);
+            }
+            catch (FileNotFoundException fnfe) {
+                logger.error(fnfe, fnfe);
+                logger.warn("Unable to write layer: " + nameUser);
+            }
+
+        }
+
+
+        /**
+         * Creates a layer file used for Mapserver's mapfile which represents
+         * geometries from database.
+         *
+         * @param flys The FLYSArtifact that owns <i>wms</i>.
+         * @param wms The WMSLayerFacet that contains information for the layer.
+         */
+        public void createDatabaseLayer(
+            FLYSArtifact    flys,
+            WMSDBLayerFacet wms,
+            String          style
+        )
+        throws FileNotFoundException, IOException
+        {
+            logger.debug("createDatabaseLayer");
+
+            LayerInfo layerinfo = new LayerInfo();
+            layerinfo.setName(wms.getName() + "-" + flys.identifier());
+            layerinfo.setType(wms.getGeometryType());
+            layerinfo.setFilter(wms.getFilter());
+            layerinfo.setData(wms.getData());
+            layerinfo.setTitle(wms.getDescription());
+            layerinfo.setStyle(style);
+            if(wms.getExtent() != null) {
+                layerinfo.setExtent(GeometryUtils.jtsBoundsToOLBounds(wms.getExtent()));
+            }
+            layerinfo.setConnection(wms.getConnection());
+            layerinfo.setConnectionType(wms.getConnectionType());
+            layerinfo.setLabelItem(wms.getLabelItem());
+            layerinfo.setSrid(wms.getSrid());
+
+            String name = MS_LAYER_PREFIX + wms.getName();
+
+            Template template = getTemplateByName(DB_LAYER_TEMPLATE);
+            if (template == null) {
+                logger.warn("Template '" + DB_LAYER_TEMPLATE + "' found.");
+                return;
+            }
+
+            try {
+                File dir = new File(getShapefileBaseDir(), flys.identifier());
+                writeLayer(layerinfo, new File(dir, name), template);
+            }
+            catch (FileNotFoundException fnfe) {
+                logger.error(fnfe, fnfe);
+                logger.warn("Unable to write layer: " + name);
+            }
+        }
+
+        @Override
+        protected String getMapfilePath() {
+            return FLYSUtils.getXPathString(FLYSUtils.XPATH_FLOODMAP_MAPFILE_PATH);
+        }
+
+        @Override
+        protected String getMapfileTemplate() {
+            return FLYSUtils.getXPathString(FLYSUtils.XPATH_FLOODMAP_MAPFILE_TEMPLATE);
+        }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/utils/DataUtil.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,34 @@
+package org.dive4elements.river.utils;
+
+import java.util.Random;
+
+import gnu.trove.TDoubleArrayList;
+
+public class DataUtil
+{
+    public static boolean guessWaterIncreasing(TDoubleArrayList data) {
+        return guessWaterIncreasing(data, 0.05f);
+    }
+
+    public static boolean guessWaterIncreasing(TDoubleArrayList data, float factor) {
+        int N = data.size();
+        if (N < 2) return false;
+
+        int samples = (int)(factor*N) + 1;
+
+        int up = 0;
+
+        Random rand = new Random();
+
+        for (int i = 0; i < samples; ++i) {
+            int    pos2 = rand.nextInt(N-1) + 1;
+            int    pos1 = rand.nextInt(pos2);
+            double w1   = data.getQuick(pos1);
+            double w2   = data.getQuick(pos2);
+            if (w2 > w1) ++up;
+        }
+
+        return up > samples/2;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/utils/DateAverager.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,36 @@
+package org.dive4elements.river.utils;
+
+import gnu.trove.TLongArrayList;
+
+import java.util.Date;
+
+public class DateAverager
+{
+   protected TLongArrayList dates;
+
+    public DateAverager() {
+        dates = new TLongArrayList();
+    }
+
+    public void add(Date date) {
+        dates.add(date.getTime());
+    }
+
+    public Date getAverage() {
+        int N = dates.size();
+        if (N == 0) {
+            return null;
+        }
+        long min = dates.min();
+        long sum = 0L;
+        for (int i = 0; i < N; ++i) {
+            sum += dates.getQuick(i) - min;
+        }
+        return new Date(min + (long)Math.round(sum/(double)N));
+    }
+
+    public void clear() {
+        dates.resetQuick();
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/utils/DoubleUtil.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,246 @@
+package org.dive4elements.river.utils;
+
+import org.dive4elements.river.artifacts.math.Linear;
+
+import gnu.trove.TDoubleArrayList;
+
+import java.util.Arrays;
+
+import org.apache.log4j.Logger;
+
+/** Utils to deal with Double precision values. */
+public class DoubleUtil
+{
+    /** Private logger. */
+    private static Logger log = Logger.getLogger(DoubleUtil.class);
+
+    public static final double DEFAULT_STEP_PRECISION = 1e6;
+
+    /** EPSILON for comparison of double precision values. */
+    public static final double EPSILON = 1e-4;
+
+    private DoubleUtil() {
+    }
+
+    public static final double [] explode(double from, double to, double step) {
+        return explode(from, to, step, DEFAULT_STEP_PRECISION);
+    }
+
+    public static final double round(double x, double precision) {
+        return Math.round(x * precision)/precision;
+    }
+
+    public static final double round(double x) {
+        return Math.round(x * DEFAULT_STEP_PRECISION)/DEFAULT_STEP_PRECISION;
+    }
+
+    /**
+     * Returns array with values from parameter from to to with given step width.
+     * from and to are included.
+     */
+    public static final double [] explode(
+        double from,
+        double to,
+        double step,
+        double precision
+    ) {
+        double lower = from;
+
+        double diff = to - from;
+        double tmp  = diff / step;
+        int    num = (int)Math.abs(Math.ceil(tmp)) + 1;
+
+        if (num < 1) {
+            return new double[0];
+        }
+
+        double [] values = new double[num];
+
+        if (from > to) {
+            step = -step;
+        }
+
+        double max = Math.max(from, to);
+
+        for (int idx = 0; idx < num; idx++) {
+            if (lower - max > EPSILON) {
+                return Arrays.copyOfRange(values, 0, idx);
+            }
+
+            values[idx] = round(lower, precision);
+            lower      += step;
+        }
+
+        return values;
+    }
+
+    public static final double interpolateSorted(
+        double [] xs,
+        double [] ys,
+        double x
+    ) {
+        int lo = 0, hi = xs.length-1;
+
+        int mid = -1;
+
+        while (lo <= hi) {
+            mid = (lo + hi) >> 1;
+            double mx = xs[mid];
+                 if (x < mx) hi = mid - 1;
+            else if (x > mx) lo = mid + 1;
+            else return ys[mid];
+        }
+        if (mid < lo) {
+            return lo >= xs.length
+                ? Double.NaN
+                : Linear.linear(x, xs[mid], xs[mid+1], ys[mid], ys[mid+1]);
+        }
+        return hi < 0
+            ? Double.NaN
+            : Linear.linear(x, xs[mid-1], xs[mid], ys[mid-1], ys[mid]);
+    }
+
+    public static final boolean isIncreasing(double [] array) {
+        int inc = 0;
+        int dec = 0;
+        int sweet = (array.length-1)/2;
+        for (int i = 1; i < array.length; ++i) {
+            if (array[i] > array[i-1]) {
+                if (++inc > sweet) {
+                    return true;
+                }
+            }
+            else if (++dec > sweet) {
+                return false;
+            }
+        }
+        return inc > sweet;
+    }
+
+    public static final double [] swap(double [] array) {
+        int lo = 0;
+        int hi = array.length-1;
+        while (hi > lo) {
+            double t  = array[lo];
+            array[lo] = array[hi];
+            array[hi] = t;
+            ++lo;
+            --hi;
+        }
+
+        return array;
+    }
+
+    public static final double [] swapClone(double [] in) {
+        double [] out = new double[in.length];
+
+        for (int j = out.length-1, i = 0; j >= 0;) {
+            out[j--] = in[i++];
+        }
+
+        return out;
+    }
+
+    public static final double [] sumDiffs(double [] in) {
+        double [] out = new double[in.length];
+
+        for (int i = 1; i < out.length; ++i) {
+            out[i] = out[i-1] + Math.abs(in[i-1] - in[i]);
+        }
+
+        return out;
+    }
+
+    public static final double [] fill(int N, double value) {
+        double [] result = new double[N];
+        Arrays.fill(result, value);
+        return result;
+    }
+
+
+    /** Use with parseSegments. */
+    public interface SegmentCallback {
+        void newSegment(double from, double to, double [] values);
+    }
+
+
+    /** Call callback for every string split by colon.
+     * Expected format FROM:TO:VALUE1,VALUE2,VALUE3*/
+    public static final void parseSegments(
+        String          input,
+        SegmentCallback callback
+    ) {
+        TDoubleArrayList vs = new TDoubleArrayList();
+
+        for (String segmentStr: input.split(":")) {
+            String [] parts = segmentStr.split(";");
+            if (parts.length < 3) {
+                log.warn("invalid segment: '" + segmentStr + "'");
+                continue;
+            }
+            try {
+                double from = Double.parseDouble(parts[0].trim());
+                double to   = Double.parseDouble(parts[1].trim());
+
+                vs.resetQuick();
+
+                for (String valueStr: parts[3].split(",")) {
+                    vs.add(round(Double.parseDouble(valueStr.trim())));
+                }
+
+                callback.newSegment(from, to, vs.toNativeArray());
+            }
+            catch (NumberFormatException nfe) {
+                log.warn("invalid segment: '" + segmentStr + "'");
+            }
+        }
+    }
+
+    public static final boolean isValid(double [][] data) {
+        for (double [] ds: data) {
+            for (double d: ds) {
+                if (Double.isNaN(d)) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+
+    /** In an array of doubles, search and return the maximum value. */
+    public static final double maxInArray(double[] values) {
+        double max = - Double.MAX_VALUE;
+        for (double d: values) {
+            if (d > max) max = d;
+        }
+        return max;
+    }
+
+    public static void removeNaNs(TDoubleArrayList [] arrays) {
+
+        int dest = 0;
+
+        int A = arrays.length;
+        int N = arrays[0].size();
+
+        OUTER: for (int i = 0; i < N; ++i) {
+            for (int j = 0; j < A; ++j) {
+                TDoubleArrayList a = arrays[j];
+                double v = a.getQuick(i);
+                if (Double.isNaN(v)) {
+                    continue OUTER;
+                }
+                a.setQuick(dest, v);
+            }
+            ++dest;
+        }
+
+        if (dest < N) {
+            for (int i = 0; i < A; ++i) {
+                arrays[i].remove(dest, N-dest);
+            }
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/utils/FLYSUtils.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,921 @@
+package org.dive4elements.river.utils;
+
+import org.dive4elements.artifactdatabase.state.State;
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.common.utils.Config;
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.StaticWKmsArtifact;
+import org.dive4elements.river.artifacts.WINFOArtifact;
+import org.dive4elements.river.artifacts.access.RangeAccess;
+import org.dive4elements.river.artifacts.context.FLYSContext;
+import org.dive4elements.river.artifacts.model.LocationProvider;
+import org.dive4elements.river.artifacts.model.RiverFactory;
+import org.dive4elements.river.artifacts.model.WKms;
+import org.dive4elements.river.artifacts.model.WQ;
+import org.dive4elements.river.artifacts.model.WQKms;
+import org.dive4elements.river.artifacts.states.WDifferencesState;
+import org.dive4elements.river.artifacts.states.WaterlevelSelectState;
+import org.dive4elements.river.backend.SessionFactoryProvider;
+import org.dive4elements.river.model.Gauge;
+import org.dive4elements.river.model.MainValue;
+import org.dive4elements.river.model.River;
+
+import gnu.trove.TDoubleArrayList;
+import gnu.trove.TIntArrayList;
+import gnu.trove.TLongArrayList;
+
+import java.text.NumberFormat;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.xml.xpath.XPathConstants;
+
+import org.apache.log4j.Logger;
+import org.hibernate.SessionFactory;
+import org.hibernate.impl.SessionFactoryImpl;
+import org.w3c.dom.Document;
+
+
+/**
+ * Static helper methods to e.g. access FLYSArtifacts data.
+ *
+ * @deprecated Don't use FLYSUtils to get data from an {@link Artifact} anymore.
+ * Instead use and/or create a {@link Access} class hierarchy.
+ **/
+@Deprecated
+public class FLYSUtils {
+
+    /** The logger that is used in this utility. */
+    private static Logger logger = Logger.getLogger(FLYSUtils.class);
+
+    /**
+     * An enum that represents the 5 possible WQ modes in FLYS. The 5 values are
+     * <i>QFREE</i> <i>QGAUGE</i> <i>WGAUGE</i> <i>WFREE</i> and <i>NONE</i>.
+     */
+    public static enum WQ_MODE { QFREE, QGAUGE, WFREE, WGAUGE, NONE };
+
+    /**
+     * An enum that represents the 4 possible WQ input modes in FLYS. The 4
+     * values are
+     * <i>ADAPTED</i> <i>SINGLE</i> <i>RANGE</i> and <i>NONE</i>.
+     */
+    public static enum WQ_INPUT { ADAPTED, SINGLE, RANGE, NONE };
+
+    public static final Pattern NUMBERS_PATTERN =
+        Pattern.compile("\\D*(\\d++.\\d*)\\D*");
+
+    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";
+
+    public static final String XPATH_FLOODMAP_VELOCITY_LOGFILE =
+        "/artifact-database/floodmap/velocity/logfile/@path";
+
+    public static final String XPATH_FLOODMAP_MAPSERVER_URL =
+        "/artifact-database/floodmap/mapserver/server/@path";
+
+    public static final String XPATH_RIVERMAP_MAPSERVER_URL =
+            "/artifact-database/rivermap/mapserver/server/@path";
+
+    public static final String XPATH_FLOODMAP_MAPFILE_PATH =
+        "/artifact-database/floodmap/mapserver/mapfile/@path";
+
+    public static final String XPATH_FLOODMAP_MAPFILE_TEMPLATE =
+        "/artifact-database/floodmap/mapserver/map-template/@path";
+
+    public static final String XPATH_FLOODMAP_MAPSERVER_TEMPLATE_PATH =
+        "/artifact-database/floodmap/mapserver/templates/@path";
+
+
+    private FLYSUtils() {
+    }
+
+
+    /**
+     * Pulls Artifact with given UUID fromm database.
+     * @return FLYSArtifact with given UUID or null (in case of errors).
+     */
+    public static FLYSArtifact getArtifact(String uuid, CallContext context) {
+        try {
+            Artifact artifact = context.getDatabase().getRawArtifact(uuid);
+
+            if (artifact == null) {
+                logger.error("Artifact '" + uuid + "' does not exist.");
+                return null;
+            }
+
+            if (!(artifact instanceof FLYSArtifact)) {
+                logger.error("Artifact '" +uuid+ "' is no valid FLYSArtifact.");
+                return null;
+            }
+
+            return (FLYSArtifact) artifact;
+        }
+        // TODO: catch more selective
+        catch (Exception e) {
+            logger.error("Cannot get FLYSArtifact " + uuid
+                + " from database (" + e.getMessage() + ").");
+            return null;
+        }
+    }
+
+
+    /**
+     * Returns the FLYSContext from context object.
+     *
+     * @param context The CallContext or the FLYSContext.
+     *
+     * @return the FLYSContext.
+     */
+    public static FLYSContext getFlysContext(Object context) {
+        return context instanceof FLYSContext
+            ? (FLYSContext) context
+            : (FLYSContext) ((CallContext) context).globalContext();
+    }
+
+
+    /**
+     * Convinience function to retrieve an XPath as string with replaced config
+     * directory.
+     *
+     * @param xpath The XPath expression.
+     *
+     * @return a string with replaced config directory.
+     */
+    public static String getXPathString(String xpath) {
+        String tmp = Config.getStringXPath(xpath);
+        tmp        = Config.replaceConfigDir(tmp);
+
+        return tmp;
+    }
+
+
+    public static boolean isUsingOracle() {
+        SessionFactory sf = SessionFactoryProvider.getSessionFactory();
+
+        String d = SessionFactoryProvider.getDriver((SessionFactoryImpl) sf);
+
+        return d != null ? d.indexOf("Oracle") >= 0 : false;
+    }
+
+
+    /**
+     * This method returns an WQ_MODE enum which is based on the parameters
+     * stored in <i>flys</i> Artifact. If there is no <i>wq_isq</i> parameter
+     * existing, WQ_MODE.NONE is returned.
+     *
+     * @param flys The FLYSArtifact that stores wq mode relevant parameters.
+     *
+     * @return an enum WQ_MODE.
+     */
+    public static WQ_MODE getWQMode(FLYSArtifact flys) {
+        if (flys == null) {
+            return WQ_MODE.NONE;
+        }
+
+        String values = flys.getDataAsString("wq_values");
+        Boolean isQ   = flys.getDataAsBoolean("wq_isq");
+
+        if (values != null) {
+            return isQ ? WQ_MODE.QGAUGE : WQ_MODE.WGAUGE;
+        }
+
+        Boolean isFree = flys.getDataAsBoolean("wq_isfree");
+
+        if (isQ != null && isQ) {
+            return isFree ? WQ_MODE.QFREE : WQ_MODE.QGAUGE;
+        }
+        else if (isQ != null && !isQ) {
+            return isFree ? WQ_MODE.WFREE : WQ_MODE.WGAUGE;
+        }
+        else {
+            return WQ_MODE.NONE;
+        }
+    }
+
+
+    public static WQ_INPUT getWQInputMode(FLYSArtifact flys) {
+        if (flys == null) {
+            return WQ_INPUT.NONE;
+        }
+
+        Boolean selection = flys.getDataAsBoolean("wq_isrange");
+        String adapted = flys.getDataAsString("wq_values");
+
+        if(adapted != null && adapted.length() > 0) {
+            return WQ_INPUT.ADAPTED;
+        }
+
+        if (selection != null && selection) {
+            return WQ_INPUT.RANGE;
+        }
+        else {
+            return WQ_INPUT.SINGLE;
+        }
+    }
+
+
+    /**
+     * Get bounds for river of artifact.
+     * @param flysArtifact artifact which has a "river" data.
+     * @return double array. min is at[0], max at[1]. null if given artifact is null
+     */
+    public static double[] getRiverMinMax(FLYSArtifact flysArtifact) {
+        if (flysArtifact == null) {
+            return null;
+        }
+
+        String riverName = flysArtifact.getDataAsString("river");
+
+        if (riverName == null) {
+            riverName = "";
+        }
+
+        logger.debug("Search for the min/max distances of '" + riverName + "'");
+
+        River river = RiverFactory.getRiver(riverName);
+
+        return river != null
+            ? river.determineMinMaxDistance()
+            : null;
+    }
+
+
+    public static double[] getKmFromTo(FLYSArtifact flys) {
+        String strFrom = flys.getDataAsString("ld_from");
+        String strTo   = flys.getDataAsString("ld_to");
+
+        if (strFrom == null) {
+            strFrom = flys.getDataAsString("from");
+        }
+
+        if (strTo == null) {
+            strTo = flys.getDataAsString("to");
+        }
+
+        if (strFrom == null || strTo == null) {
+            return null;
+        }
+
+        try {
+            return new double[] {
+                Double.parseDouble(strFrom),
+                Double.parseDouble(strTo) };
+        }
+        catch (NumberFormatException nfe) {
+            return null;
+        }
+    }
+
+
+    /**
+     * Return sorted array of locations at which stuff was calculated
+     * (from ld_locations data), null if not parameterized this way.
+     */
+    // TODO moved to RangeAccess. Resolve remaining calls.
+    private static double[] getLocations(FLYSArtifact flys) {
+        String locationStr = flys.getDataAsString("ld_locations");
+
+        if (locationStr == null || locationStr.length() == 0) {
+            if (flys instanceof WINFOArtifact) {
+                WINFOArtifact winfo = (WINFOArtifact) flys;
+                if (winfo.getReferenceStartKm() != null && winfo.getReferenceEndKms() != null) {
+                    return new double[]
+                        {
+                            winfo.getReferenceStartKm().doubleValue(),
+                            winfo.getReferenceEndKms()[0]
+                        };
+                }
+            }
+            return null;
+        }
+
+        String[] tmp               = locationStr.split(" ");
+        TDoubleArrayList locations = new TDoubleArrayList();
+
+        for (String l: tmp) {
+            try {
+                locations.add(Double.parseDouble(l));
+            }
+            catch (NumberFormatException nfe) {
+                logger.debug(nfe.getLocalizedMessage(), nfe);
+            }
+        }
+
+        locations.sort();
+
+        return locations.toNativeArray();
+    }
+
+
+    /**
+     * Returns the Qs for a given FLYSArtifact. This method currently accepts
+     * only instances of WINFOArtifact.
+     *
+     * @param flys A FLYSArtifact.
+     *
+     * @return the Qs.
+     */
+    public static double[] getQs(FLYSArtifact flys) {
+        // XXX this is not nice!
+        if (flys instanceof WINFOArtifact) {
+            return ((WINFOArtifact) flys).getQs();
+        }
+
+        logger.warn("This method currently supports WINFOArtifact only!");
+
+        return null;
+    }
+
+
+    /**
+     * Returns the Ws for a given FLYSArtifact. This method currently accepts
+     * only instances of WINFOArtifact.
+     *
+     * @param flys A FLYSArtifact.
+     *
+     * @return the Ws.
+     */
+    public static double[] getWs(FLYSArtifact flys) {
+        // XXX this is not nice!
+        if (flys instanceof WINFOArtifact) {
+            return ((WINFOArtifact) flys).getWs();
+        }
+
+        logger.warn("This method currently supports WINFOArtifact only!");
+
+        return null;
+    }
+
+
+    /**
+     * Returns the selected River object based on the 'river' data that might
+     * have been inserted by the user.
+     *
+     * @return the selected River or null if no river has been chosen yet.
+     */
+    public static River getRiver(FLYSArtifact flys) {
+        String sRiver = getRivername(flys);
+
+        return (sRiver != null)
+            ? RiverFactory.getRiver(sRiver)
+            : null;
+    }
+
+
+    /**
+     * Returns the name of the river specified in the given <i>flys</i>
+     * Artifact.
+     *
+     * @param flys The FLYSArtifact that stores a river relevant information.
+     *
+     * @return the name of the specified river or null.
+     */
+    public static String getRivername(FLYSArtifact flys) {
+        return flys != null ? flys.getDataAsString("river") : null;
+    }
+
+
+    /**
+     * Extracts the SRID defined in the global configuration for the river
+     * specified in <i>artifact</i>.
+     *
+     * @param artifact The FLYSArtifact that stores the name of the river.
+     *
+     * @return the SRID as string (e.g. "31466").
+     */
+    public static String getRiverSrid(FLYSArtifact artifact) {
+        String river = artifact.getDataAsString("river");
+
+        if (river == null || river.length() == 0) {
+            return null;
+        }
+
+        return getRiverSrid(river);
+    }
+
+
+    public static String getRiverSrid(String rivername) {
+        Map<String, String> variables = new HashMap<String, String>(1);
+        variables.put("name", rivername);
+
+        Document cfg = Config.getConfig();
+
+        return (String) XMLUtils.xpath(
+            cfg,
+            XPATH_FLOODMAP_RIVER_PROJECTION,
+            XPathConstants.STRING,
+            null,
+            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.
+     * @param flys the artifact in question.
+     * @return (First) gauge of locations of river of artifact.
+     */
+    public static Gauge getGauge(FLYSArtifact flys) {
+        River river = getRiver(flys);
+
+        if (river == null) {
+            logger.debug("no river found");
+            return null;
+        }
+
+        RangeAccess rangeAccess = new RangeAccess(flys, null);
+        double[] dist = rangeAccess.getKmRange();
+
+        if (dist == null) {
+            logger.debug("no range found");
+            return null;
+        }
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("Determine gauge for:");
+            logger.debug("... river: " + river.getName());
+            logger.debug("... distance: " + dist[0] + " - " + dist[1]);
+        }
+
+        Gauge gauge = river.determineGauge(dist[0], dist[1]);
+
+        String name = gauge != null ? gauge.getName() : "'n/a";
+        logger.debug("Found gauge: " + name);
+
+        return gauge;
+    }
+
+
+    public static String getGaugename(FLYSArtifact flys) {
+        Gauge gauge = getGauge(flys);
+
+        return gauge != null ? gauge.getName() : null;
+    }
+
+
+    public static Gauge getReferenceGauge(FLYSArtifact flys) {
+        Long officialNumber = flys.getDataAsLong("reference_gauge");
+
+        return officialNumber != null
+            ? Gauge.getGaugeByOfficialNumber(officialNumber)
+            : null;
+    }
+
+
+    public static String getReferenceGaugeName(FLYSArtifact flys) {
+        Gauge refGauge = getReferenceGauge(flys);
+
+        return refGauge != null
+            ? refGauge.getName()
+            : "-- not found --";
+    }
+
+
+    public static Double getValueFromWQ(WQ wq) {
+        if (wq == null) {
+            return null;
+        }
+
+        Matcher m = NUMBERS_PATTERN.matcher(wq.getName());
+
+        if (m.matches()) {
+            logger.debug("Found a number.");
+
+            String raw = m.group(1);
+
+            try {
+                return Double.valueOf(raw);
+            }
+            catch (NumberFormatException nfe) {
+            }
+        }
+
+        return null;
+    }
+
+
+    /** Creates human-readable name for a wsp (waterlevel/longitudinal section).
+     * @param name will be split at '='s.
+     */
+    public static String createWspWTitle(
+        WINFOArtifact winfo,
+        CallContext   cc,
+        String        name
+    ) {
+        String[] parts = name.split("=");
+
+        NumberFormat nf = Formatter.getWaterlevelW(cc);
+
+        String namedMainValue = null;
+
+        boolean isQ    = winfo.isQ();
+        boolean isFree = winfo.isFreeQ();
+
+        double v;
+
+        try {
+            v = Double.valueOf(parts[1]);
+
+            namedMainValue = getNamedMainValue(winfo.getGauge(), v);
+        }
+        catch (NumberFormatException nfe) {
+            logger.warn("Cannot parse Double of: '" + parts[1] + "'");
+            return name;
+        }
+
+        String prefix = null;
+
+        if (isQ && !isFree && namedMainValue != null) {
+            return "W (" + namedMainValue + ")";
+        }
+
+        if (isQ) {
+            prefix = "Q=";
+        }
+
+        return prefix == null
+            ? "W(" + nf.format(v) + ")"
+            : "W(" + prefix + nf.format(v) + ")";
+    }
+
+
+    public static String createWspQTitle(
+        WINFOArtifact winfo,
+        CallContext   cc,
+        String        name
+    ) {
+        String[] parts = name.split("=");
+
+        NumberFormat nf = Formatter.getWaterlevelQ(cc);
+
+        String namedMainValue = null;
+
+        boolean isQ    = winfo.isQ();
+        boolean isFree = winfo.isFreeQ();
+
+        double v;
+
+        try {
+            v = Double.valueOf(parts[1]);
+
+            namedMainValue = getNamedMainValue(winfo.getGauge(), v);
+        }
+        catch (NumberFormatException nfe) {
+            logger.warn("Cannot parse Double of: '" + parts[1] + "'");
+            return name;
+        }
+
+        String prefix = null;
+
+        if (isQ && !isFree && namedMainValue != null) {
+            return namedMainValue;
+        }
+
+        if (!isQ) {
+            prefix = "W=";
+        }
+
+        return prefix == null
+            ? "Q(" + nf.format(v) + ")"
+            : "Q(" + prefix + nf.format(v) + ")";
+    }
+
+
+    /**
+     * Returns the named main value if a Q was selected and if this Q fits to a
+     * named main value. Otherwise, this function returns null.
+     *
+     * @param winfo The WINFO Artifact.
+     * @param value The Q (or W) value.
+     *
+     * @return a named main value or null.
+     */
+    public static String getNamedMainValue(WINFOArtifact winfo, double value) {
+        WQ_MODE wqmode = getWQMode(winfo);
+
+        if (wqmode != WQ_MODE.QGAUGE) {
+            return null;
+        }
+        else {
+            return getNamedMainValue(winfo.getGauge(), value);
+        }
+    }
+
+
+    public static String getNamedMainValue(Gauge gauge, double value) {
+        List<MainValue> mainValues = gauge.getMainValues();
+        logger.debug("Search named main value for: " + value);
+
+        for (MainValue mv: mainValues) {
+            if (mv.getValue().doubleValue() == value) {
+                logger.debug("Found named main value: " + mv.getMainValue().getName());
+                return mv.getMainValue().getName();
+            }
+        }
+
+        logger.debug("Did not find a named main value for: " + value);
+        return null;
+    }
+
+
+    /**
+     *
+     * @param nmv A string that represents a named main value.
+     *
+     * @throws NullPointerException if nmv is null.
+     */
+    public static String stripNamedMainValue(String nmv) {
+        int startIndex = nmv.indexOf("(");
+        int endIndex   = nmv.indexOf(")");
+
+        if (startIndex > 0 && endIndex > 0 && startIndex < endIndex) {
+            return nmv.substring(0, startIndex);
+        }
+
+        return nmv;
+    }
+
+
+    /**
+     * Returns the URL of user mapfile for the owner of Artifact
+     * <i>artifactId</i>.
+     *
+     * @param artifactId The UUID of an artifact.
+     *
+     * @return the URL of the user wms.
+     */
+    public static String getUserWMSUrl(String artifactId) {
+        String url = getXPathString(XPATH_FLOODMAP_MAPSERVER_URL);
+        url = url.endsWith("/") ? url + "user-wms" : url + "/" + "user-wms";
+
+        return url;
+    }
+
+
+    public static String getRiverWMSUrl() {
+        String url = getXPathString(XPATH_RIVERMAP_MAPSERVER_URL);
+        url = url.endsWith("/") ? url + "river-wms" : url + "/" + "river-wms";
+
+        return url;
+    }
+
+
+    /**
+     * This method returns the description for a given <i>km</i> for a specific
+     * river. The river is provided by the FLYSArtifact <i>flys</i>.
+     *
+     * @param flys The FLYSArtifact that provides a river.
+     * @param km The kilometer.
+     *
+     * @return the description for <i>km</i> or an empty string if no
+     * description was found.
+     */
+    public static String getLocationDescription(FLYSArtifact flys, double km) {
+        String river = getRivername(flys);
+
+        if (river == null) {
+            return "";
+        }
+
+        return LocationProvider.getLocation(river, km);
+    }
+
+
+    /**
+     * This method returns the differences for a w-differences calculation.
+     *
+     * @param winfo The WINFOArtifact.
+     * @param context The context.
+     *
+     * @return The differences as string separated by semicolon and linebreak.
+     */
+    public static String getWDifferences(
+        WINFOArtifact winfo,
+        CallContext context)
+    {
+        State state = winfo.getCurrentState(context);
+        if(state instanceof WDifferencesState) {
+            String diffids = winfo.getDataAsString("diffids");
+            String datas[] = diffids.split("#");
+
+            // Validate the Data-Strings.
+            for (String s: datas) {
+                if (!WaterlevelSelectState.isValueValid(s)) {
+                    return "";
+                }
+            }
+
+            if (datas.length < 2) {
+                return "";
+            }
+
+            String diffs = "";
+            for(int i = 0; i < datas.length; i+=2) {
+                // e.g.:
+                // 42537f1e-3522-42ef-8968-635b03d8e9c6;longitudinal_section.w;1
+                WKms minuendWKms = getWKms(StringUtil.unbracket(datas[i+0]),
+                    context);
+                WKms subtrahendWKms = getWKms(StringUtil.unbracket(datas[i+1]),
+                    context);
+                if (minuendWKms != null && subtrahendWKms != null) {
+                    diffs += StringUtil.wWrap(minuendWKms.getName())
+                        + " - " + StringUtil.wWrap(subtrahendWKms.getName());
+                }
+                diffs += ";\n";
+            }
+            return diffs;
+        }
+        else {
+            logger.warn("Not a valid state for differences.");
+            return "";
+        }
+    }
+
+
+    protected static WKms getWKms(String mingle, CallContext context) {
+        String[] def  = mingle.split(";");
+        String   uuid = def[0];
+        String   name = def[1];
+        int      idx  = Integer.parseInt(def[2]);
+
+        if (name.startsWith("staticwkms")) {
+            StaticWKmsArtifact staticWKms =
+                (StaticWKmsArtifact) FLYSUtils.getArtifact(
+                    uuid,
+                    context);
+            WKms wkms = staticWKms.getWKms(idx);
+            if (wkms == null)
+                logger.error("No WKms from artifact.");
+            return wkms;
+        }
+
+        WINFOArtifact flys = (WINFOArtifact) FLYSUtils.getArtifact(
+            uuid,
+            context);
+
+        if (flys == null) {
+            logger.warn("One of the artifacts (1) for diff calculation could not be loaded");
+            return null;
+        }
+        else{
+            WQKms[] wqkms = (WQKms[]) flys.getWaterlevelData().
+                                              getData();
+            if (wqkms == null)
+            logger.warn("not  waterlevels in artifact");
+            else if (wqkms.length < idx)
+            logger.warn("not enough waterlevels in artifact");
+            return wqkms[idx];
+        }
+    }
+
+
+    /**
+     * This method transform a string into an int array. Therefore, the string
+     * <i>raw</i> must consist of int values separated by a <i>';'</i>.
+     *
+     * @param raw The raw integer array as string separated by a ';'.
+     *
+     * @return an array of int values.
+     */
+    public static int[] intArrayFromString(String raw) {
+        String[] splitted = raw != null ? raw.split(";") : null;
+
+        if (splitted == null || splitted.length == 0) {
+            logger.warn("No integer values found in '" + raw + "'");
+            return new int[0];
+        }
+
+        TIntArrayList integers = new TIntArrayList(splitted.length);
+
+        for (String value: splitted) {
+            try {
+                integers.add(Integer.parseInt(value));
+            }
+            catch (NumberFormatException nfe) {
+                logger.warn("Parsing integer failed: " + nfe);
+            }
+        }
+
+        return integers.toNativeArray();
+    }
+
+
+    /**
+     * This method transform a string into a long array. Therefore, the string
+     * <i>raw</i> must consist of int values separated by a <i>';'</i>.
+     *
+     * @param raw The raw long array as string separated by a ';'.
+     *
+     * @return an array of int values.
+     */
+    public static long[] longArrayFromString(String raw) {
+        String[] splitted = raw != null ? raw.split(";") : null;
+
+        if (splitted == null || splitted.length == 0) {
+            logger.warn("No long values found in '" + raw + "'");
+            return new long[0];
+        }
+
+        TLongArrayList longs = new TLongArrayList(splitted.length);
+
+        for (String value: splitted) {
+            try {
+                longs.add(Long.valueOf(value));
+            }
+            catch (NumberFormatException nfe) {
+                logger.warn("Parsing long failed: " + nfe);
+            }
+        }
+
+        return longs.toNativeArray();
+    }
+
+
+    /**
+     * This method transform a string into an double array. Therefore, the
+     * string <i>raw</i> must consist of double values separated by a
+     * <i>';'</i>.
+     *
+     * @param raw The raw double array as string separated by a ';'.
+     *
+     * @return an array of double values.
+     */
+    public static double[] doubleArrayFromString(String raw) {
+        String[] splitted = raw != null ? raw.split(";") : null;
+
+        if (splitted == null || splitted.length == 0) {
+            logger.warn("No double values found in '" + raw + "'");
+            return new double[0];
+        }
+
+        TDoubleArrayList doubles = new TDoubleArrayList(splitted.length);
+
+        for (String value: splitted) {
+            try {
+                doubles.add(Double.valueOf(value));
+            }
+            catch (NumberFormatException nfe) {
+                logger.warn("Parsing double failed: " + nfe);
+            }
+        }
+
+        return doubles.toNativeArray();
+    }
+
+
+    /**
+     * Returns the gauges that match the selected kilometer range.
+     *
+     * @param flys the flys artifact.
+     *
+     * @return the gauges based on the selected kilometer range (null if
+     *         none/no range set).
+     */
+    public static List<Gauge> getGauges(FLYSArtifact flys) {
+
+        River river = getRiver(flys);
+        if (river == null) {
+            logger.debug("getGauges: no river!");
+            return null;
+        }
+
+        RangeAccess rangeAccess = new RangeAccess(flys, null);
+        double[] dist = rangeAccess.getKmRange();
+        if (dist == null) {
+            logger.debug("getGauges: no dist!");
+            return null;
+        }
+        logger.debug("getGauges: " + dist[0] + " - " + dist[1]);
+
+        return river.determineGauges(dist[0], dist[1]);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/utils/Formatter.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,406 @@
+package org.dive4elements.river.utils;
+
+import java.text.DateFormat;
+import java.text.NumberFormat;
+import java.text.SimpleDateFormat;
+import java.util.Locale;
+
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.river.artifacts.resources.Resources;
+
+
+/** Helper to access static i18n Formatters. */
+public final class Formatter {
+
+    // KMS IN ERROR REPORTS.
+    public static final int CALCULATION_REPORT_KM_MIN_DIGITS = 1;
+    public static final int CALCULATION_REPORT_KM_MAX_DIGITS = 3;
+
+    // WATERLEVEL FORMATTER CONSTANTS
+    public static final int WATERLEVEL_KM_MIN_DIGITS = 3;
+    public static final int WATERLEVEL_KM_MAX_DIGITS = 3;
+    public static final int WATERLEVEL_W_MIN_DIGITS  = 0;
+    public static final int WATERLEVEL_W_MAX_DIGITS  = 2;
+    public static final int WATERLEVEL_Q_MIN_DIGITS  = 0;
+    public static final int WATERLEVEL_Q_MAX_DIGITS  = 2;
+
+
+    // COMPUTED DISCHARGE CURVE FORMATTER CONSTANTS
+    public static final int COMPUTED_DISCHARGE_W_MIN_DIGITS  = 2;
+    public static final int COMPUTED_DISCHARGE_W_MAX_DIGITS  = 2;
+    public static final int COMPUTED_DISCHARGE_Q_MIN_DIGITS  = 0;
+    public static final int COMPUTED_DISCHARGE_Q_MAX_DIGITS  = 2;
+
+
+    // HISTORICAL DISCHARGE CURVE FORMATTER CONSTANTS
+    public static final int HISTORICAL_DISCHARGE_W_MIN_DIGITS = 0;
+    public static final int HISTORICAL_DISCHARGE_W_MAX_DIGITS = 2;
+    public static final int HISTORICAL_DISCHARGE_Q_MIN_DIGITS = 0;
+    public static final int HISTORICAL_DISCHARGE_Q_MAX_DIGITS = 2;
+
+
+    // DURATION CURVE FORMATTER CONSTANTS
+    public static final int DURATION_W_MIN_DIGITS = 0;
+    public static final int DURATION_W_MAX_DIGITS = 2;
+    public static final int DURATION_Q_MIN_DIGITS = 0;
+    public static final int DURATION_Q_MAX_DIGITS = 1;
+    public static final int DURATION_D_MIN_DIGITS = 0;
+    public static final int DURATION_D_MAX_DIGITS = 0;
+
+
+    // FLOW VELOCITY FORMATTER CONSTANTS
+    public static final int FLOW_VELOCITY_KM_MIN_DIGITS     = 3;
+    public static final int FLOW_VELOCITY_KM_MAX_DIGITS     = 3;
+    public static final int FLOW_VELOCITY_VALUES_MIN_DIGITS = 2;
+    public static final int FLOW_VELOCITY_VALUES_MAX_DIGITS = 2;
+    public static final int FLOW_VELOCITY_Q_MIN_DIGITS      = 0;
+    public static final int FLOW_VELOCITY_Q_MAX_DIGITS      = 2;
+
+
+    // MIDDLE BED HEIGHT FORMATTER CONSTANTS
+    public static final int MIDDLE_BED_HEIGHT_KM_MIN_DIGITS             = 3;
+    public static final int MIDDLE_BED_HEIGHT_KM_MAX_DIGITS             = 3;
+    public static final int MIDDLE_BED_HEIGHT_HEIGHT_MIN_DIGITS         = 3;
+    public static final int MIDDLE_BED_HEIGHT_HEIGHT_MAX_DIGITS         = 3;
+    public static final int MIDDLE_BED_HEIGHT_UNCERT_MIN_DIGITS         = 3;
+    public static final int MIDDLE_BED_HEIGHT_UNCERT_MAX_DIGITS         = 3;
+    public static final int MIDDLE_BED_HEIGHT_DATAGAP_MIN_DIGITS        = 2;
+    public static final int MIDDLE_BED_HEIGHT_DATAGAP_MAX_DIGITS        = 2;
+    public static final int MIDDLE_BED_HEIGHT_SOUNDING_WIDTH_MIN_DIGITS = 0;
+    public static final int MIDDLE_BED_HEIGHT_SOUNDING_WIDTH_MAX_DIGITS = 0;
+    public static final int MIDDLE_BED_HEIGHT_WIDTH_MIN_DIGITS          = 3;
+    public static final int MIDDLE_BED_HEIGHT_WIDTH_MAX_DIGITS          = 3;
+
+    public static final int FIX_DELTA_W_KM_MIN_DIGITS = 3;
+    public static final int FIX_DELTA_W_KM_MAX_DIGITS = 3;
+    public static final int FIX_DELTA_W_DELTA_W_MIN_DIGITS = 3;
+    public static final int FIX_DELTA_W_DELTA_W_MAX_DIGITS = 3;
+    public static final int FIX_DELTA_W_DELTA_Q_MIN_DIGITS  = 0;
+    public static final int FIX_DELTA_W_DELTA_Q_MAX_DIGITS  = 2;
+
+
+    /**
+     * Creates a localized NumberFormatter with given range of decimal digits.
+     * @param m CallMeta to find the locale.
+     * @param min minimum number of decimal ("fraction") digits.
+     * @param max maximum number of decimal ("fraction") digits.
+     * @return A NumberFormat. Use #format(NUMBER) to get String representation
+     *         of NUMBER.
+     */
+    public static NumberFormat getFormatter(CallMeta m, int min, int max){
+        Locale       locale = Resources.getLocale(m);
+        NumberFormat nf     = NumberFormat.getInstance(locale);
+
+        nf.setMaximumFractionDigits(max);
+        nf.setMinimumFractionDigits(min);
+
+        return nf;
+    }
+
+
+    public static NumberFormat getFormatter(CallContext c, int min, int max){
+        return getFormatter(c.getMeta(), min, max);
+    }
+
+
+    /**
+     * Returns a number formatter with no max or min digits set.
+     *
+     * @param c The CallContext.
+     *
+     * @return a number formatter.
+     */
+    public static NumberFormat getRawFormatter(CallContext c) {
+        Locale locale = Resources.getLocale(c.getMeta());
+        return NumberFormat.getInstance(locale);
+    }
+
+
+    /**
+     * Returns a date formatter with SHORT style.
+     */
+    public static DateFormat getShortDateFormat(CallContext cc) {
+        Locale locale = Resources.getLocale(cc.getMeta());
+        return DateFormat.getDateInstance(DateFormat.SHORT, locale);
+    }
+
+
+    /**
+     * Returns a date formatter with MEDIUM style.
+     */
+    public static DateFormat getMediumDateFormat(CallContext cc) {
+        Locale locale = Resources.getLocale(cc.getMeta());
+        return DateFormat.getDateInstance(DateFormat.MEDIUM, locale);
+    }
+
+
+    /**
+     * Returns the number formatter for kilometer values in waterlevel exports.
+     *
+     * @return the number formatter for kilometer values.
+     */
+    public static NumberFormat getWaterlevelKM(CallContext context) {
+        return getFormatter(
+                context,
+                WATERLEVEL_KM_MIN_DIGITS,
+                WATERLEVEL_KM_MAX_DIGITS);
+    }
+
+
+    public static NumberFormat getWaterlevelW(CallMeta meta) {
+        return getFormatter(
+                meta,
+                WATERLEVEL_W_MIN_DIGITS,
+                WATERLEVEL_W_MAX_DIGITS);
+    }
+
+
+    /**
+     * Returns the number formatter for W values in waterlevel exports.
+     *
+     * @return the number formatter for W values.
+     */
+    public static NumberFormat getWaterlevelW(CallContext context) {
+        return getFormatter(
+                context,
+                WATERLEVEL_W_MIN_DIGITS,
+                WATERLEVEL_W_MAX_DIGITS);
+    }
+
+
+    /**
+     * Returns the number formatter for Q values in waterlevel exports.
+     *
+     * @return the number formatter for Q values.
+     */
+    public static NumberFormat getWaterlevelQ(CallContext context) {
+        return getFormatter(
+                context,
+                WATERLEVEL_Q_MIN_DIGITS,
+                WATERLEVEL_Q_MAX_DIGITS);
+    }
+
+
+    public static NumberFormat getWaterlevelQ(CallMeta meta) {
+        return getFormatter(
+                meta,
+                WATERLEVEL_Q_MIN_DIGITS,
+                WATERLEVEL_Q_MAX_DIGITS);
+    }
+
+    /**
+     * Returns the number formatter for W values in exports of computed
+     * discharge curves.
+     *
+     * @return the number formatter for W values.
+     */
+    public static NumberFormat getComputedDischargeW(CallContext context) {
+        return getFormatter(
+                context,
+                COMPUTED_DISCHARGE_W_MIN_DIGITS,
+                COMPUTED_DISCHARGE_W_MAX_DIGITS);
+    }
+
+
+    /**
+     * Returns the number formatter for Q values in exports of computed
+     * discharge curves.
+     *
+     * @return the number formatter for Q values.
+     */
+    public static NumberFormat getComputedDischargeQ(CallContext context) {
+        return getFormatter(
+                context,
+                COMPUTED_DISCHARGE_Q_MIN_DIGITS,
+                COMPUTED_DISCHARGE_Q_MAX_DIGITS);
+    }
+
+
+    /**
+     * Returns the number formatter for W values in exports of historical
+     * discharge curves.
+     *
+     * @return the number formatter for W values.
+     */
+    public static NumberFormat getHistoricalDischargeW(CallContext context) {
+        return getFormatter(
+                context,
+                HISTORICAL_DISCHARGE_W_MIN_DIGITS,
+                HISTORICAL_DISCHARGE_W_MAX_DIGITS);
+    }
+
+
+    /**
+     * Returns the number formatter for Q values in exports of historical
+     * discharge curves.
+     *
+     * @return the number formatter for Q values.
+     */
+    public static NumberFormat getHistoricalDischargeQ(CallContext context) {
+        return getFormatter(
+                context,
+                HISTORICAL_DISCHARGE_Q_MIN_DIGITS,
+                HISTORICAL_DISCHARGE_Q_MAX_DIGITS);
+    }
+
+
+    /**
+     * Returns the number formatter for W values in duration curve exports.
+     *
+     * @return the number formatter for W values.
+     */
+    public static NumberFormat getDurationW(CallContext context) {
+        return getFormatter(
+                context,
+                DURATION_W_MIN_DIGITS,
+                DURATION_W_MAX_DIGITS);
+    }
+
+
+    /**
+     * Returns the number formatter for Q values in duration curve exports.
+     *
+     * @return the number formatter for W values.
+     */
+    public static NumberFormat getDurationQ(CallContext context) {
+        return getFormatter(
+                context,
+                DURATION_Q_MIN_DIGITS,
+                DURATION_Q_MAX_DIGITS);
+    }
+
+
+    /**
+     * Returns the number formatter for D values in duration curve exports.
+     *
+     * @return the number formatter for W values.
+     */
+    public static NumberFormat getDurationD(CallContext context) {
+        return getFormatter(
+                context,
+                DURATION_D_MIN_DIGITS,
+                DURATION_D_MAX_DIGITS);
+    }
+
+    public static NumberFormat getCalculationKm(CallMeta meta) {
+        return getFormatter(
+                meta,
+                CALCULATION_REPORT_KM_MIN_DIGITS,
+                CALCULATION_REPORT_KM_MAX_DIGITS);
+    }
+
+
+    public static NumberFormat getFlowVelocityKM(CallContext context) {
+        return getFormatter(
+                context,
+                FLOW_VELOCITY_KM_MIN_DIGITS,
+                FLOW_VELOCITY_KM_MAX_DIGITS);
+    }
+
+
+    public static NumberFormat getFlowVelocityValues(CallContext context) {
+        return getFormatter(
+                context,
+                FLOW_VELOCITY_VALUES_MIN_DIGITS,
+                FLOW_VELOCITY_VALUES_MAX_DIGITS);
+    }
+
+
+    public static NumberFormat getFlowVelocityQ(CallContext context) {
+        return getFormatter(
+                context,
+                FLOW_VELOCITY_Q_MIN_DIGITS,
+                FLOW_VELOCITY_Q_MAX_DIGITS);
+    }
+
+
+    public static NumberFormat getMiddleBedHeightKM(CallContext context) {
+        return getFormatter(
+                context,
+                MIDDLE_BED_HEIGHT_KM_MIN_DIGITS,
+                MIDDLE_BED_HEIGHT_KM_MAX_DIGITS);
+    }
+
+
+    public static NumberFormat getMiddleBedHeightHeight(CallContext context) {
+        return getFormatter(
+                context,
+                MIDDLE_BED_HEIGHT_HEIGHT_MIN_DIGITS,
+                MIDDLE_BED_HEIGHT_HEIGHT_MAX_DIGITS);
+    }
+
+
+    public static NumberFormat getMiddleBedHeightUncert(CallContext context) {
+        return getFormatter(
+                context,
+                MIDDLE_BED_HEIGHT_UNCERT_MIN_DIGITS,
+                MIDDLE_BED_HEIGHT_UNCERT_MAX_DIGITS);
+    }
+
+
+    public static NumberFormat getMiddleBedHeightDataGap(CallContext context) {
+        return getFormatter(
+                context,
+                MIDDLE_BED_HEIGHT_DATAGAP_MIN_DIGITS,
+                MIDDLE_BED_HEIGHT_DATAGAP_MAX_DIGITS);
+    }
+
+
+    public static NumberFormat getMiddleBedHeightSounding(CallContext context) {
+        return getFormatter(
+                context,
+                MIDDLE_BED_HEIGHT_SOUNDING_WIDTH_MIN_DIGITS,
+                MIDDLE_BED_HEIGHT_SOUNDING_WIDTH_MAX_DIGITS);
+    }
+
+
+    public static NumberFormat getMiddleBedHeightWidth(CallContext context) {
+        return getFormatter(
+                context,
+                MIDDLE_BED_HEIGHT_WIDTH_MIN_DIGITS,
+                MIDDLE_BED_HEIGHT_WIDTH_MAX_DIGITS);
+    }
+
+    public static NumberFormat getFixDeltaWKM(CallContext context) {
+        return getFormatter(
+                context,
+                FIX_DELTA_W_KM_MIN_DIGITS,
+                FIX_DELTA_W_KM_MAX_DIGITS);
+    }
+
+    public static NumberFormat getFixDeltaWDeltaW(CallContext context) {
+        return getFormatter(
+                context,
+                FIX_DELTA_W_DELTA_W_MIN_DIGITS,
+                FIX_DELTA_W_DELTA_W_MAX_DIGITS);
+    }
+
+    public static NumberFormat getFixDeltaWQ(CallContext context) {
+        return getFormatter(
+                context,
+                FIX_DELTA_W_DELTA_Q_MIN_DIGITS,
+                FIX_DELTA_W_DELTA_Q_MAX_DIGITS);
+    }
+
+    public static NumberFormat getFixDeltaWW(CallContext context) {
+        return getFormatter(
+                context,
+                FIX_DELTA_W_DELTA_W_MIN_DIGITS,
+                FIX_DELTA_W_DELTA_W_MAX_DIGITS);
+    }
+
+    public static NumberFormat getMeterFormat(CallContext context) {
+        return getFormatter(
+                context,
+                0,
+                2);
+
+    }
+
+    public static DateFormat getDateFormatter(CallMeta m, String pattern) {
+        Locale locale = Resources.getLocale(m);
+        return new SimpleDateFormat(pattern, locale);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/utils/GeometryUtils.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,383 @@
+package org.dive4elements.river.utils;
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.Envelope;
+import com.vividsolutions.jts.geom.Geometry;
+
+import org.dive4elements.river.model.RiverAxis;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.Serializable;
+import java.net.MalformedURLException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+import org.geotools.data.DataStoreFactorySpi;
+import org.geotools.data.DefaultTransaction;
+import org.geotools.data.FeatureWriter;
+import org.geotools.data.Transaction;
+import org.geotools.data.shapefile.ShapefileDataStore;
+import org.geotools.data.shapefile.ShapefileDataStoreFactory;
+import org.geotools.data.simple.SimpleFeatureIterator;
+import org.geotools.feature.FeatureCollection;
+import org.geotools.feature.FeatureIterator;
+import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
+import org.geotools.geojson.feature.FeatureJSON;
+import org.geotools.geometry.jts.JTS;
+import org.geotools.geometry.jts.ReferencedEnvelope;
+import org.geotools.referencing.CRS;
+import org.hibernate.HibernateException;
+import org.opengis.feature.simple.SimpleFeature;
+import org.opengis.feature.simple.SimpleFeatureType;
+import org.opengis.referencing.FactoryException;
+import org.opengis.referencing.NoSuchAuthorityCodeException;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.operation.MathTransform;
+import org.opengis.referencing.operation.TransformException;
+
+public class GeometryUtils {
+
+    private static final Logger logger = Logger.getLogger(GeometryUtils.class);
+
+    public static final String PREFIX_EPSG  = "EPSG:";
+
+    public static final String DEFAULT_EPSG = "EPSG:31467";
+
+    private GeometryUtils() {
+    }
+
+    public static Envelope getRiverBoundary(String rivername) {
+        try {
+            List<RiverAxis> axes = RiverAxis.getRiverAxis(rivername);
+            if (axes != null && axes.size() > 0) {
+                Envelope max = null;
+
+                for (RiverAxis axis: axes) {
+                    // TODO Take the correct EPSG into account. Maybe, we need to
+                    // reproject the geometry.
+                    Envelope env = axis.getGeom().getEnvelopeInternal();
+
+                    if (max == null) {
+                        max = env;
+                    }
+                    else {
+                        max.expandToInclude(env);
+                    }
+                }
+
+                return max;
+            }
+        }
+        catch(HibernateException iae) {
+            logger.warn("No vaild river axis found for " + rivername);
+            return null;
+        }
+        logger.warn("No vaild river axis found for " + rivername);
+
+        return null;
+    }
+
+    public static String getRiverBounds(String rivername) {
+        Envelope env = getRiverBoundary(rivername);
+
+        if (env != null) {
+            return jtsBoundsToOLBounds(env);
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns the boundary of Envelope <i>env</i> in OpenLayers representation.
+     *
+     * @param env The envelope of a geometry.
+     *
+     * @return the OpenLayers boundary of <i>env</i>.
+     */
+    public static String jtsBoundsToOLBounds(Envelope env) {
+        StringBuilder buf = new StringBuilder();
+        buf.append(env.getMinX()); buf.append(' ');
+        buf.append(env.getMinY()); buf.append(' ');
+        buf.append(env.getMaxX()); buf.append(' ');
+        buf.append(env.getMaxY());
+        return buf.toString();
+    }
+
+    public static String createOLBounds(Geometry a, Geometry b) {
+        Coordinate[] ca = a.getCoordinates();
+        Coordinate[] cb = b.getCoordinates();
+
+        double lowerX = Double.MAX_VALUE;
+        double lowerY = Double.MAX_VALUE;
+        double upperX = -Double.MAX_VALUE;
+        double upperY = -Double.MAX_VALUE;
+
+        for (Coordinate c: ca) {
+            lowerX = lowerX < c.x ? lowerX : c.x;
+            lowerY = lowerY < c.y ? lowerY : c.y;
+
+            upperX = upperX > c.x ? upperX : c.x;
+            upperY = upperY > c.y ? upperY : c.y;
+        }
+
+        for (Coordinate c: cb) {
+            lowerX = lowerX < c.x ? lowerX : c.x;
+            lowerY = lowerY < c.y ? lowerY : c.y;
+
+            upperX = upperX > c.x ? upperX : c.x;
+            upperY = upperY > c.y ? upperY : c.y;
+        }
+
+        return "" + lowerX + " " + lowerY + " " + upperX + " " + upperY;
+    }
+
+    public static SimpleFeatureType buildFeatureType(
+        String name, String srs, Class<?> geometryType)
+    {
+        return buildFeatureType(name, srs, geometryType, null);
+    }
+
+    /**
+     * Creates a new SimpleFeatureType using a SimpleFeatureTypeBuilder.
+     *
+     * @param name The name of the FeatureType.
+     * @param srs The SRS (e.g. "EPSG:31466").
+     * @param geometryType The geometry type's class (e.g. Polygon.class).
+     * @param attrs Optional. An object with attribute-name/attribute-class pairs
+     * where index 0 specifies the name as string and index 1 the
+     * ype as class.
+     *
+     * @return a new SimpleFeatureType.
+     */
+    public static SimpleFeatureType buildFeatureType(String name, String srs,
+        Class<?> geometryType, Object[][] attrs) {
+        try {
+            SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder();
+            CoordinateReferenceSystem crs    = CRS.decode(srs);
+
+            builder.setName("flys");
+            builder.setNamespaceURI("http://www.intevation.de/");
+            builder.setCRS(crs);
+            builder.setSRS(srs);
+
+            builder.add("geometry", geometryType, crs);
+
+            if (attrs != null) {
+                for (Object[] attr: attrs) {
+                    builder.add((String) attr[0], (Class<?>) attr[1]);
+                }
+            }
+
+            return builder.buildFeatureType();
+        }
+        catch (NoSuchAuthorityCodeException nsae) {
+        }
+        catch (FactoryException fe) {
+        }
+
+        return null;
+    }
+
+    public static List<SimpleFeature> parseGeoJSON(
+        String geojson, SimpleFeatureType ft
+    ) {
+        List<SimpleFeature> collection = new ArrayList<SimpleFeature>();
+
+        try {
+            FeatureJSON fjson = new FeatureJSON();
+            fjson.setFeatureType(ft);
+
+            FeatureIterator<SimpleFeature> iterator =
+                fjson.streamFeatureCollection(geojson);
+
+            while (iterator.hasNext()) {
+                collection.add(iterator.next());
+            }
+        }
+        catch (IOException ioe) {
+            // TODO handle exception
+        }
+
+        return collection;
+    }
+
+
+    /**
+     * This method returns the {@link CoordinateReferenceSystem} by the
+     * {@link String} <i>epsg</i>.
+     *
+     * @param epsg An EPSG code like <b>EPSG:31466</b>
+     *
+     * @return the {@link CoordinateReferenceSystem} specified by <i>epsg</i>.
+     */
+    public static CoordinateReferenceSystem getCoordinateReferenceSystem(
+        String epsg
+    ) {
+        if (epsg == null) {
+            logger.warn("cannot create CoordinateReferenceSystem with null");
+            return null;
+        }
+
+        if (!epsg.startsWith(PREFIX_EPSG)) {
+            epsg = PREFIX_EPSG + epsg;
+        }
+
+        try {
+            return CRS.decode(epsg);
+        }
+        catch (FactoryException fe) {
+            logger.error(
+                "unable to get CoordinateReferenceSystem for: " + epsg,
+                fe);
+        }
+
+        return null;
+    }
+
+
+    public static Envelope transform(Envelope orig, String targetSrs) {
+        return transform(orig, targetSrs, DEFAULT_EPSG);
+    }
+
+
+    public static Envelope transform(
+        Envelope orig,
+        String   targetSrs,
+        String   origSrs
+    ) {
+        if (targetSrs == null || orig == null || origSrs == null) {
+            logger.warn("unable to transform envelope: empty parameters");
+            return orig;
+        }
+
+        logger.debug("Transform envlope to '" + targetSrs + "'");
+        try {
+            CoordinateReferenceSystem sourceCRS =
+                getCoordinateReferenceSystem(origSrs);
+
+            CoordinateReferenceSystem targetCRS =
+                getCoordinateReferenceSystem(targetSrs);
+
+            if (sourceCRS != null && targetCRS != null) {
+                ReferencedEnvelope tmpEnv =
+                    new ReferencedEnvelope(orig, CRS.decode(origSrs));
+
+                Envelope target = tmpEnv.transform(targetCRS, false);
+
+                if (logger.isDebugEnabled()) {
+                    logger.debug("   orig envelope       : " + orig);
+                    logger.debug("   transformed envelope: " + target);
+                }
+
+                return target;
+            }
+        }
+        catch (NoSuchAuthorityCodeException nsae) {
+            logger.error("Cannot get CoordinateReferenceSystem!", nsae);
+        }
+        catch (FactoryException fe) {
+            logger.error("Cannot get CoordinateReferenceSystem!", fe);
+        }
+        catch (TransformException te) {
+            logger.error("Cannot transform envelope from source "
+                + origSrs + " to target srs " + targetSrs);
+        }
+
+        return null;
+    }
+
+
+    public static boolean writeShapefile(File shape,
+        SimpleFeatureType featureType, FeatureCollection<?, ?> collection) {
+        return writeShapefile(shape, featureType, collection,
+            featureType.getCoordinateReferenceSystem());
+    }
+
+
+    public static boolean writeShapefile(File shape,
+        SimpleFeatureType featureType, FeatureCollection<?, ?> collection,
+        CoordinateReferenceSystem crs) {
+        if (collection.isEmpty()) {
+            logger.warn("Shapefile is not written - no features given!");
+            return false;
+        }
+
+        Transaction   transaction = null;
+
+        try {
+            MathTransform transform = CRS.findMathTransform(
+                CRS.decode(DEFAULT_EPSG), crs);
+
+            Map<String, Serializable> params =
+                new HashMap<String, Serializable>();
+
+            params.put("url", shape.toURI().toURL());
+            params.put("create spatial index", Boolean.TRUE);
+
+            DataStoreFactorySpi dataStoreFactory =
+                new ShapefileDataStoreFactory();
+
+            ShapefileDataStore newDataStore =
+                (ShapefileDataStore)dataStoreFactory.createNewDataStore(params);
+            newDataStore.createSchema(featureType);
+
+            transaction = new DefaultTransaction("create");
+
+            String typeName = newDataStore.getTypeNames()[0];
+
+            FeatureWriter<SimpleFeatureType, SimpleFeature> writer =
+                newDataStore.getFeatureWriter(typeName, transaction);
+
+            SimpleFeatureIterator iterator = (SimpleFeatureIterator) collection.features();
+
+            while (iterator.hasNext()){
+                SimpleFeature feature = iterator.next();
+                SimpleFeature copy    = writer.next();
+
+                copy.setAttributes(feature.getAttributes());
+
+                Geometry orig        = (Geometry) feature.getDefaultGeometry();
+                Geometry reprojected = JTS.transform(orig, transform);
+
+                copy.setDefaultGeometry(reprojected);
+                writer.write();
+            }
+
+            transaction.commit();
+
+            return true;
+        }
+        catch (MalformedURLException mue) {
+            logger.error("Unable to prepare shapefile: " + mue.getMessage());
+        }
+        catch (IOException ioe) {
+            logger.error("Unable to write shapefile: " + ioe.getMessage());
+        }
+        catch (NoSuchAuthorityCodeException nsae) {
+            logger.error("Cannot get CoordinateReferenceSystem for '"
+                + DEFAULT_EPSG + "'");
+        }
+        catch (FactoryException fe) {
+            logger.error("Cannot get CoordinateReferenceSystem for '"
+                + DEFAULT_EPSG + "'");
+        }
+        catch (TransformException te) {
+            logger.error("Was not able to transform geometry!", te);
+        }
+        finally {
+            if (transaction != null) {
+                try {
+                    transaction.close();
+                }
+                catch (IOException ioe) { /* do nothing */ }
+            }
+        }
+
+        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/flys-artifacts/src/main/java/org/dive4elements/river/utils/IdGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,18 @@
+package org.dive4elements.river.utils;
+
+public class IdGenerator {
+
+    protected int id;
+
+    public IdGenerator() {
+    }
+
+    public IdGenerator(int id) {
+        this.id = id;
+    }
+
+    public int next() {
+        return id++;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/utils/KMIndex.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,93 @@
+package org.dive4elements.river.utils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Iterator;
+
+import java.io.Serializable;
+
+/** km to value, searchable. Tolerance is at 10cm. */
+public class KMIndex<A>
+implements   Serializable, Iterable<KMIndex.Entry<A>>
+{
+    public static final double EPSILON = 1e-4;
+
+    public static class Entry<A>
+    implements          Serializable, Comparable<Entry<A>>
+    {
+        protected double km;
+        protected A      value;
+
+        public Entry(double km) {
+            this.km = km;
+        }
+
+        public Entry(double km, A value) {
+            this.km    = km;
+            this.value = value;
+        }
+
+        public double getKm() {
+            return km;
+        }
+
+        public A getValue() {
+            return value;
+        }
+
+        public void setValue(A value) {
+            this.value = value;
+        }
+
+        @Override
+        public int compareTo(Entry<A> other) {
+            double diff = km - other.km;
+            if (diff < -EPSILON) return -1;
+            if (diff > +EPSILON) return +1;
+            return 0;
+        }
+
+        public boolean epsilonEquals(double km) {
+            return Math.abs(this.km - km) < EPSILON;
+        }
+    } // class Entry
+
+
+    protected List<Entry<A>> entries;
+
+    public KMIndex() {
+        this(10);
+    }
+
+    public KMIndex(int capacity) {
+        entries = new ArrayList<Entry<A>>(capacity);
+    }
+
+    public void add(double km, A value) {
+        entries.add(new Entry<A>(km, value));
+    }
+
+    public void sort() {
+        Collections.sort(entries);
+    }
+
+    public Entry<A> search(double km) {
+        for (Entry<A> entry: entries) {
+            if (entry.epsilonEquals(km)) {
+                return entry;
+            }
+        }
+        return null;
+    }
+
+    public Entry<A> binarySearch(double km) {
+        int index = Collections.binarySearch(entries, new Entry<A>(km));
+        return index >= 0 ? entries.get(index) : null;
+    }
+
+    public Iterator<Entry<A>> iterator() {
+        return entries.iterator();
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/utils/MapUtils.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,147 @@
+package org.dive4elements.river.utils;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.log4j.Logger;
+import org.hibernate.impl.SessionFactoryImpl;
+
+import org.dive4elements.river.backend.SessionFactoryProvider;
+
+
+public class MapUtils
+{
+    private static final Logger logger = Logger.getLogger(MapUtils.class);
+
+    public static final Pattern DB_URL_PATTERN =
+        Pattern.compile("(.*)\\/\\/(.*):([0-9]+)\\/([\\.a-zA-Z0-9_-]+)");
+
+    public static final Pattern DB_PSQL_URL_PATTERN =
+        Pattern.compile("(.*)\\/\\/(.*):([0-9]+)\\/([a-zA-Z0-9_-]+)");
+
+    /**
+     * This method returns a connection string for databases used by
+     * Mapserver's Mapfile.
+     *
+     * @return A connection string for Mapserver.
+     */
+    public static String getConnection() {
+        SessionFactoryImpl sf = (SessionFactoryImpl)
+        SessionFactoryProvider.getSessionFactory();
+
+        String user = SessionFactoryProvider.getUser(sf);
+        String pass = SessionFactoryProvider.getPass(sf);
+        String url  = SessionFactoryProvider.getURL(sf);
+
+        logger.debug("Parse connection url: " + url);
+
+        Matcher m = DB_URL_PATTERN.matcher(url);
+        if (!m.matches()) {
+            logger.warn("Could not parse Connection string." +
+                "Try to parse PostgreSQL string.");
+            // maybe this is a PostgreSQL connection...
+            return getPostgreSQLConnection();
+        }
+
+        logger.debug("Groups for connection string: " + m.groupCount());
+        int groups = m.groupCount();
+
+
+        if (logger.isDebugEnabled()) {
+            for (int i = 0; i <= groups; i++) {
+                logger.debug("Group " + i + ": " + m.group(i));
+            }
+        }
+
+        String connection = null;
+
+        if (FLYSUtils.isUsingOracle()) {
+            if (groups < 4) {
+                logger.warn("Could only partially parse connection string.");
+                return null;
+            }
+
+            String host = m.group(2);
+            String port = m.group(3);
+            String backend = m.group(4);
+            connection = user + "/" + pass + "@" + host + ":" + port + "/" + backend;
+        }
+        else {
+            if (groups < 4) {
+                logger.warn("Could only partially parse connection string.");
+                return null;
+            }
+
+            String host = m.group(2);
+            String port = m.group(3);
+            String db   = m.group(4);
+
+            connection = createConnectionString(user, pass, host, db, port);
+        }
+
+        return connection;
+    }
+
+    public static String createConnectionString(
+        String user,
+        String pass,
+        String host,
+        String db,
+        String port
+    ) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("dbname=").append(db);
+        sb.append(" host='").append(host).append("'");
+        sb.append(" user=").append(user);
+        sb.append(" port=").append(port);
+        // XXX: We need to escape this somehow.
+        sb.append(" password='").append(pass).append("'");
+        sb.append(" sslmode=disable");
+        return sb.toString();
+    }
+
+    protected static String getPostgreSQLConnection() {
+        SessionFactoryImpl sf = (SessionFactoryImpl)
+        SessionFactoryProvider.getSessionFactory();
+
+        String user = SessionFactoryProvider.getUser(sf);
+        String pass = SessionFactoryProvider.getPass(sf);
+        String url  = SessionFactoryProvider.getURL(sf);
+
+        Matcher m = DB_PSQL_URL_PATTERN.matcher(url);
+        if (!m.matches()) {
+            logger.warn("Could not parse PostgreSQL Connection string.");
+            return null;
+        }
+
+        int groups = m.groupCount();
+        logger.debug("Groups for PostgreSQL connection string: " + groups);
+
+        if (logger.isDebugEnabled()) {
+            for (int i = 0; i <= groups; i++) {
+                logger.debug("Group " + i + ": " + m.group(i));
+            }
+        }
+
+        String connection = null;
+
+        if (groups < 4) {
+            logger.warn("Could only partially parse connection string.");
+            return null;
+        }
+
+        String host = m.group(2);
+        String port = m.group(3);
+        String db   = m.group(4);
+
+        connection = createConnectionString(user, pass, host, db, port);
+
+        logger.debug("Created connection: '" + connection + "'");
+
+        return connection;
+    }
+
+    public static String getConnectionType() {
+        return FLYSUtils.isUsingOracle() ? "oraclespatial" : "postgis";
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/utils/MapfileGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,384 @@
+package org.dive4elements.river.utils;
+
+import org.dive4elements.artifacts.common.utils.Config;
+import org.dive4elements.river.artifacts.model.LayerInfo;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileWriter;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.runtime.RuntimeConstants;
+
+/**
+ * This class iterates over a bunch of directories, searches for meta
+ * information coresponding to shapefiles and creates a mapfile which is used by
+ * a <i>MapServer</i>.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public abstract class MapfileGenerator
+{
+    public static final String WSPLGEN_RESULT_SHAPE   = "wsplgen.shp";
+    public static final String WSPLGEN_LINES_SHAPE    = "barrier_lines.shp";
+    public static final String WSPLGEN_POLYGONS_SHAPE = "barrier_polygons.shp";
+    public static final String WSPLGEN_USER_SHAPE     = "user-rgd.shp";
+
+    public static final String WSPLGEN_LAYER_TEMPLATE = "wsplgen_layer.vm";
+    public static final String SHP_LAYER_TEMPLATE = "shapefile_layer.vm";
+    public static final String DB_LAYER_TEMPLATE  = "db_layer.vm";
+    public static final String RIVERAXIS_LAYER_TEMPLATE = "riveraxis-layer.vm";
+
+    public static final String MS_WSPLGEN_PREFIX   = "wsplgen-";
+    public static final String MS_BARRIERS_PREFIX  = "barriers-";
+    public static final String MS_LINE_PREFIX      = "lines-";
+    public static final String MS_POLYGONS_PREFIX  = "polygons-";
+    public static final String MS_LAYER_PREFIX     = "ms_layer-";
+    public static final String MS_USERSHAPE_PREFIX = "user-";
+
+    private static Logger logger = Logger.getLogger(MapfileGenerator.class);
+
+    private File shapefileDirectory;
+
+    private VelocityEngine velocityEngine;
+
+
+    protected MapfileGenerator() {
+    }
+
+
+    /**
+     * Method to check the existance of a template file.
+     *
+     * @param templateID The name of a template.
+     * @return true, of the template exists - otherwise false.
+     */
+    public boolean templateExists(String templateID){
+        Template template = getTemplateByName(templateID);
+        return template != null;
+    }
+
+
+    public abstract void generate() throws Exception;
+
+
+    /**
+     * Returns the VelocityEngine used for the template mechanism.
+     *
+     * @return the velocity engine.
+     */
+    protected VelocityEngine getVelocityEngine() {
+        if (velocityEngine == null) {
+            velocityEngine = new VelocityEngine();
+            try {
+                setupVelocity(velocityEngine);
+            }
+            catch (Exception e) {
+                logger.error(e, e);
+                return null;
+            }
+        }
+        return velocityEngine;
+    }
+
+
+    /**
+     * Initialize velocity.
+     *
+     * @param engine Velocity engine.
+     * @throws Exception if an error occured while initializing velocity.
+     */
+    protected void setupVelocity(VelocityEngine engine)
+    throws Exception
+    {
+        engine.setProperty(
+            "input.encoding",
+            "UTF-8");
+
+        engine.setProperty(
+            RuntimeConstants.RUNTIME_LOG,
+            getVelocityLogfile());
+
+        engine.setProperty(
+            "resource.loader",
+            "file");
+
+        engine.setProperty(
+            "file.resource.loader.path",
+            getMapserverTemplatePath());
+
+        engine.init();
+    }
+
+    protected abstract String getVelocityLogfile();
+
+    protected abstract String getMapserverTemplatePath();
+
+    public abstract String getMapserverUrl();
+
+    protected VelocityContext getVelocityContext() {
+        VelocityContext context = new VelocityContext();
+
+        try {
+            context.put("MAPSERVERURL",
+                getMapserverUrl());
+            context.put("SHAPEFILEPATH",
+                getShapefileBaseDir().getCanonicalPath());
+            context.put("CONFIGDIR",
+                Config.getConfigDirectory().getCanonicalPath());
+        }
+        catch (FileNotFoundException fnfe) {
+            // this is bad
+            logger.warn(fnfe, fnfe);
+        }
+        catch (IOException ioe) {
+            // this is also bad
+            logger.warn(ioe, ioe);
+        }
+
+        return context;
+    }
+
+
+    /**
+     * Returns a template specified by <i>model</i>.
+     *
+     * @param model The name of the template.
+     * @return a template.
+     */
+    public Template getTemplateByName(String model) {
+        if (model.indexOf(".vm") < 0) {
+            model = model.concat(".vm");
+        }
+
+        try {
+            VelocityEngine engine = getVelocityEngine();
+            if (engine == null) {
+                logger.error("Error while fetching VelocityEngine.");
+                return null;
+            }
+
+            return engine.getTemplate(model);
+        }
+        catch (Exception e) {
+            logger.warn(e, e);
+        }
+
+        return null;
+    }
+
+
+    /**
+     * Returns the mapfile  template.
+     *
+     * @return the mapfile template.
+     * @throws Exception if an error occured while reading the configuration.
+     */
+    protected Template getMapfileTemplateObj()
+    throws Exception
+    {
+        String mapfileName = getMapfileTemplate();
+        return getTemplateByName(mapfileName);
+    }
+
+    protected abstract String getMapfilePath();
+
+    protected abstract String getMapfileTemplate();
+
+
+    /**
+     * Returns the base directory storing the shapefiles.
+     *
+     * @return the shapefile base directory.
+     *
+     * @throws FileNotFoundException if no shapefile path is found or
+     * configured.
+     */
+    public File getShapefileBaseDir()
+            throws FileNotFoundException, IOException
+    {
+        if (shapefileDirectory == null) {
+            String path = FLYSUtils.getXPathString(
+                FLYSUtils.XPATH_FLOODMAP_SHAPEFILE_DIR);
+
+            if (path != null) {
+                shapefileDirectory = new File(path);
+            }
+
+            if (shapefileDirectory == null) {
+                throw new FileNotFoundException("No shapefile directory given");
+            }
+
+            if (!shapefileDirectory.exists()) {
+                shapefileDirectory.mkdirs();
+            }
+        }
+
+        return shapefileDirectory;
+    }
+
+
+    protected File[] getUserDirs()
+            throws FileNotFoundException, IOException
+    {
+        File   baseDir      = getShapefileBaseDir();
+        File[] artifactDirs = baseDir.listFiles();
+
+        // TODO ONLY RETURN DIRECTORIES OF THE SPECIFIED USER
+
+        return artifactDirs;
+    }
+
+
+
+    protected List<String> parseLayers(File[] dirs) {
+        List<String> layers = new ArrayList<String>();
+
+        for (File dir: dirs) {
+            File[] layerFiles = dir.listFiles(new FilenameFilter() {
+                @Override
+                public boolean accept(File directory, String name) {
+                    return name.startsWith(MS_LAYER_PREFIX);
+                }
+            });
+
+            for (File layer: layerFiles) {
+                try {
+                    layers.add(layer.getCanonicalPath());
+                }
+                catch (IOException ioe) {
+                    logger.warn(ioe, ioe);
+                }
+            }
+        }
+
+        return layers;
+    }
+
+
+
+
+    /**
+     * Creates a layer snippet which might be included in the mapfile.
+     *
+     * @param layerinfo A LayerInfo object that contains all necessary
+     * information to build a Mapserver LAYER section.
+     * @param dir The base dir for the LAYER snippet.
+     * @param filename The name of the file that is written.
+     * @param tpl The Velocity template which is used to create the LAYER
+     * section.
+     */
+    public void writeLayer(
+        LayerInfo layerInfo,
+        File      layerFile,
+        Template  tpl
+    )
+    throws    FileNotFoundException
+    {
+        if (logger.isDebugEnabled()) {
+            logger.debug("Write layer for:");
+            logger.debug("   directory/file: " + layerFile.getName());
+        }
+
+        Writer writer = null;
+
+        try {
+            writer = new FileWriter(layerFile);
+
+            VelocityContext context = getVelocityContext();
+            context.put("LAYER", layerInfo);
+
+            tpl.merge(context, writer);
+        }
+        catch (FileNotFoundException fnfe) {
+            logger.error(fnfe, fnfe);
+        }
+        catch (IOException ioe) {
+            logger.error(ioe, ioe);
+        }
+        catch (Exception e) {
+            logger.error(e, e);
+        }
+        finally {
+            try {
+                if (writer != null) {
+                    writer.close();
+                }
+            }
+            catch (IOException ioe) {
+                logger.debug(ioe, ioe);
+            }
+        }
+    }
+
+
+    /**
+     * Creates a mapfile with the layer information stored in <i>layers</i>.
+     *
+     * @param layers Layer information.
+     */
+    public void writeMapfile(List<String> layers) {
+        String tmpMapName = "mapfile" + new Date().getTime();
+
+        File mapfile = new File(getMapfilePath());
+
+        File   tmp     = null;
+        Writer writer  = null;
+
+        try {
+            tmp = new File(mapfile.getParent(), tmpMapName);
+            tmp.createNewFile();
+
+            writer = new FileWriter(tmp);
+
+            VelocityContext context = getVelocityContext();
+            context.put("LAYERS", layers);
+
+            Template mapTemplate = getMapfileTemplateObj();
+            if (mapTemplate == null) {
+                logger.warn("No mapfile template found.");
+                return;
+            }
+
+            mapTemplate.merge(context, writer);
+
+            // we need to create a temporary mapfile first und rename it into
+            // real mapfile because we don't run into race conditions on this
+            // way. (iw)
+            tmp.renameTo(mapfile);
+        }
+        catch (FileNotFoundException fnfe) {
+            logger.error(fnfe, fnfe);
+        }
+        catch (IOException ioe) {
+            logger.error(ioe, ioe);
+        }
+        catch (Exception e) {
+            logger.error(e, e);
+        }
+        finally {
+            try {
+                if (writer != null) {
+                    writer.close();
+                }
+
+                if (tmp.exists()) {
+                    tmp.delete();
+                }
+            }
+            catch (IOException ioe) {
+                logger.debug(ioe, ioe);
+            }
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/utils/Pair.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2010 by Intevation GmbH
+ *
+ * This program is free software under the LGPL (>=v2.1)
+ * Read the file LGPL.txt coming with the software for details
+ * or visit http://www.gnu.org/licenses/ if it does not exist.
+ */
+
+package org.dive4elements.river.utils;
+
+import java.io.Serializable;
+
+/**
+ * @param <A>
+ * @param <B>
+ * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a>
+ */
+public final class Pair<A, B>
+implements         Serializable
+{
+    private A a;
+    private B b;
+
+    @SuppressWarnings("unused")
+    private Pair() {
+    }
+
+    public Pair(A a, B b) {
+        this.a = a;
+        this.b = b;
+    }
+
+    public A getA() {
+        return a;
+    }
+
+    public B getB() {
+        return b;
+    }
+
+    public void setA(A a) {
+        this.a = a;
+    }
+
+    public void setB(B b) {
+        this.b = b;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/utils/RiverMapfileGenerator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,161 @@
+package org.dive4elements.river.utils;
+
+import com.vividsolutions.jts.geom.Envelope;
+import com.vividsolutions.jts.geom.MultiLineString;
+
+import org.dive4elements.river.artifacts.model.LayerInfo;
+import org.dive4elements.river.artifacts.model.RiverFactory;
+
+import org.dive4elements.river.model.River;
+import org.dive4elements.river.model.RiverAxis;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import java.util.regex.Pattern;
+
+import org.apache.log4j.Logger;
+
+import org.apache.velocity.Template;
+import org.hibernate.HibernateException;
+
+public class RiverMapfileGenerator extends MapfileGenerator {
+
+    public static final String XPATH_RIVERMAP_RIVER_PROJECTION =
+            "/artifact-database/rivermap/river[@name=$name]/srid/@value";
+
+    public static final String XPATH_RIVERMAP_SHAPEFILE_DIR =
+            "/artifact-database/rivermap/shapefile-path/@value";
+
+    public static final String XPATH_RIVERMAP_VELOCITY_LOGFILE =
+            "/artifact-database/rivermap/velocity/logfile/@path";
+
+    public static final String XPATH_RIVERMAP_MAPSERVER_URL =
+            "/artifact-database/rivermap/mapserver/server/@path";
+
+    public static final String XPATH_RIVERMAP_MAPFILE_PATH =
+            "/artifact-database/rivermap/mapserver/mapfile/@path";
+
+    public static final String XPATH_RIVERMAP_MAPFILE_TEMPLATE =
+            "/artifact-database/rivermap/mapserver/map-template/@path";
+
+    public static final String XPATH_RIVERMAP_MAPSERVER_TEMPLATE_PATH =
+            "/artifact-database/rivermap/mapserver/templates/@path";
+
+    public static final Pattern DB_URL_PATTERN =
+        Pattern.compile("(.*)\\/\\/(.*):([0-9]+)\\/([a-zA-Z]+)");
+
+    public static final Pattern DB_PSQL_URL_PATTERN =
+        Pattern.compile("(.*)\\/\\/(.*):([0-9]+)\\/([a-zA-Z0-9]+)");
+
+    private static Logger logger = Logger.getLogger(RiverMapfileGenerator.class);
+
+    /**
+     * Generate river axis mapfile.
+     */
+    @Override
+    public void generate() {
+        logger.debug("generate()");
+
+        List<River>  rivers     = RiverFactory.getRivers();
+        List<String> riverFiles = new ArrayList<String>();
+
+        for (River river : rivers) {
+            // We expect that every river has only one RiverAxis.
+            // This is not correct but currently the case here, see
+            // RiverAxis.java.
+            List<RiverAxis> riverAxis = null;
+            try {
+                riverAxis = RiverAxis.getRiverAxis(river.getName());
+            }
+            catch (HibernateException iae) {
+                logger.error("No valid riveraxis found for " + river.getName());
+                continue;
+            }
+
+            if (riverAxis == null) {
+                logger.warn("River " + river.getName() + " has no river axis!");
+                continue;
+            }
+            if (riverAxis.get(0).getGeom() == null) {
+                logger.warn("River " + river.getName() +
+                    " has no riveraxis geometry!");
+                continue;
+            }
+            MultiLineString geom = riverAxis.get(0).getGeom();
+            Envelope extent = geom.getEnvelopeInternal();
+
+            createRiverAxisLayer(
+                    river.getName(),
+                    river.getId(),
+                    Integer.toString(geom.getSRID()),
+                    extent.getMinX() + " " +
+                    extent.getMinY() + " " +
+                    extent.getMaxX() + " " +
+                    extent.getMaxY());
+
+            riverFiles.add("river-" + river.getName() + ".map");
+        }
+        writeMapfile(riverFiles);
+    }
+
+    protected void createRiverAxisLayer(String riverName, int riverID, String srid, String extend) {
+        LayerInfo layerInfo = new LayerInfo();
+        layerInfo.setName(riverName);
+        layerInfo.setConnection(MapUtils.getConnection());
+        layerInfo.setConnectionType(MapUtils.getConnectionType());
+        layerInfo.setSrid(srid);
+        layerInfo.setExtent(extend);
+        layerInfo.setType("line");
+        // FIXME: Use templates for that
+        if (FLYSUtils.isUsingOracle()) {
+            layerInfo.setData("geom FROM river_axes USING SRID " + srid);
+        } else {
+            layerInfo.setData("geom FROM river_axes");
+        }
+        layerInfo.setFilter("river_id = " + riverID + " and kind_id = 1");
+        layerInfo.setTitle(riverName + " RiverAxis");
+
+        File layerFile = new File("river-" + riverName + ".map");
+        Template template = getTemplateByName("riveraxis-layer.vm");
+        if (template == null) {
+            logger.warn("Template riveraxis-layer.vm not found.");
+            return;
+        }
+
+        try {
+            writeLayer(layerInfo, layerFile, template);
+        }
+        catch (FileNotFoundException e) {
+            logger.warn(e.getLocalizedMessage(), e);
+        }
+    }
+
+    @Override
+    protected String getVelocityLogfile() {
+        return FLYSUtils.getXPathString(XPATH_RIVERMAP_VELOCITY_LOGFILE);
+    }
+
+    @Override
+    protected String getMapserverTemplatePath() {
+        return FLYSUtils.getXPathString(XPATH_RIVERMAP_MAPSERVER_TEMPLATE_PATH);
+    }
+
+    @Override
+    public String getMapserverUrl() {
+        return FLYSUtils.getXPathString(XPATH_RIVERMAP_MAPSERVER_URL);
+    }
+
+    @Override
+    protected String getMapfilePath() {
+        return FLYSUtils.getXPathString(XPATH_RIVERMAP_MAPFILE_PATH);
+    }
+
+    @Override
+    protected String getMapfileTemplate() {
+        return FLYSUtils.getXPathString(XPATH_RIVERMAP_MAPFILE_TEMPLATE);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/utils/ThemeUtil.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,798 @@
+package org.dive4elements.river.utils;
+
+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 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);
+
+    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)
+    {
+        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);
+/*
+        String strNumClasses = XMLUtils.xpathString(theme, XPATH_WSPLGEN_NUMCLASSES, null);
+        int numClasses;
+        if (strNumClasses != null) {
+            numClasses = Integer.parseInt(strNumClasses);
+        }
+        else {
+            numClasses = 5;
+            logger.warn("createDynamicMapserverStyle(): strNumClasses is null");
+        }
+
+        if (numClasses < 5) {
+            numClasses = 5;
+        }
+        else if (numClasses > 20) {
+            numClasses = 20;
+        }*/
+
+
+        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;
+
+        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 = createWSPLGENExpression(from + n * step, step, n + 1, numClasses);
+
+            Clazz c = new Clazz(expr);
+            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 createWSPLGENExpression(float val, float step, int idx, int maxIdx) {
+        if (idx < maxIdx) {
+            return "[DIFF] >= " + val + " AND  [DIFF] < " + (val + step);
+        }
+        else {
+            return "[DIFF] >= " + 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/flys-artifacts/src/main/java/org/dive4elements/river/wsplgen/FacetCreator.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,204 @@
+package org.dive4elements.river.wsplgen;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import com.vividsolutions.jts.geom.Envelope;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.access.RangeAccess;
+import org.dive4elements.river.artifacts.FLYSArtifact;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+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.DefaultState.ComputeType;
+import org.dive4elements.river.model.CrossSectionTrack;
+import org.dive4elements.river.utils.FLYSUtils;
+import org.dive4elements.river.utils.GeometryUtils;
+import org.dive4elements.river.utils.MapfileGenerator;
+
+
+public class FacetCreator implements FacetTypes {
+
+    public static final String I18N_WSPLGEN_RESULT    = "floodmap.uesk";
+    public static final String I18N_WSPLGEN_DEFAULT   = "floodmap.uesk";
+    public static final String I18N_BARRIERS          = "floodmap.barriers";
+    public static final String I18N_BARRIERS_DEFAULT  = "floodmap.barriers";
+    public static final String I18N_USERSHAPE         = "floodmap.usershape";
+    public static final String I18N_USERSHAPE_DEFAULT = "floodmap.usershape";
+
+    protected FLYSArtifact artifact;
+
+    protected CallContext  cc;
+
+    protected List<Facet> facets;
+    protected List<Facet> tmpFacets;
+
+    protected String url;
+    protected String hash;
+    protected String stateId;
+
+
+    private static Logger logger = Logger.getLogger(FacetCreator.class);
+
+
+    public FacetCreator(
+        FLYSArtifact artifact,
+        CallContext  cc,
+        String       hash,
+        String       sId,
+        List<Facet>  facets
+    ) {
+        this.tmpFacets  = new ArrayList<Facet>(2);
+        this.facets     = facets;
+        this.artifact   = artifact;
+        this.cc         = cc;
+        this.hash       = hash;
+        this.stateId    = sId;
+    }
+
+    // TODO We have FLYSUtils and will have RiverAccess to do this
+    protected String getRiver() {
+        return artifact.getDataAsString("river");
+    }
+
+    protected String getUrl() {
+        return FLYSUtils.getUserWMSUrl(artifact.identifier());
+    }
+
+    protected String getSrid() {
+        return FLYSUtils.getRiverSrid(artifact);
+    }
+
+    protected Envelope getWSPLGENBounds() {
+        String river = getRiver();
+        RangeAccess rangeAccess = new RangeAccess(artifact, null);
+        double kms[] = rangeAccess.getKmRange();
+
+        logger.debug("### getWSPLGENBounds");
+        logger.debug("###    from km: " + kms[0]);
+        logger.debug("###    to   km: " + kms[1]);
+
+        CrossSectionTrack a =
+            CrossSectionTrack.getCrossSectionTrack(river, kms[0]);
+
+        CrossSectionTrack b =
+            CrossSectionTrack.getCrossSectionTrack(river, kms[1]);
+
+        if (a == null || b == null) {
+            return null;
+        }
+
+        Envelope envA = a.getGeom().getEnvelopeInternal();
+        Envelope envB = b.getGeom().getEnvelopeInternal();
+
+        envA.expandToInclude(envB);
+        envA = GeometryUtils.transform(envA, getSrid());
+
+        logger.debug("###    => " + envA);
+
+        return envA;
+    }
+
+    protected Envelope getBounds() {
+        return GeometryUtils.getRiverBoundary(getRiver());
+    }
+
+    public List<Facet> getFacets() {
+        return tmpFacets;
+    }
+
+    public void createWSPLGENFacet() {
+        String river = getRiver();
+        RangeAccess rangeAccess = new RangeAccess(artifact, null);
+        double kms[] = rangeAccess.getKmRange();
+
+        WSPLGENLayerFacet wsplgen = new WSPLGENLayerFacet(
+            0,
+            FLOODMAP_WSPLGEN,
+            Resources.format(
+                cc.getMeta(),
+                I18N_WSPLGEN_RESULT,
+                I18N_WSPLGEN_DEFAULT,
+                river,
+                kms[0], kms[1]),
+            ComputeType.ADVANCE,
+            stateId,
+            hash,
+            getUrl());
+
+        Envelope bounds = getWSPLGENBounds();
+
+        if (bounds == null) {
+            bounds = getBounds();
+        }
+
+        wsplgen.addLayer(
+            MapfileGenerator.MS_WSPLGEN_PREFIX + artifact.identifier());
+        wsplgen.setSrid(getSrid());
+        wsplgen.setOriginalExtent(bounds);
+        wsplgen.setExtent(bounds);
+
+        tmpFacets.add(wsplgen);
+    }
+
+    public void createBarrierFacet() {
+        WMSLayerFacet barriers = new WMSLayerFacet(
+            1,
+            FLOODMAP_BARRIERS,
+            Resources.getMsg(
+                cc.getMeta(),
+                I18N_BARRIERS,
+                I18N_BARRIERS_DEFAULT),
+            ComputeType.ADVANCE,
+            stateId,
+            hash,
+            getUrl());
+
+        barriers.addLayer(MapfileGenerator.MS_LAYER_PREFIX +
+            MapfileGenerator.MS_BARRIERS_PREFIX + "lines" + artifact.identifier());
+        barriers.addLayer( MapfileGenerator.MS_LAYER_PREFIX +
+            MapfileGenerator.MS_BARRIERS_PREFIX + "poly" + artifact.identifier());
+
+        barriers.setSrid(getSrid());
+        barriers.setExtent(getBounds());
+
+        tmpFacets.add(barriers);
+    }
+
+
+    public void createShapeFacet(
+        String desc,
+        String layer,
+        String type,
+        int ndx) {
+        WMSLayerFacet shape = new WMSLayerFacet(
+            1,
+            type,
+            Resources.getMsg(
+                cc.getMeta(),
+                desc,
+                I18N_USERSHAPE_DEFAULT),
+            ComputeType.ADVANCE,
+            stateId,
+            hash,
+            getUrl());
+
+        shape.addLayer(
+            layer + artifact.identifier());
+        shape.setSrid(getSrid());
+        shape.setExtent(getBounds());
+
+        tmpFacets.add(shape);
+    }
+
+
+    public void finish() {
+        facets.addAll(getFacets());
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/wsplgen/JobObserver.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,106 @@
+package org.dive4elements.river.wsplgen;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.river.artifacts.model.CalculationMessage;
+import org.dive4elements.river.artifacts.model.map.WSPLGENJob;
+
+
+public class JobObserver extends Thread {
+
+    private static Logger logger = Logger.getLogger(JobObserver.class);
+
+
+    public static final String WSPLGEN_ENCODING =
+        "ISO-8859-1";
+
+    public static final String WSPLGEN_LOG_OUTPUT =
+        System.getProperty("wsplgen.log.output", "false");
+
+    public static final String[] STEPS = {
+        ".*<-Auswertung der Kommandozeilen-Parameter beendet.*",
+        ".*->Laden des DGM in Datei '.*' gestartet.*",
+        ".*->Triangulierung der Knoten gestartet.*",
+        ".*->Anpassung der Elemente an Dämme und Gräben gestartet.*",
+        ".*<-WSPLGEN Version .* beendet.*"
+    };
+
+
+    protected WSPLGENJob job;
+
+    protected InputStream in;
+
+    protected Pattern[] patterns;
+
+    protected int len;
+
+    protected boolean copy;
+
+
+    public JobObserver(WSPLGENJob job) {
+        this.job  = job;
+        this.len  = 0;
+        this.copy = Boolean.parseBoolean(WSPLGEN_LOG_OUTPUT);
+
+        patterns = new Pattern[STEPS.length];
+    }
+
+
+    protected void prepareRegexes() {
+        for (int num = STEPS.length, i = 0; i < num; i++) {
+            patterns[i] = Pattern.compile(STEPS[i], Pattern.DOTALL);
+        }
+    }
+
+
+    public void setInputStream(InputStream in) {
+        this.in = in;
+    }
+
+
+    public void run() {
+        logger.debug("Start observation...");
+        prepareRegexes();
+
+        try {
+            BufferedReader reader =
+                new BufferedReader(
+                    new InputStreamReader(in, WSPLGEN_ENCODING));
+
+            String line = null;
+
+            while ((line = reader.readLine()) != null) {
+                if (copy) {
+                    logger.debug(line);
+                }
+
+                update(line);
+            }
+        }
+        catch (IOException ioe) {
+            logger.warn("Observation canceled: " + ioe.getMessage());
+        }
+    }
+
+
+    protected void update(String log) {
+        for (int num = patterns.length, i = 0; i < num; i++) {
+            Matcher m = patterns[i].matcher(log);
+
+            if (m.matches()) {
+                job.getCallContext().addBackgroundMessage(
+                    new CalculationMessage(num, i+1, log));
+
+                logger.info("Finished step " + (i+1) + " / " + num);
+            }
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=5 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/wsplgen/ProblemObserver.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,115 @@
+package org.dive4elements.river.wsplgen;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.river.artifacts.model.map.WSPLGENCalculation;
+import org.dive4elements.river.artifacts.model.map.WSPLGENJob;
+
+
+public class ProblemObserver extends JobObserver {
+
+    private static Logger logger = Logger.getLogger(ProblemObserver.class);
+
+
+    public static final Pattern WSPLGEN_ERROR_START = Pattern.compile(
+        ".*->(.*Fehler)(\\s*\\((\\d+)\\).*)*",
+        Pattern.DOTALL);
+
+    public static final Pattern WSPLGEN_ERROR_END = Pattern.compile(
+        ".*<-(.*Fehler).*",
+        Pattern.DOTALL);
+
+    public static final Pattern WSPLGEN_WARNING_START = Pattern.compile(
+        ".*->Warnung\\s*\\((\\d+)\\).*",
+        Pattern.DOTALL);
+
+    public static final Pattern WSPLGEN_WARNING_END = Pattern.compile(
+        ".*<-Warnung .*",
+        Pattern.DOTALL);
+
+
+    protected int error;
+    protected int warning;
+
+    protected WSPLGENCalculation calculation;
+
+
+    public ProblemObserver(WSPLGENJob job) {
+        super(job);
+        error       = -1;
+        warning     = -1;
+        calculation = job.getCalculation();
+    }
+
+
+    public void run() {
+        logger.debug("Start observation...");
+
+        super.run();
+    }
+
+
+    @Override
+    protected void prepareRegexes() {
+        // do nothing
+    }
+
+
+    @Override
+    protected void update(String log) {
+        Matcher startError = WSPLGEN_ERROR_START.matcher(log);
+        if (startError.matches()) {
+            if (startError.groupCount() >= 2) {
+                String tmp = startError.group(3);
+
+                if (tmp != null && tmp.length() > 0) {
+                    error = Integer.parseInt(tmp);
+                }
+                else error = 1;
+            }
+            else {
+                error = 1;
+            }
+
+            return;
+        }
+
+        Matcher endError = WSPLGEN_ERROR_END.matcher(log);
+        if (endError.matches()) {
+            error = -1;
+        }
+
+        if (error > 0) {
+            calculation.addError(new Integer(error), log);
+        }
+
+        Matcher startWarning = WSPLGEN_WARNING_START.matcher(log);
+        if (startWarning.matches()) {
+            warning = Integer.parseInt(startWarning.group(1));
+            return;
+        }
+
+        Matcher endWarning = WSPLGEN_WARNING_END.matcher(log);
+        if (endWarning.matches()) {
+            warning = -1;
+        }
+
+        if (warning > 0) {
+            calculation.addWarning(new Integer(warning), log);
+        }
+    }
+
+
+    public int numErrors() {
+        return calculation.numErrors();
+    }
+
+
+    public int numWarnings() {
+        return calculation.numWarnings();
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=5 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/wsplgen/Scheduler.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,111 @@
+package org.dive4elements.river.wsplgen;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.model.map.WSPLGENJob;
+
+
+/**
+ * The Scheduler is used to retrieve new WSPLGENJob. The incoming jobs are added
+ * to a ScheduledThreadPoolExecutor. This thread pool has a number of worker
+ * threads that processes the WSPLGENJobs. The number of worker threads can be
+ * set using a System property <i>wsplgen.max.threads</i> ; its default value is
+ * 1.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class Scheduler {
+
+    private class FutureJob {
+        public Future     future;
+        public WSPLGENJob job;
+
+        public FutureJob(Future future, WSPLGENJob job) {
+            this.future = future;
+            this.job    = job;
+        }
+    }
+
+    public static final int MAX_WSPLGEN_PROCESSES =
+        Integer.getInteger("wsplgen.max.threads", 1);
+
+
+    protected ScheduledThreadPoolExecutor pool;
+    protected Map<String, FutureJob> jobs;
+
+
+    private static Scheduler INSTANCE;
+
+    private static final Logger logger = Logger.getLogger(Scheduler.class);
+
+
+
+    private Scheduler() {
+        jobs = new HashMap<String, FutureJob>();
+        pool = new ScheduledThreadPoolExecutor(MAX_WSPLGEN_PROCESSES);
+    }
+
+
+    public static Scheduler getInstance() {
+        if (INSTANCE == null) {
+            logger.info("Create new WSPLGEN Scheduler...");
+
+            INSTANCE = new Scheduler();
+        }
+
+        return INSTANCE;
+    }
+
+
+    public void addJob(final WSPLGENJob job) {
+        synchronized (jobs) {
+            WSPLGENFuture f = new WSPLGENFuture(new WSPLGENCallable(this, job));
+            pool.execute(f);
+
+            jobs.put(job.getArtifact().identifier(), new FutureJob(f, job));
+
+            logger.info("New WSPLGEN job successfully added.");
+        }
+    }
+
+
+    /**
+     * Cancels a running (or queued) job.
+     *
+     * @param jobId The id of the job (which is the identifier of an Artifact).
+     */
+    public void cancelJob(String jobId) {
+        logger.debug("Search job in queue: " + jobId);
+
+        synchronized (jobs) {
+            FutureJob fj = jobs.get(jobId);
+
+            if (fj != null) {
+                logger.info("Try to cancel job: " + jobId);
+
+                fj.future.cancel(true);
+
+                removeJob(jobId);
+
+                fj.job.getCallContext().afterBackground(
+                    CallContext.STORE);
+
+                logger.info("Canceled job: " + jobId);
+            }
+        }
+    }
+
+
+    protected void removeJob(String id) {
+        synchronized (jobs) {
+            jobs.remove(id);
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/wsplgen/SchedulerSetup.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,37 @@
+package org.dive4elements.river.wsplgen;
+
+import org.w3c.dom.Document;
+
+import org.dive4elements.artifacts.GlobalContext;
+
+import org.dive4elements.artifactdatabase.LifetimeListener;
+
+import org.dive4elements.river.artifacts.context.FLYSContext;
+
+
+/**
+ * A LifetimeListener that is used to create an instance of Scheduler. This
+ * instance is put into the GlobalContext using FLYSContext.SCHEDULER.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class SchedulerSetup implements LifetimeListener {
+
+    @Override
+    public void setup(Document document) {
+    }
+
+
+    @Override
+    public void systemUp(GlobalContext globalContext) {
+        Scheduler scheduler = Scheduler.getInstance();
+        globalContext.put(FLYSContext.SCHEDULER, scheduler);
+    }
+
+
+    @Override
+    public void systemDown(GlobalContext globalContext) {
+        // TODO IMPLEMENT ME!
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/wsplgen/WSPLGENCallable.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,137 @@
+package org.dive4elements.river.wsplgen;
+
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.model.map.WSPLGENJob;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.concurrent.Callable;
+
+import org.apache.log4j.Logger;
+
+
+/**
+ * A Callable that is used to start and observe an external Process for WSPLGEN.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class WSPLGENCallable implements Callable {
+
+    public static final String WSPLGEN_PARAMETER_FILE =
+        "wsplgen.par";
+
+    public static final String WSPLGEN_BIN_PATH =
+        System.getProperty("wsplgen.bin.path");
+
+
+    private Logger logger = Logger.getLogger(WSPLGENCallable.class);
+
+    private Process process;
+
+    protected Scheduler scheduler;
+
+    protected WSPLGENJob job;
+
+    protected JobObserver     logObserver;
+    protected ProblemObserver errorObserver;
+
+
+    public WSPLGENCallable(Scheduler scheduler, WSPLGENJob job) {
+        this.scheduler     = scheduler;
+        this.job           = job;
+        this.logObserver   = new JobObserver(job);
+        this.errorObserver = new ProblemObserver(job);
+    }
+
+
+    @Override
+    public WSPLGENJob call() {
+        File dir       = job.getWorkingDir();
+        File parameter = new File(dir, WSPLGEN_PARAMETER_FILE);
+
+        String[] args = new String[] {
+            WSPLGEN_BIN_PATH,
+            "-PAR=\"" + parameter.getAbsolutePath() + "\""
+        };
+
+        execute(args, dir);
+
+        return job;
+    }
+
+
+    protected void execute(String[] args, File dir) {
+        logger.info("Start JobExecutor for artifact: " + dir.getName());
+
+        try {
+            synchronized (this) {
+                process = Runtime.getRuntime().exec(args, null, dir);
+
+                logObserver.setInputStream(process.getInputStream());
+                errorObserver.setInputStream(process.getErrorStream());
+
+                logObserver.start();
+                errorObserver.start();
+
+                try {
+                    process.waitFor();
+                }
+                catch (InterruptedException ie) {
+                    logger.warn("WSPLGEN job interrupted: " + ie.getMessage());
+                }
+
+                try {
+                    logObserver.join();
+                    errorObserver.join();
+                }
+                catch (InterruptedException iee) { /* do nothing */ }
+
+                logger.info("WSPLGEN exit value: " + process.exitValue());
+                logger.info(
+                    "WSPLGEN throw " +
+                    errorObserver.numErrors() + " errors.");
+                logger.info(
+                    "WSPLGEN throw " +
+                    errorObserver.numWarnings() + " warnings.");
+
+                if (process.exitValue() < 2 && errorObserver.numErrors() == 0) {
+                    FacetCreator fc = job.getFacetCreator();
+                    fc.createWSPLGENFacet();
+                    fc.finish();
+                }
+
+                job.getCallContext().afterBackground(CallContext.STORE);
+
+                scheduler.removeJob(getJob().getArtifact().identifier());
+
+                return;
+            }
+        }
+        catch (SecurityException se) {
+            logger.error(se);
+        }
+        catch (IOException ioe) {
+            logger.error(ioe);
+        }
+        catch (NullPointerException npe) {
+            logger.error(npe, npe);
+        }
+        catch (IndexOutOfBoundsException ioobe) {
+            logger.error(ioobe, ioobe);
+        }
+    }
+
+
+    public void cancelWSPLGEN() {
+        if (process != null) {
+            logger.debug("Cancel running WSPLGEN process.");
+            process.destroy();
+        }
+    }
+
+
+    public WSPLGENJob getJob() {
+        return job;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/wsplgen/WSPLGENFuture.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,41 @@
+package org.dive4elements.river.wsplgen;
+
+import java.util.concurrent.FutureTask;
+
+import org.apache.log4j.Logger;
+
+
+/**
+ * This FutureTask overrides the <i>cancel()</i> method. Before super.cancel()
+ * is called, WSPLGENCallable.cancelWSPLGEN() is executed to kill a running
+ * WSPLGEN process.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class WSPLGENFuture extends FutureTask {
+
+    private static final Logger logger = Logger.getLogger(WSPLGENFuture.class);
+
+    protected WSPLGENCallable wsplgenCallable;
+
+
+    public WSPLGENFuture(WSPLGENCallable callable) {
+        super(callable);
+        this.wsplgenCallable = callable;
+    }
+
+
+    public WSPLGENCallable getWSPLGENCallable() {
+        return wsplgenCallable;
+    }
+
+
+    @Override
+    public boolean cancel(boolean mayInterruptIfRunning) {
+        logger.debug("WSPLGENFuture.cancel");
+
+        wsplgenCallable.cancelWSPLGEN();
+        return super.cancel(mayInterruptIfRunning);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :

http://dive4elements.wald.intevation.org